alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 *                 Sun Public License Notice
 *
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 *
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
 * Microsystems, Inc. All Rights Reserved.
 */

package org.netbeans.modules.vcscore.cache;

import org.netbeans.modules.vcscore.util.VcsUtilities;

import java.io.*;
import java.lang.ref.Reference;
//import java.lang.ref.WeakReference;
import java.util.*;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

/** this class displays a directory in the cache and ensures the consistency of the cache. 
 *  You can add/remove directories/files, write to disk cache etc. Also keeps track if all children are stil in the cache.
 *  Your subclasses should mainly just override the abstract methods.
 *
 * @author  mkleint
 */

public abstract class CacheDir extends CacheFile {
    
    private int strategy;
    /** absolute pathname of the directory.
     */
//    protected String dirName;
    /** File instance of this directory.
     */
    protected File dirFile;
    private boolean modified = false;

    /** hashmap of subdirectories - are kept separately from files
     * Comtains pairs of directory name and cache dir or a reference to cache dir.
     * It's important to keep just references to non-empty directories so that
     * they can be garbage-collected when not used.
     */
    private Map childDirs = null;

    /** hashmap of files  - are kept separately from  subdirectories */
    private Map childFiles;
    /** flag that is switched off when a file/subdir is removed by the CacheQueue
     */
    //private boolean complete;
//    protected int childCount;
    //    private CacheDir parent = null;
    protected FileSystemCache cacheObject;
    
    // Ignore list for support of ignore files
    private java.util.List ignoreList;
    private boolean ignoreListWasSet = false;
    
    // buffered regular expression, for efficiency reasons
    private Pattern regExp;
    
    private Object ignoreLock = new Object();
    
    /**
     * Object used for synchronization of access to child files
     */
    protected final Object CHILD_FILES_LOCK;
    
    /** Creates new CacheDir */
    public CacheDir(String cacheName, File dirFile, CacheFile.PersistentData data) {
        super(cacheName, data);
        setAppliedLevel(CacheHandler.STRAT_NONE);
        childFiles = new HashMap(20);
        childDirs = new HashMap(10);
        CHILD_FILES_LOCK = childFiles;
        modified = false;
//        childCount = 0;
        this.dirFile = dirFile;
//        dirName = dirFile.getAbsolutePath();
        this.setName(dirFile.getName());
        //complete = true;
        cacheObject = CacheHandler.getInstance().getCache(getCacheName());
    }
    
    /**
     * Add child names, that are written in the disk storage.
     * This is necessary if the created directory is written in the disk cache
     * and later is asked for it's children.
     */
    protected final void addChildNames(String[] fileNames, String[] dirNames) {
        synchronized (CHILD_FILES_LOCK) {
            for (int i = 0; i < fileNames.length; i++) {
                if (!childFiles.containsKey(fileNames[i])) {
                    childFiles.put(fileNames[i], createReference(null));
                }
            }
            for (int i = 0; i < dirNames.length; i++) {
                if (!childDirs.containsKey(dirNames[i])) {
                    childDirs.put(dirNames[i], createReference(null));
                }
            }
        }
    }
    
    // ------ dir stuff -----------------------------------------------------------
    
    /** return all subdirectories in an array
     */
    public CacheDir[] getSubDirs() {

        // TODO keep directoriesConsistencyInvariant
        // assert directoriesConsistencyInvariant();

        LinkedList col = new LinkedList();
        CacheHandler handler = CacheHandler.getInstance();
        synchronized (CHILD_FILES_LOCK) {
            for (Iterator it = new ArrayList(childDirs.keySet()).iterator(); it.hasNext(); ) {
                String name = (String) it.next();
                Reference rDir = (Reference) childDirs.get(name);
                CacheDir cDir = (CacheDir) rDir.get();
                if (cDir == null) {
                    CacheFile.PersistentData data = handler.getReferencedData(rDir);
                    if (data != null) {
                        cDir = (CacheDir) createChildFromData(data);
                    } else {
                        cDir = readDirFromDisk(name);
                    }
                    cDir.setParent(this);
                    childDirs.put(name, createReference(cDir));
                }
                col.add(cDir);
            }
        }
        return (CacheDir[]) col.toArray(new CacheDir[col.size()]);
    }
    
