|
What this is
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 |
... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
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.