    public String[] getSubDirNames() {
        synchronized (CHILD_FILES_LOCK) {
            return (String[]) childDirs.keySet().toArray(new String[0]);
        }
    }

    /** Get a single subdir by name (just name not absolute path).
     * If it's not there, returns null,
     * if the directory was released from memory, it's retrieved from the disk cache.
     */
    public CacheDir getSubDir(String name) {
        synchronized (CHILD_FILES_LOCK) {
            Reference rDir = (Reference) childDirs.get(name);
            if (rDir == null) return null;
            CacheDir cDir = (CacheDir) rDir.get();
            if (cDir == null) {
                CacheFile.PersistentData data = CacheHandler.getInstance().getReferencedData(rDir);
                if (data != null) {
                    cDir = (CacheDir) createChildFromData(data);
                } else {
                    cDir = readDirFromDisk(name);
                }
                cDir.setParent(this);
                childDirs.put(name, createReference(cDir));
                getCacheObject().registerDir(cDir);
            }
            return cDir;
        }
    }
    
    /**
     * Get a cache dir if exists in memory. Does not load it from the disk cache.
     */
    protected CacheDir getSubDirIfExists(String name) {
        synchronized (CHILD_FILES_LOCK) {
            Reference rDir = (Reference) childDirs.get(name);
            if (rDir == null) return null;
            return (CacheDir) rDir.get();
        }
    }
    
/*    //--
    public String[] getDirContentNames() {
        D.deb("getDirContentName() -- beginning of " + this.getAbsolutePath());
        Collection colDr = childDirs.values();
        Collection colFl = cachedFiles.values();
        int dirSize = colDr.size() + colFl.size();
        int size1 = colDr.size();
        String[] content = new String[dirSize];
        Iterator it = colDr.iterator();
        int index = 0;
        while (index < size1) {
            CacheDir subDir = (CacheDir)it.next();
            content[index] = subDir.getName();
            index = index + 1;
        }
        it = colFl.iterator();
        for (int index2 = 0; index2 < colFl.size(); index2++) {
            content[index2 + size1] = ((CacheFile)it.next()).getName();
        }
        return content;
    }
 */   
    
    /** Adds a subdirectory to the current one.
     * @param fireEvent - if set to true, fires cacheAdded event + also causes the parent directory to get modified
     */
    public CacheDir addChildDir(CacheDir subDir, boolean fireEvent) {
        CacheDir dir = getCacheObject().getDir(subDir.getAbsolutePath());
        if (dir != null && dir.getCacheName().equals(getCacheName())) {
          /* directory is already in the cache - could be done by other fs mounted at different point
           *  or when reloading the directory (we don't want to destroy subdir structure, just find new nodes
           */
            subDir = dir;
        } else { // is new dir.. do register it..
            getCacheObject().registerDir(subDir);
        }
        synchronized (CHILD_FILES_LOCK) {
            // do change this dir to modified
            this.setModifiedContent(true);
            childDirs.put(subDir.getName(), createReference(subDir));
            subDir.setParent(this);
        }
        if (fireEvent) {
            getCacheObject().fireCacheHandlerEvent(FileSystemCache.EVENT_ADD, subDir);
        }
        return subDir;
    }
    
    /** Removes a directory from the current one.
     * @param fireEvent - if set to true, fires cacheRemoved event;
     * also causes the parent directory to get modified
     */
    public void removeChildDir(String subdirName, boolean fireEvent) {
        CacheDir subDir = this.getSubDir(subdirName);
        if (subDir != null) {
            synchronized (CHILD_FILES_LOCK) {
                this.setModifiedContent(true);
                childDirs.remove(subDir.getName());
            }
            getCacheObject().unregisterDir(subDir);
            if (fireEvent) {
                getCacheObject().fireCacheHandlerEvent(FileSystemCache.EVENT_REMOVE, subDir);
            }
            subDir.setParent(null);
            // Set as not modified to disable write of a deleted cache dir into the disk cache.
            subDir.setModifiedContent(false);
            subDir.getPersistentData().setModified(false);
        }
    }
    
    /**
     * Rename a child file/dir.
     */
    public void renameChild(String oldName, String newName, boolean fireEvent) {
        CacheFile file = getFile(oldName);
        //System.err.println("CacheDir "+this+"\nRENAME Child("+oldName+", "+newName+")");
        if (file != null) {
            synchronized (CHILD_FILES_LOCK) {
                file.setName(newName);
                childFiles.put(newName, childFiles.remove(oldName));
            }
        } else {
            CacheDir dir = getSubDir(oldName);
            if (dir != null) {
                synchronized (CHILD_FILES_LOCK) {
                    getCacheObject().unregisterDir(dir);
                    dir.setName(newName);
                    childDirs.remove(oldName);
                    childDirs.put(newName, createReference(dir));
                    getCacheObject().registerDir(dir);
                }
            } else fireEvent = false; // We've nothing to fire
        }
        //System.err.println("CacheDir AFTER rename = "+this);
        if (fireEvent) {
            getCacheObject().fireCacheHandlerEvent(FileSystemCache.EVENT_CHANGED, this);
        }
    }
    
    /**
     * Moves all children directories to a new parent.
     * @param fireEvent - if set to true, fires cacheAdded event;
     * also causes the parent directory to get modified
     */
    public void renameChildDirs(CacheDir newParent, boolean fireEvent) {
        CacheDir[] dirs = this.getSubDirs();
        for (int index = 0; index < dirs.length; index++) {
            CacheDir childDir = dirs[index];
            removeChildDir(childDir.getName(), fireEvent);
            childDir.rename(new File(newParent.getAbsolutePath() + File.separator + childDir.getName()));
            newParent.addChildDir(childDir, fireEvent);
            childDir.renameChildDirs(childDir,false);
        }
    }
    
    /** Renames the current instance. 
     * It will also register-unregister the dir in the cache.
     */
    public void rename(File newLocation) {
        getCacheObject().unregisterDir(this);
        dirFile = newLocation;
        this.setName(dirFile.getName());
        getCacheObject().registerDir(this);
    }
    
    
    //----- file stuff -------------------------------------------------------

    public CacheFile[] getFiles() {
        LinkedList col = new LinkedList();
        CacheHandler handler = CacheHandler.getInstance();
        synchronized (CHILD_FILES_LOCK) {
            String cacheFilePath = getCacheFileName();
            BufferedReader in = null;
            File cacheFile = null;
            try {
                if (cacheFilePath != null) {
                    cacheFile = new File(cacheFilePath); // the actual netbeans.cache file
                    //Recycle the same stream using mark and reset
                    //
                    if (cacheFile.exists() && cacheFile.canRead())
                        in = new BufferedReader(new FileReader(cacheFile));
                }
                
                for (Iterator it = new ArrayList(childFiles.keySet()).iterator(); it.hasNext(); ) {
                    String name = (String) it.next();
                    Reference rFile = (Reference) childFiles.get(name);
                    CacheFile cFile = (CacheFile) rFile.get();
                    if (cFile == null) {
                        CacheFile.PersistentData data = handler.getReferencedData(rFile);
                        if (data != null) {
                            cFile = (CacheFile) createChildFromData(data);
                        } else {
                            if (in != null) {
                                in.mark( (int)cacheFile.length());
                            }
                            cFile = readFileFromDisk(name, in);
                            if (in != null) {
                                in.reset();
                            }
                        }
                        cFile.setParent(this);
                        childFiles.put(name, createReference(cFile));
                    }
                    col.add(cFile);
                   
                }
            } catch (FileNotFoundException e) {
            } catch (IOException e) {
            } finally {
                if (in != null) {
                    try {
                        in.close();
                    } catch (IOException exc) {}
                }
            }
            
        }
        return (CacheFile[]) col.toArray(new CacheFile[col.size()]);
    }
    
    public String[] getFileNames() {
        synchronized (CHILD_FILES_LOCK) {
            return (String[]) childFiles.keySet().toArray(new String[0]);
        }
    }
         
    public String[] getDirNames() {
        synchronized (CHILD_FILES_LOCK) {
            return (String[]) childDirs.keySet().toArray(new String[0]);
        }
    }

    /** Gets a file that is in the directory. If it's not there, returns null,
     * if the file was released from memory, it's retrieved from the disk cache.
     * @param name - filename of the file - no path
     */ 
    public CacheFile getFile(String name) {
        synchronized (CHILD_FILES_LOCK) {
            Reference rFile = (Reference) childFiles.get(name);
            if (rFile == null) return null;
            CacheFile cFile = (CacheFile) rFile.get();
            if (cFile == null) {
                CacheFile.PersistentData data = CacheHandler.getInstance().getReferencedData(rFile);
                if (data != null) {
                    cFile = (CacheFile) createChildFromData(data);
                } else {
                    cFile = readFileFromDisk(name);
                }
                cFile.setParent(this);
                childFiles.put(name, createReference(cFile));
            }
            return cFile;
        }
    }
    
    /**
     * Get a cache file if exists in memory. Does not load it from the disk cache.
     */
    protected CacheFile getFileIfExists(String name) {
        synchronized (CHILD_FILES_LOCK) {
            Reference rFile = (Reference) childFiles.get(name);
            if (rFile == null) return null;
            return (CacheFile) rFile.get();
        }
    }
    
    /** adds a file to the directory.
     * @param fireEvent - if true, fires EVENT_ADD event + sets this dir as modified.
     */
    public void addFile(CacheFile fl, boolean fireEvent) {
        synchronized (CHILD_FILES_LOCK) {
            setModifiedContent(true);
            childFiles.put(fl.getName(), createReference(fl));
            fl.setParent(this);
        }
        if (fireEvent) {
            getCacheObject().fireCacheHandlerEvent(FileSystemCache.EVENT_ADD, fl);
        }
    }

    /** removes a file from the directory.
     * @param fireEvent - if true, fires EVENT_REMOVE event + sets this dir as modified.
     */
    public void removeFile(String flName, boolean fireEvent) {
        CacheFile fl = getFile(flName);
        if (fl != null) {
            //System.err.println("CacheDir "+this+"\nREMOVE File("+fl+")");
            synchronized (CHILD_FILES_LOCK) {
                setModifiedContent(true);
                childFiles.remove(flName);
            }
            if (fireEvent) {
                getCacheObject().fireCacheHandlerEvent(FileSystemCache.EVENT_REMOVE, fl);
            }
            fl.setParent(null);
            // Set as not modified to disable write of a deleted cache dir into the disk cache.
            fl.getPersistentData().setModified(false);
        }
    }
    
    /**
     * Removes all files in this directory. No events are fired.
     */
    public void removeFiles() {
        synchronized (CHILD_FILES_LOCK) {
            for (Iterator it = childFiles.values().iterator(); it.hasNext(); ) {
                Reference ref = (Reference) it.next();
                CacheFile file = (CacheFile) ref.get();
                if (file != null) file.setParent(null);
            }
            if (childFiles.size() > 0) setModifiedContent(true);
            childFiles.clear();
        }
    }

    /**
     * Removes all subdir and file cache objects in this directory. No events are fired.
     * @param recursively Remove them all recursively
     */
    public void removeAll(boolean recursively) {
        removeFiles();
        // same for dirs.
        CacheDir[] dirs = getSubDirs();
        for (int i = 0; i < dirs.length; i++) {
            if (recursively) {
                dirs[i].removeAll(true);
            }
            removeChildDir(dirs[i].getName(), false);
        }
    }
    
    
    protected abstract CacheFile createChildFromData(CacheFile.PersistentData data);
    
    /** sets the strategy level taht this directory resides at.
     */
    public final void setAppliedLevel(int strat) {
        /*
        if (strat != CacheHandler.STRAT_NONE) {
            CacheDir parent = getParent();
            if (parent != null) {
                // I need to set a weak reference to me if I have some content!
                synchronized (parent.childDirs) {
                    Object dir = parent.childDirs.get(getName());
                    if (dir instanceof CacheDir) {
                        parent.childDirs.put(getName(), new CacheDirReference((CacheDir) dir));
                    }
                }
            }
        }
         */
        strategy = strat;
    }
    
    /** gets the strategy level taht this directory resides at.
     */
    public final int getAppliedLevel() {
        return strategy;
    }
    
    /** Whether this cache directory was already loaded and thus may contain some
     * interesting content.
     *
    public final boolean isLoaded() {
        return strategy != CacheHandler.STRAT_NONE;
    }
     */
    
    /** returns the name of the directory
     */
    public String getName() {
        return dirFile.getName(); // return short name, just name of file..
        //        return dirName;
    }
    
    public void setName(String name) {
        super.setName(name);
        File parent = dirFile.getParentFile();
        if (parent != null) {
            dirFile = new File(parent, name);
        }
    }
    
    
    /** Absolute path to the directory.
     * By this string the directory can be found by the cache Handler
     */
    public String getAbsolutePath() {
        return dirFile.getAbsolutePath();
    }

    /**
     *
     */
    public String getFilePath() {
        return dirFile.getPath();
    }
    
    public String toString() {
        return this.getName();
    }
    
    /** Sets the dir content as modified. Do so when you want the dir to be written to disk cache later.
     */
    public final void setModifiedContent(boolean modified) {
        this.modified = modified;
        //if (modified) contentChangeddNotify();
    }
    
    /**
     * Notify, that the content of the cache directory has changed and the cache
     * directory content is likely to be saved on disk.
     *
    protected void contentChangeddNotify() {
    }
     */
    
    /** can be used when deciding whether it should be written to disk cache
     *
    public final boolean isModifiedContent() {
        return modified;
    }
     */
    
    /** sets indication about the completeness of the directory in the cache.
     * is set by the CacheQueue when removing the dir's file/subdir.
     * If it was previously modified, then it's first saved to disk.
     *
    public void setComplete(boolean compl) {
        if (this.complete == true && compl == false) {
            if (isModified()) {
                writeToDisk();
            }
            //TODO .. fine tuning might be needed when files that still exist are requested
        }
        complete = compl;
    }
    
    /** check wheather the directory is complete 
     *
    
    public boolean isComplete() {
        return complete;
    }
     */
    

   public boolean isEmpty() {
       return childDirs.size() == 0 && childFiles.size() == 0;
    }
   
   /**
    * Will perform a check on all subdirs and file checking if these are 
    * referenced from the Filesystem, if not will remove them. 
    * Is to be called from ReferenceQueue only.
    *
   void removeChildrenIfNotReferenced(boolean recursively) {
       if (cachedFiles.size() > 0) {
           CacheFile[] files = getFiles();
           for (int i = 0; i < files.length; i++) {
               if (files[i].getReferenceCount() == 0) {
                   setComplete(false);
                   removeFile(files[i].getName(), false);
               }
           }
       }
       if (childDirs.size() > 0) {
           CacheDir[] dirs = getSubDirs();
           for (int i = 0; i < dirs.length; i++) {
               if (dirs[i].getReferenceCount() == 0) {
                   if (recursively) {
                       dirs[i].removeChildrenIfNotReferenced(recursively);
                   }
                   if (dirs[i].isEmpty()) {
                       setComplete(false);
                       removeChildDir(dirs[i].getName(), false);
                   } else {
                       setComplete(false);
                       dirs[i].setParent(null);
                       childDirs.remove(dirs[i].getName());
                   }
               }
           }
       }
   }
    */
 
    
    public final boolean isLocal() {
        return getPersistentData().isLocal();
    }
    
    public final void setLocal(boolean local) {
        this.getPersistentData().setLocal(local);
    }
    
    public FileSystemCache getCacheObject() {
        return cacheObject;
    }
    
    /**
     * Where to find the file that the cache is written to (for this directory).
     * @return The path of file, that this cache dir is written to.
     */
    protected abstract String getCacheFileName();
    
    /** from CacheFile, generates a line that's written to disk cache.
     * By default nothing is written, directories don't get written to cache by default.
     *
    public String writeLineToDisk() {
        return ""; // NOI18N
    }
     */
    
    public void writeToDiskRecursively() {
        writeToDisk();
//        System.out.println("writing..." + this.getName());
        CacheDir[] dirs = getSubDirs();
        for (int i = 0; i < dirs.length; i++) {
            dirs[i].writeToDiskRecursively();
        }
    }
    
    public final void setIgnoreList (java.util.List ignoreList) {
        //System.out.println("CacheDir["+this+"].setIgnoreList("+new java.util.HashSet(ignoreList)+")");
        synchronized (ignoreLock) {
            this.ignoreList = ignoreList;
            this.regExp = null;
            this.ignoreListWasSet = true;
        }
        getCacheObject().fireCacheHandlerEvent(FileSystemCache.EVENT_CHANGED_IGNORE_LIST, this);
    }
    
    public final java.util.List getIgnoreList () {
        return this.ignoreList;
    }
    
    public final boolean isIgnoreListSet() {
        return ignoreListWasSet;
    }
    
    private Object createIgnoreListLock = new Object();
    
    /**
     * This method is called to create an ignore list when it is needed.
     * The default implementation does nothing. Subclasses should
     * call setIgnoreList() method to set the created ignore list.
     */
    protected void createIgnoreList() {
    }
    
    public final boolean isIgnored (String name) {
        //System.out.println("isIgnored("+name+"), ignoreList = "+ignoreList+", ignoreListWasSet = "+ignoreListWasSet);
        if (!isIgnoreListSet()) {
            synchronized (createIgnoreListLock) {
                createIgnoreList();
            }
        }
        synchronized (ignoreLock) {
            if (this.ignoreList == null) {
                return false;
            }
            //System.out.println("isIgnored("+name+"), ignoreList = "+org.netbeans.modules.vcscore.util.VcsUtilities.arrayToString((String[]) ignoreList.toArray(new String[0])));
            //System.out.println(" regExp = "+regExp);
            if (this.regExp == null) {
                String unionExp = VcsUtilities.computeRegularExpressionFromIgnoreList(ignoreList);
                try {
                    this.regExp = Pattern.compile(unionExp);
                    //System.out.println(" **** GOT reg EXP: '"+unionExp+"' *********");
                } catch (PatternSyntaxException malformedRE) {
                    try {
                      this.regExp = Pattern.compile(""); // epsilon, no regular file match epsilon // NOI18N
                    } catch (PatternSyntaxException innerMalformedRE) {}
                }
            }
            //System.out.println(regExp+".match("+name+") = "+regExp.match(name));
            return this.regExp.matcher(name).matches();
        }
    }
    
    private String ignoreListToString() {
        if (ignoreList == null) return "null"; // NOI18N
        StringBuffer list = new StringBuffer();
        for (int i = 0; i < this.ignoreList.size(); i++) {
            list.append("'"+ignoreList.get(i)+"', ");
        }
        return list.toString();
    }

    private Reference createReference(CacheFile cFile) {
        CacheHandler handler = CacheHandler.getInstance();
        Reference ref = new CacheReference(cFile, handler.getCacheFileReferenceQueue());
                      //new WeakReference(cFile, handler.getCacheFileReferenceQueue());
        if (cFile != null) handler.addReferencedData(ref, cFile.getPersistentData());
        return ref;
        //return new /*java.lang.ref.WeakReference(cFile);//*/CacheFileReference(cFile);
    }
    
    /** Release the content of this directory. This method is used to shrink the
     * cache structure when not used. The cache directory should release all it's
     * content and replace itself with a strong reference in it's parent.
     *
    void releaseContent() {
        System.out.println("releaseContent("+dirFile+")");
        if (isModified()) {
            writeToDisk();
        }
        cachedFiles.clear();
        childDirs.clear();
        strategy = CacheHandler.STRAT_NONE;
        CacheDir parent = getParent();
        if (parent != null) {
            synchronized (parent.childDirs) {
                parent.childDirs.put(getName(), shallowCopy());
            }
        }
    }
    
    /** Perform a shallow copy of this cache directory. Create a new instance
     * and copy all attributes. No children files or directories should be copied.
     *
    protected abstract CacheDir shallowCopy();
     */
    
    /** Specify there how the directory content should be written to disk.
     */
    public abstract void writeToDisk();
    
    /** associates with the STRAT_LOCAL, if that strategy is requested this method is called.
     * It should populate the the directory with files on the local disk physically there.
     * No status retrieved.
     */
    public abstract void populateWithLocal(Object locker);
    
    /** associates with the STRAT_DISK, if that strategy is requested this method is called.
     * do load the disk cache in this method. I suggest not to add any files there, just update status etc.
     * adding new files that are not physically presnt can cause trouble.
     */
    public abstract boolean readFromDisk(Object locker);
    
    /**
     * Read just one file from disk cache. This method must not return null.
     * If the file does not exist in the disk cache, create a local file.
     * @param name The file name
     * @return A cache file.
     */
    protected abstract CacheFile readFileFromDisk(String name);
    
    /**
     * Read just one directory from disk cache. This method must not return null.
     * If the directory does not exist in the disk cache, create a local dir.
     * @param name The directory name
     * @return A cache directory.
     */
    protected abstract CacheDir readDirFromDisk(String name);
    
    /**
     * Just like readDirFromDisk(String name), but uses the given file reader 
     * for efficiency.
     * @param name the file name
     * @param in the file reader, can be null, in which case the
     *           method should return a reasonable default cache file.
     * @return a cache file
     */
    protected abstract CacheFile readFileFromDisk(String name, BufferedReader in);
    
    /** associates with the STRAT_REFRESH, if that strategy is requested this method is called.
     * do a server check. Connect to server and request status update etc..
     * @param locker Object, that should be used to "lock" the cache dir content.
     *               The content of this cache directory must not be garbage-collected
     *               before this locker object.
     */
    public abstract void checkServer(Object locker);
  
    /** associates with the STRAT_REFRESH_RECURS, if that strategy is requested this method is called.
     * do a recursive server check. Connect to server and request status update etc..
     * Note: within this method implementations need to make sure
     * @param locker Object, that should be used to "lock" the cache dir content recursively.
     *               The content of this cache directory and all it's subdirectories
     *               must not be garbage-collected before this locker object.
     */
    public abstract void checkServerRecursive(Object locker);

    // Invariants ~~~~~~~~~~~~~~~~~~~~~~~~~

    /**
     * Assert consistency with local disk,  all folders must be modeled by cache dirs.
     * Even local folders.
     * @throws AssertionError if inconsistent
     * @return true to be callable by assert statement
     */
    public boolean directoriesConsistencyInvariant() {
        String[] cacheDirNames = new String[0];
        String[] fsDirNames = new String[0];
        assert (cacheDirNames = (String[]) childDirs.keySet().toArray(new String[0])) != null;
        assert (fsDirNames = dirFile.list(DIR_FILTER)) != null : "Probably is not directory " + dirFile;
        assert cacheDirNames.length == fsDirNames.length :
               dirFile + " cache content(dirs) inconsistent!\n" + prepareAssertMsg(fsDirNames, cacheDirNames);

        return true;
    }

    /** Accepts only directories. */
    private static final FilenameFilter DIR_FILTER = new DirFilter();

    private static class DirFilter implements FilenameFilter {
        public boolean accept(File dir, String name) {
            return new File(dir, name).isDirectory();
        }
    }

    private String prepareAssertMsg(String[] fsDirs, String[] cacheDirs) {
        StringBuffer sb = new StringBuffer();

        TreeSet set = new TreeSet();
        for (int i = 0; i
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.