FileManagerImpl.java

Index Score
com.limegroup.gnutella
FrostWire

View: Reasons, Metrics, Source Code

These are the metrics that contribute to the Enerjy Score for this file, ranked by impact. So the metrics listed at the top influence the score to a greater extent that the metrics listed at the bottom.

MetricDescription
LINE_COMMENTNumber of line comments
EXEC_COMMENTSComments in executable code
DECL_COMMENTSComments in declarations
JAVA0034JAVA0034 Missing braces in if statement
SIZESize of the file in bytes
CYCLOMATICCyclomatic complexity
INTERFACE_COMPLEXITYInterface complexity
COMPARISONSNumber of comparison operators
RETURNSNumber of return points from functions
OPERATORSNumber of operators
EXITSProcedure exits
PROGRAM_LENGTHHalstead program length
LINESNumber of lines in the source file
LOGICAL_LINESNumber of statements
ELOCEffective lines of code
OPERANDSNumber of operands
COMMENTSComment lines
LOCLines of code
PARAMSNumber of formal parameter declarations
UNIQUE_OPERANDSNumber of unique operands
BLOCKSNumber of blocks
FUNCTIONSNumber of function declarations
PROGRAM_VOCABHalstead program vocabulary
JAVA0108JAVA0108 Incorrect javadoc: no @param tag for 'parameter'
JAVA0143JAVA0143 Synchronized method
LOOPSNumber of loops
JAVA0020JAVA0020 Field name does not have required form
WHITESPACENumber of whitespace lines
DOC_COMMENTNumber of javadoc comment lines
BLOCK_COMMENTNumber of block comment lines
JAVA0110JAVA0110 Incorrect javadoc: no @return tag
JAVA0270JAVA0270 Use Java 5.0 enhanced for loop construct to iterate over all elements in an array
JAVA0035JAVA0035 Missing braces in for statement
JAVA0173JAVA0173 Unused method parameter
JAVA0177JAVA0177 Variable declaration missing initializer
JAVA0123JAVA0123 Use all three components of for loop
UNIQUE_OPERATORSNumber of unique operators
JAVA0144JAVA0144 Line exceeds maximum M characters
JAVA0076JAVA0076 Use of magic number
JAVA0282JAVA0282 Call to Iterator.next() in loop which does not test Iterator.hasNext()
JAVA0119JAVA0119 Control variable changed within body of for loop
JAVA0130JAVA0130 Non-static method does not use instance fields
JAVA0138JAVA0138 N parameters defined for method (maximum: M)
JAVA0061JAVA0061 Inaccessible member in anonymous class
JAVA0117JAVA0117 Missing javadoc: method 'method'
JAVA0008JAVA0008 Empty catch block
JAVA0128JAVA0128 Public constructor in non-public class
JAVA0077JAVA0077 Private field not used in declaring class
PROGRAM_VOLUMEHalstead program volume
JAVA0259JAVA0259 Return of collection/array field
JAVA0136JAVA0136 N methods defined in class (maximum: M)
JAVA0126JAVA0126 Method declares unchecked exception in throws
JAVA0264JAVA0264 Integer math in long context - check for overflow
JAVA0100JAVA0100 Class contains N non-final fields (maximum: M)
NEST_DEPTHMaximum nesting depth
package com.limegroup.gnutella; import java.io.File; import java.io.FileFilter; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; import java.util.TreeMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.limewire.collection.Comparators; import org.limewire.collection.Function; import org.limewire.collection.IntSet; import org.limewire.collection.MultiCollection; import org.limewire.collection.MultiIterator; import org.limewire.collection.StringTrie; import org.limewire.concurrent.ExecutorsHelper; import org.limewire.inspection.Inspectable; import org.limewire.inspection.InspectableContainer; import org.limewire.inspection.InspectableForSize; import org.limewire.inspection.InspectablePrimitive; import org.limewire.inspection.InspectionPoint; import org.limewire.setting.StringArraySetting; import org.limewire.statistic.StatsUtils; import org.limewire.util.ByteOrder; import org.limewire.util.FileUtils; import org.limewire.util.I18NConvert; import org.limewire.util.RPNParser; import org.limewire.util.StringUtils; import com.google.inject.Inject; import com.limegroup.gnutella.FileManagerEvent.Type; import com.limegroup.gnutella.auth.ContentResponseData; import com.limegroup.gnutella.auth.ContentResponseObserver; import com.limegroup.gnutella.downloader.VerifyingFile; import com.limegroup.gnutella.library.LibraryData; import com.limegroup.gnutella.library.SharingUtils; import com.limegroup.gnutella.licenses.LicenseType; import com.limegroup.gnutella.messages.QueryRequest; import com.limegroup.gnutella.routing.HashFunction; import com.limegroup.gnutella.routing.QueryRouteTable; import com.limegroup.gnutella.settings.DHTSettings; import com.limegroup.gnutella.settings.MessageSettings; import com.limegroup.gnutella.settings.SearchSettings; import com.limegroup.gnutella.settings.SharingSettings; import com.limegroup.gnutella.simpp.SimppListener; import com.limegroup.gnutella.util.LimeWireUtils; import com.limegroup.gnutella.xml.LimeXMLDocument; /** * The list of all shared files. Provides operations to add and remove * individual files, directory, or sets of directories. Provides a method to * efficiently query for files whose names contain certain keywords.<p> * * This class is thread-safe. */ //@Singleton // abstract, so not a singleton -- subclasses are. public abstract class FileManagerImpl implements FileManager { private static final Log LOG = LogFactory.getLog(FileManagerImpl.class); private static final ExecutorService LOADER = ExecutorsHelper.newProcessingQueue("FileManagerLoader"); /** * An index value that describes store files which have a file descriptor but are not * shared. */ private static final int STORE_FILEDESC_INDEX = Integer.MAX_VALUE; /** * delay between qrp updates should the simpp words change. * Not final for testing. Betas update faster for experiments. */ private static long QRP_DELAY = (LimeWireUtils.isBetaRelease() ? 1 : 60) * 60 * 1000; /** List of event listeners for FileManagerEvents. */ private volatile CopyOnWriteArrayList<FileEventListener> eventListeners = new CopyOnWriteArrayList<FileEventListener>(); /********************************************************************** * LOCKING: obtain this's monitor before modifying this. **********************************************************************/ /** * All of the data for FileManager. */ private final LibraryData _data = new LibraryData(); /** * The list of complete and incomplete files. An entry is null if it * is no longer shared. * INVARIANT: for all i, _files[i]==null, or _files[i].index==i and either * _files[i]._path is in a shared directory with a shareable extension or * _files[i]._path is the incomplete directory if _files[i] is an IncompleteFileDesc. */ private List<FileDesc> _files; /** * The total size of all complete files, in bytes. * INVARIANT: _filesSize=sum of all size of the elements of _files, * except IncompleteFileDescs, whose size may change at any time. */ @InspectablePrimitive("total size of shared files") private long _filesSize; /** * The number of complete files. * INVARIANT: _numFiles==number of elements of _files that are not null * and not IncompleteFileDescs. */ @InspectablePrimitive("number of shared files") private int _numFiles; /** * The total number of files that are pending sharing. * (ie: awaiting hashing or being added) */ @InspectablePrimitive("number of pending files") private int _numPendingFiles; /** * The total number of incomplete files. * INVARIANT: _numFiles + _numIncompleteFiles == the number of * elements of _files that are not null. */ @InspectablePrimitive("number of incomplete files") private int _numIncompleteFiles; /** * The number of files that are forcibly shared over the network. * INVARIANT: _numFiles >= _numForcedFiles. */ @InspectablePrimitive("number force-shared files") private int _numForcedFiles; /** * An index that maps a <tt>File</tt> on disk to the * <tt>FileDesc</tt> holding it. * * INVARIANT: For all keys k in _fileToFileDescMap, * _files[_fileToFileDescMap.get(k).getIndex()].getFile().equals(k) * * Keys must be canonical <tt>File</tt> instances. */ private Map<File, FileDesc> _fileToFileDescMap; /** * A trie mapping keywords in complete filenames to the indices in _files. * Keywords are the tokens when the filename is tokenized with the * characters from DELIMITERS as delimiters. * * IncompleteFile keywords are NOT stored. * * INVARIANT: For all keys k in _keywordTrie, for all i in the IntSet * _keywordTrie.get(k), _files[i]._path.substring(k)!=-1. Likewise for all * i, for all k in _files[i]._path where _files[i] is not an * IncompleteFileDesc, _keywordTrie.get(k) contains i. */ @InspectableForSize("size of keyword trie") private StringTrie<IntSet> _keywordTrie; /** * A trie mapping keywords in complete filenames to the indices in _files. * Contains ONLY incomplete keywords. */ @InspectableForSize("size of incomplete keyword trie") private StringTrie<IntSet> _incompleteKeywordTrie; /** * A map of appropriately case-normalized URN strings to the * indices in _files. Used to make query-by-hash faster. * * INVARIANT: for all keys k in _urnMap, for all i in _urnMap.get(k), * _files[i].containsUrn(k). Likewise for all i, for all k in * _files[i].getUrns(), _urnMap.get(k) contains i. */ private Map<URN, IntSet> _urnMap; /** * The set of file extensions to share, sorted by StringComparator. * INVARIANT: all extensions are lower case. */ private static Set<String> _extensions; /** * A Set of shared directories that are completely shared. Files in these * directories are shared by default and will be shared unless the File is * listed in SharingSettings.FILES_NOT_TO_SHARE. */ @InspectableForSize("number completely shared directories") private Set<File> _completelySharedDirectories; /** * The IntSet for incomplete shared files. * * INVARIANT: for all i in _incompletesShared, * _files[i]._path == the incomplete directory. * _files[i] instanceof IncompleteFileDesc * Likewise, for all i s.t. * _files[i] != null and _files[i] instanceof IncompleteFileDesc, * _incompletesShared.contains(i) * * This structure is not strictly needed for correctness, but it allows * others to retrieve all the incomplete shared files, which is * relatively useful. */ @InspectableForSize("number incompletely shared files") private IntSet _incompletesShared; /** * A Set of URNs that we're currently requesting validation for. * This is NOT cleared on new revisions, because it'll always be * valid. */ private Set<URN> _requestingValidation = Collections.synchronizedSet(new HashSet<URN>()); /** * Files that are shared only for this LW session. * INVARIANT: no file can be in this and _data.SPECIAL_FILES_TO_SHARE * at the same time */ @InspectableForSize("number of transiently shared files") private Set<File> _transientSharedFiles = new HashSet<File>(); /** * An index that maps a LWS <tt>File</tt> on disk to the * <tt>FileDesc</tt> holding it. * * INVARIANT: For all keys k in _fileToFileDescMap, * _files[_fileToFileDescMap.get(k).getIndex()].getFile().equals(k) * * Keys must be canonical <tt>File</tt> instances. */ private Map<File, FileDesc> _storeToFileDescMap; /** * The directory for downloading LWS songs to and any subdirectories * that may recursively exist */ @InspectableForSize("number of directories for the store") private Set<File> _storeDirectories; /** * Individual files that are not in a shared folder. */ @InspectableForSize("number of individually shared files") private Collection<File> _individualSharedFiles; /** * The revision of the library. Every time 'loadSettings' is called, the revision * is incremented. */ @InspectablePrimitive("filemanager revision") protected volatile int _revision = 0; /** * The revision that finished loading all pending files. */ @InspectablePrimitive("revision that finished loading") private volatile int _pendingFinished = -1; /** * The revision that finished updating shared directories. */ private volatile int _updatingFinished = -1; /** * If true, indicates that the FileManager is currently updating. */ @InspectablePrimitive("filemanager currently updating") private volatile boolean _isUpdating = false; /** * The last revision that finished both pending & updating. */ private volatile int _loadingFinished = -1; /** * Whether the FileManager has been shutdown. */ protected volatile boolean shutdown; /** * Different types of files to be added to the filemanager */ public enum AddType{ ADD_SHARE(Type.ADD_FILE, Type.ADD_FAILED_FILE), ADD_STORE(Type.ADD_STORE_FILE, Type.ADD_STORE_FAILED_FILE); private final Type success; private final Type failure; AddType(Type success, Type failure) { this.success = success; this.failure = failure; } public Type getSuccessType(){ return success; } public Type getFailureType(){ return failure; } }; /** * The filter object to use to discern shareable files. */ private final FileFilter SHAREABLE_FILE_FILTER = new FileFilter() { public boolean accept(File f) { return isFileShareable(f); } }; /** * The filter object to use to determine directories. */ private static final FileFilter DIRECTORY_FILTER = new FileFilter() { public boolean accept(File f) { return f.isDirectory(); } }; /** * An empty callback so we don't have to do != null checks everywhere. */ private static final FileEventListener EMPTY_CALLBACK = new FileEventListener() { public void handleFileEvent(FileManagerEvent evt) {} }; /** * The QueryRouteTable kept by this. The QueryRouteTable will be * lazily rebuilt when necessary. */ protected static QueryRouteTable _queryRouteTable; /** * Boolean for checking if the QRT needs to be rebuilt. */ protected static volatile boolean _needRebuild = true; private static final boolean isDelimiter(char c) { switch (c) { case ' ': case '-': case '.': case '_': case '+': case '/': case '*': case '(': case ')': case '\\': case ',': return true; default: return false; } } private final QRPUpdater qrpUpdater = new QRPUpdater(); /** Contains the definition of a rare file */ private final RareFileDefinition rareDefinition; protected final FileManagerController fileManagerController; /** * Creates a new <tt>FileManager</tt> instance. */ @Inject public FileManagerImpl(FileManagerController fileManagerController) { this.fileManagerController = fileManagerController; // We'll initialize all the instance variables so that the FileManager // is ready once the constructor completes, even though the // thread launched at the end of the constructor will immediately // overwrite all these variables rareDefinition = new RareFileDefinition(); resetVariables(); } /** * Method that resets all of the variables for this class, maintaining * all invariants. This is necessary, for example, when the shared * files are reloaded. */ private void resetVariables() { _filesSize = 0; _numFiles = 0; _numIncompleteFiles = 0; _numPendingFiles = 0; _numForcedFiles = 0; _files = new ArrayList<FileDesc>(); _keywordTrie = new StringTrie<IntSet>(true); //ignore case _incompleteKeywordTrie = new StringTrie<IntSet>(true); _urnMap = new HashMap<URN, IntSet>(); _extensions = new HashSet<String>(); _completelySharedDirectories = new HashSet<File>(); _incompletesShared = new IntSet(); _fileToFileDescMap = new HashMap<File, FileDesc>(); // the transient files and the special files. _individualSharedFiles = Collections.synchronizedCollection( new MultiCollection<File>(_transientSharedFiles, _data.SPECIAL_FILES_TO_SHARE)); _storeToFileDescMap = new HashMap<File, FileDesc>(); _storeDirectories = new HashSet<File>(); } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#start() */ public void start() { _data.clean(); cleanIndividualFiles(); loadSettings(); fileManagerController.addSimppListener(qrpUpdater); } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#startAndWait(long) */ public void startAndWait(long timeout) throws InterruptedException, TimeoutException { final CountDownLatch startedLatch = new CountDownLatch(1); FileEventListener listener = new FileEventListener() { public void handleFileEvent(FileManagerEvent evt) { if (evt.getType() == Type.FILEMANAGER_LOADED) { startedLatch.countDown(); } } }; try { addFileEventListener(listener); start(); if (!startedLatch.await(timeout, TimeUnit.MILLISECONDS)) { throw new TimeoutException("Initialization of FileManager did not complete within " + timeout + " ms"); } } finally { removeFileEventListener(listener); } } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#stop() */ public void stop() { save(); fileManagerController.removeSimppListener(qrpUpdater); shutdown = true; } protected void save(){ _data.save(); fileManagerController.save(); } /////////////////////////////////////////////////////////////////////////// // Accessors /////////////////////////////////////////////////////////////////////////// /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#getSize() */ public int getSize() { return ByteOrder.long2int(_filesSize); } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#getNumFiles() */ public int getNumFiles() { return _numFiles - _numForcedFiles; } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#getNumStoreFiles() */ public int getNumStoreFiles() { return _storeToFileDescMap.size(); } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#getNumIncompleteFiles() */ public int getNumIncompleteFiles() { return _numIncompleteFiles; } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#getNumPendingFiles() */ public int getNumPendingFiles() { return _numPendingFiles; } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#getNumForcedFiles() */ public int getNumForcedFiles() { return _numForcedFiles; } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#get(int) */ public synchronized FileDesc get(int i) { return _files.get(i); } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#isValidIndex(int) */ public synchronized boolean isValidIndex(int i) { return (i >= 0 && i < _files.size()); } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#getURNForFile(java.io.File) */ public synchronized URN getURNForFile(File f) { FileDesc fd = getFileDescForFile(f); if (fd != null) return fd.getSHA1Urn(); return null; } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#getFileDescForFile(java.io.File) */ public synchronized FileDesc getFileDescForFile(File f) { try { f = FileUtils.getCanonicalFile(f); } catch(IOException ioe) { return null; } if(_fileToFileDescMap.containsKey(f)) return _fileToFileDescMap.get(f); else return _storeToFileDescMap.get(f); } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#isUrnShared(com.limegroup.gnutella.URN) */ public synchronized boolean isUrnShared(final URN urn) { FileDesc fd = getFileDescForUrn(urn); return fd != null && !(fd instanceof IncompleteFileDesc); } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#getFileDescForUrn(com.limegroup.gnutella.URN) */ public synchronized FileDesc getFileDescForUrn(final URN urn) { if (!urn.isSHA1()) throw new IllegalArgumentException(); IntSet indices = _urnMap.get(urn); if(indices == null) return null; IntSet.IntSetIterator iter = indices.iterator(); //Pick the first non-null non-Incomplete FileDesc. FileDesc ret = null; while ( iter.hasNext() && ( ret == null || ret instanceof IncompleteFileDesc) ) { int index = iter.next(); ret = _files.get(index); } return ret; } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#getIncompleteFileDescriptors() */ public synchronized FileDesc[] getIncompleteFileDescriptors() { if (_incompletesShared == null) return null; FileDesc[] ret = new FileDesc[_incompletesShared.size()]; IntSet.IntSetIterator iter = _incompletesShared.iterator(); for (int i = 0; iter.hasNext(); i++) { FileDesc fd = _files.get(iter.next()); assert fd != null : "Directory has null entry"; ret[i]=fd; } return ret; } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#getAllSharedFileDescriptors() */ public synchronized FileDesc[] getAllSharedFileDescriptors() { // Instead of using _files.toArray, use // _fileToFileDescMap.values().toArray. This is because // _files will still contain null values for removed // shared files, but _fileToFileDescMap will not. FileDesc[] fds = new FileDesc[_fileToFileDescMap.size()]; fds = _fileToFileDescMap.values().toArray(fds); return fds; } /** * Returns a list of all shared file descriptors in the given directory, * in any order. * * Returns null if directory is not shared, or a zero-length array if it is * shared but contains no files. This method is not recursive; files in * any of the directory's children are not returned. * * This operation is <b>not</b> efficient, and should not be done often. */ public synchronized List<FileDesc> getSharedFilesInDirectory(File directory) { if (directory == null) throw new NullPointerException("null directory"); // a. Remove case, trailing separators, etc. try { directory = FileUtils.getCanonicalFile(directory); } catch (IOException e) { // invalid directory ? return Collections.emptyList(); } List<FileDesc> shared = new ArrayList<FileDesc>(); for(FileDesc fd : _fileToFileDescMap.values()) { if(directory.equals(fd.getFile().getParentFile())) shared.add(fd); } return shared; } /////////////////////////////////////////////////////////////////////////// // Loading /////////////////////////////////////////////////////////////////////////// /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#loadSettings() */ public void loadSettings() { final int currentRevision = ++_revision; if(LOG.isDebugEnabled()) LOG.debug("Starting new library revision: " + currentRevision); LOADER.execute(new Runnable() { public void run() { loadStarted(currentRevision); loadSettingsInternal(currentRevision); } }); } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#loadSettingsAndWait(long) */ public void loadSettingsAndWait(long timeout) throws InterruptedException, TimeoutException { final CountDownLatch loadedLatch = new CountDownLatch(1); FileEventListener listener = new FileEventListener() { public void handleFileEvent(FileManagerEvent evt) { if (evt.getType() == Type.FILEMANAGER_LOADED) { loadedLatch.countDown(); } } }; try { addFileEventListener(listener); loadSettings(); if (!loadedLatch.await(timeout, TimeUnit.MILLISECONDS)) { throw new TimeoutException("Loading of FileManager settings did not complete within " + timeout + " ms"); } } finally { removeFileEventListener(listener); } } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#loadWithNewDirectories(java.util.Set, java.util.Set) */ public void loadWithNewDirectories(Set<? extends File> shared, Set<File> blackListSet) { SharingSettings.DIRECTORIES_TO_SHARE.setValue(shared); synchronized(_data.DIRECTORIES_NOT_TO_SHARE) { _data.DIRECTORIES_NOT_TO_SHARE.clear(); _data.DIRECTORIES_NOT_TO_SHARE.addAll(canonicalize(blackListSet)); _storeDirectories.clear(); _storeDirectories.add(SharingSettings.getSaveLWSDirectory()); } loadSettings(); } /** * Kicks off necessary stuff for a load being started. */ protected void loadStarted(int revision) { fileManagerController.loadStarted(); } /** * Notification that something finished loading. */ private void tryToFinish() { int revision; synchronized(this) { if(_pendingFinished != _updatingFinished || // Pending's revision must == update _pendingFinished != _revision || // The revision must be the current library's _loadingFinished >= _revision) // And we can't have already finished. return; _loadingFinished = _revision; revision = _loadingFinished; } loadFinished(revision); } /** * Kicks off necessary stuff for loading being done. */ protected void loadFinished(int revision) { if(LOG.isDebugEnabled()) LOG.debug("Finished loading revision: " + revision); // Various cleanup & persisting... trim(); fileManagerController.loadFinished(); save(); fileManagerController.loadFinishedPostSave(); dispatchFileEvent(new FileManagerEvent(this, Type.FILEMANAGER_LOADED)); } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#isLoadFinished() */ public boolean isLoadFinished() { return _loadingFinished == _revision; } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#isUpdating() */ public boolean isUpdating() { return _isUpdating; } /** * Loads all shared files, putting them in a queue for being added. * * If the current revision ever changed from the expected revision, this returns * immediately. */ protected void loadSettingsInternal(int revision) { if(LOG.isDebugEnabled()) LOG.debug("Loading Library Revision: " + revision); final File[] directories; synchronized (this) { // Reset the file list info resetVariables(); // Load the extensions. String[] extensions = StringArraySetting.decode(SharingSettings.EXTENSIONS_TO_SHARE.getValue().toLowerCase()); for( String ext : extensions ) { _extensions.add(ext); } // Add any extra extensions per chance if (SharingSettings.EXTENSIONS_LIST_CUSTOM.getValue().length() > 0) { String[] array = StringArraySetting.decode(SharingSettings.EXTENSIONS_LIST_CUSTOM.getValue()); for( String ext : array ) { _extensions.add(ext); } } // Assert no sensitive extensions are shared if (SharingSettings.DISABLE_SENSITIVE.getValue()) { for( String ext : SharingSettings.getDefaultDisabledExtensions() ) { _extensions.remove(ext); } } // Assert no disabled extensions are shared if (SharingSettings.EXTENSIONS_LIST_UNSHARED.getValue().length() > 0) { String[] array = StringArraySetting.decode(SharingSettings.EXTENSIONS_LIST_UNSHARED.getValue()); for( String ext : array ) { _extensions.remove(ext); } } //Ideally we'd like to ensure that "C:\dir\" is loaded BEFORE //C:\dir\subdir. Although this isn't needed for correctness, it may //help the GUI show "subdir" as a subdirectory of "dir". One way of //doing this is to do a full topological sort, but that's a lot of //work. So we just approximate this by sorting by filename length, //from smallest to largest. Unless directories are specified as //"C:\dir\..\dir\..\dir", this will do the right thing. directories = SharingSettings.DIRECTORIES_TO_SHARE.getValueAsArray(); Arrays.sort(directories, new Comparator<File>() { public int compare(File a, File b) { return a.toString().length()-b.toString().length(); } }); } //clear this, list of directories retrieved fileManagerController.fileManagerLoading(); dispatchFileEvent(new FileManagerEvent(this, Type.FILEMANAGER_LOADING)); // Update the FORCED_SHARE directory. updateSharedDirectories(SharingUtils.PROGRAM_SHARE, null, revision); updateSharedDirectories(SharingUtils.PREFERENCE_SHARE, null, revision); //Load the shared directories and add their files. _isUpdating = true; for(int i = 0; i < directories.length && _revision == revision; i++) updateSharedDirectories(directories[i], null, revision); // Add specially shared files Collection<File> specialFiles = _individualSharedFiles; ArrayList<File> list; synchronized(specialFiles) { // iterate over a copied list, since addFileIfShared might call // _data.SPECIAL_FILES_TO_SHARE.remove() which can cause a concurrent // modification exception list = new ArrayList<File>(specialFiles); } for(File file : list) { if(_revision != revision) break; System.out.println("*****SPECIAL FILE IS: " + file); addFileIfSharedOrStore(file, EMPTY_DOCUMENTS, true, _revision, null, AddType.ADD_SHARE); } // Update the store directory and add only files from the LWS /* File storeDir = SharingSettings.getSaveLWSDirectory(); updateStoreDirectories(storeDir.getAbsoluteFile(), null, revision); // Optain the list of lws files found in shared directories Collection<File> specialStoreFiles = _data.SPECIAL_STORE_FILES; ArrayList<File> storeList; synchronized (specialStoreFiles) { storeList = new ArrayList<File>(specialStoreFiles); } */ // list lws files found in shared directories in the special // store files node /* for(File file: storeList) { if(_revision != revision) break; addFileIfSharedOrStore(file, EMPTY_DOCUMENTS, true, _revision, null, AddType.ADD_STORE); } */ _isUpdating = false; trim(); if(LOG.isDebugEnabled()) LOG.debug("Finished queueing shared files for revision: " + revision); synchronized (this) { _updatingFinished = revision; if(_numPendingFiles == 0) // if we didn't even try adding any files, pending is finished also. _pendingFinished = revision; } tryToFinish(); } private void updateSharedDirectories(File directory, File parent, int revision) { updateSharedDirectories(directory, directory, parent, revision, 1); } /** * Recursively adds this directory and all subdirectories to the shared * directories as well as queueing their files for sharing. Does nothing * if <tt>directory</tt> doesn't exist, isn't a directory, or has already * been added. This method is thread-safe. It acquires locks on a * per-directory basis. If the current revision ever changes from the * expected revision, this returns immediately. * * @requires directory is part of DIRECTORIES_TO_SHARE or one of its * children, and parent is directory's shared parent or null if * directory's parent is not shared. * @modifies this */ private void updateSharedDirectories(File rootShare, File directory, File parent, int revision, int depth) { // if(LOG.isDebugEnabled()) // LOG.debug("Attempting to share directory: " + directory); //We have to get the canonical path to make sure "D:\dir" and "d:\DIR" //are the same on Windows but different on Unix. try { directory = FileUtils.getCanonicalFile(directory); } catch (IOException e) { return; } if(!directory.exists()) return; // STEP 0: // Do not share unsharable directories. if(!isFolderShareable(directory, true)) return; // Do not share sensitive directories if (SharingUtils.isSensitiveDirectory(directory)) { // go through directories that explicitly should not be shared if (_data.SENSITIVE_DIRECTORIES_NOT_TO_SHARE.contains(directory)) { return; } // if we haven't already validated the sensitive directory, ask about it. if (!_data.SENSITIVE_DIRECTORIES_VALIDATED.contains(directory)) { // ask the user whether the sensitive directory should be shared // THIS CALL CAN BLOCK. if (!fileManagerController.warnAboutSharingSensitiveDirectory(directory)) return; } } // Exit quickly (without doing the dir lookup) if revisions changed. if(_revision != revision) return; // STEP 1: // Add directory boolean isForcedShare = SharingUtils.isForcedShareDirectory(directory); synchronized (this) { // if it was already added, ignore. if (_completelySharedDirectories.contains(directory)) return; if (!_storeDirectories.contains(directory)) _completelySharedDirectories.add(directory); if (!isForcedShare) { dispatchFileEvent( new FileManagerEvent(this, Type.ADD_FOLDER, rootShare, depth, directory, parent)); } } // STEP 2: // Scan subdirectory for the amount of shared files. File[] file_list = directory.listFiles(SHAREABLE_FILE_FILTER); if (file_list == null) return; for(int i = 0; i < file_list.length && _revision == revision; i++) addFileIfSharedOrStore(file_list[i], EMPTY_DOCUMENTS, true, _revision, null, AddType.ADD_SHARE); // Exit quickly (without doing the dir lookup) if revisions changed. if(_revision != revision) return; // STEP 3: // Recursively add subdirectories. // This has the effect of ensuring that the number of pending files // is closer to correct number. // TODO: when we add non-recursive support, add it here. if (isForcedShare) return; // Do not share subdirectories of the forcibly shared dir. File[] dir_list = directory.listFiles(DIRECTORY_FILTER); if(dir_list != null) { for(int i = 0; i < dir_list.length && _revision == revision; i++) updateSharedDirectories(rootShare, dir_list[i], directory, revision, depth+1); } } /** * Recursively add files from the LWS download directory. Does nothing * if <tt>directory</tt> doesn't exist, isn't a directory, or has already * been added. This method is thread-safe. It acquires locks on a * per-directory basis. If the current revision ever changes from the * expected revision, this returns immediately. * * @requires directory is part of _storeDirectories or one of its * children, and parent is directory's store directory parent * @modifies this */ private void updateStoreDirectories(File directory, File parent, int revision) { //We have to get the canonical path to make sure "D:\dir" and "d:\DIR" //are the same on Windows but different on Unix. try { directory = FileUtils.getCanonicalFile(directory); } catch (IOException e) { return; } if(!directory.exists()) return; // Exit quickly (without doing the dir lookup) if revisions changed. if(_revision != revision) return; synchronized (this) { // if it was already added, ignore. if (_storeDirectories.contains(directory)) return; //otherwise add this directory to list to avoid rescanning it _storeDirectories.add(directory); dispatchFileEvent( new FileManagerEvent(this, Type.ADD_STORE_FOLDER, directory, parent)); } // STEP 2: // Scan subdirectory for the amount of shared files. File[] file_list = directory.listFiles(); if (file_list == null) return; for(int i = 0; i < file_list.length && _revision == revision; i++) addFileIfSharedOrStore(file_list[i], EMPTY_DOCUMENTS, true, _revision, null, AddType.ADD_STORE); // Exit quickly (without doing the dir lookup) if revisions changed. if(_revision != revision) return; // STEP 3: // Recursively add subdirectories. // This has the effect of ensuring that the number of pending files // is closer to correct number. File[] dir_list = directory.listFiles(DIRECTORY_FILTER); if(dir_list != null) { for(int i = 0; i < dir_list.length && _revision == revision; i++) updateStoreDirectories(dir_list[i], directory, revision); } } /////////////////////////////////////////////////////////////////////////// // Adding and removing shared files and directories /////////////////////////////////////////////////////////////////////////// /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#removeFolderIfShared(java.io.File) */ public void removeFolderIfShared(File folder) { _isUpdating = true; removeFolderIfShared(folder, null); _isUpdating = false; } /** * Removes a given directory from being completed shared. * If 'parent' is null, this will remove it from the root-level of * shared folders if it existed there. (If it is non-null & it was * a root-level shared folder, the folder remains shared.) * * The first time this is called, parent must be non-null in order to ensure * it works correctly. Otherwise, we'll end up adding tons of stuff * to the DIRECTORIES_NOT_TO_SHARE. */ protected void removeFolderIfShared(File folder, File parent) { if (!folder.isDirectory() && folder.exists()) throw new IllegalArgumentException("Expected a directory, but given: "+folder); try { folder = FileUtils.getCanonicalFile(folder); } catch(IOException ignored) {} // grab the value quickly. release the lock // so that we don't hold it during a long recursive function. // it's no big deal if it changes, we'll just do some extra work for a short // bit of time. boolean contained; synchronized(this) { contained = _completelySharedDirectories.contains(folder); } if(contained) { if(parent != null && SharingSettings.DIRECTORIES_TO_SHARE.contains(folder)) { // we don't wanna remove it, since it's a root-share, nor do we want // to remove any of its children, so we return immediately. return; } else if(parent == null) { // Add the directory in the exclude list if it wasn't in the DIRECTORIES_NOT_TO_SHARE, // or if it was *and* a parent folder of it is fully shared. boolean explicitlyShared = SharingSettings.DIRECTORIES_TO_SHARE.remove(folder); if(!explicitlyShared || isFolderShared(folder.getParentFile())) _data.DIRECTORIES_NOT_TO_SHARE.add(folder); } // note that if(parent != null && not a root share) // we DO NOT ADD to DIRECTORIES_NOT_TO_SHARE. // this is by design, because the parent has already been removed // from sharing, which inherently will remove the child directories. // there's no need to clutter up DIRECTORIES_NOT_TO_SHARE with useless // entries. synchronized(this) { _completelySharedDirectories.remove(folder); } File[] subs = folder.listFiles(); if(subs != null) { for(int i = 0; i < subs.length; i++) { File f = subs[i]; if(f.isDirectory()) removeFolderIfShared(f, folder); else if(f.isFile() && !_individualSharedFiles.contains(f)) { if(removeFileIfShared(f) == null) fileManagerController.clearPendingShare(f); if(isStoreFile(f)) _data.SPECIAL_STORE_FILES.remove(f); } } } // send the event last. this is a hack so that the GUI can properly // receive events with the children first, moving any leftover children up to // potential parent directories. dispatchFileEvent( new FileManagerEvent(this, Type.REMOVE_FOLDER, folder)); } } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#addSharedFolders(java.util.Set, java.util.Set) */ public void addSharedFolders(Set<File> folders, Set<File> blackListedSet) { if (folders.isEmpty()) { throw new IllegalArgumentException("Only blacklisting without sharing, not allowed"); } _data.DIRECTORIES_NOT_TO_SHARE.addAll(canonicalize(blackListedSet)); for (File folder : folders) { addSharedFolder(folder); } } /** * Returns set of canonicalized files or the same set if there * was an IOException for one of the files while canconicalizing. */ private static Set<File> canonicalize(Set<File> files) { // canonicalize blacklist Set<File> canonical = new HashSet<File>(files.size()); try { for (File excluded : files) { canonical.add(FileUtils.getCanonicalFile(excluded)); } } catch (IOException ie) { // use original black list if we run into problems canonical = files; } return canonical; } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#getFolderNotToShare() */ public Set<File> getFolderNotToShare() { synchronized (_data.DIRECTORIES_NOT_TO_SHARE) { return new HashSet<File>(_data.DIRECTORIES_NOT_TO_SHARE); } } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#addSharedFolder(java.io.File) */ public boolean addSharedFolder(File folder) { if (!folder.isDirectory()) throw new IllegalArgumentException("Expected a directory, but given: "+folder); try { folder = FileUtils.getCanonicalFile(folder); } catch(IOException ignored) {} if(!isFolderShareable(folder, false)) return false; _data.DIRECTORIES_NOT_TO_SHARE.remove(folder); if (!isFolderShared(folder.getParentFile())) SharingSettings.DIRECTORIES_TO_SHARE.add(folder); _isUpdating = true; updateSharedDirectories(folder, null, _revision); _isUpdating = false; return true; } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#addFileAlways(java.io.File) */ public void addFileAlways(File file) { addFileAlways(file, EMPTY_DOCUMENTS, null); } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#addFileAlways(java.io.File, com.limegroup.gnutella.FileEventListener) */ public void addFileAlways(File file, FileEventListener callback) { addFileAlways(file, EMPTY_DOCUMENTS, callback); } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#addFileAlways(java.io.File, java.util.List) */ public void addFileAlways(File file, List<? extends LimeXMLDocument> list) { addFileAlways(file, list, null); } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#addFileAlways(java.io.File, java.util.List, com.limegroup.gnutella.FileEventListener) */ public void addFileAlways(File file, List<? extends LimeXMLDocument> list, FileEventListener callback) { _data.FILES_NOT_TO_SHARE.remove(file); if (!isFileShareable(file)) _data.SPECIAL_FILES_TO_SHARE.add(file); addFileIfSharedOrStore(file, list, true, _revision, callback, AddType.ADD_SHARE); } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#addFileForSession(java.io.File) */ public void addFileForSession(File file) { addFileForSession(file, null); } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#addFileForSession(java.io.File, com.limegroup.gnutella.FileEventListener) */ public void addFileForSession(File file, FileEventListener callback) { _data.FILES_NOT_TO_SHARE.remove(file); if (!isFileShareable(file)) _transientSharedFiles.add(file); addFileIfSharedOrStore(file, EMPTY_DOCUMENTS, true, _revision, callback, AddType.ADD_SHARE); } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#addFileIfShared(java.io.File) */ public void addFileIfShared(File file) { addFileIfSharedOrStore(file, EMPTY_DOCUMENTS, true, _revision, null, AddType.ADD_SHARE); } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#addFileIfShared(java.io.File, com.limegroup.gnutella.FileEventListener) */ public void addFileIfShared(File file, FileEventListener callback) { addFileIfSharedOrStore(file, EMPTY_DOCUMENTS, true, _revision, callback, AddType.ADD_SHARE); } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#addFileIfShared(java.io.File, java.util.List) */ public void addFileIfShared(File file, List<? extends LimeXMLDocument> list) { addFileIfSharedOrStore(file, list, true, _revision, null, AddType.ADD_SHARE); } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#addFileIfShared(java.io.File, java.util.List, com.limegroup.gnutella.FileEventListener) */ public void addFileIfShared(File file, List<? extends LimeXMLDocument> list, FileEventListener callback) { addFileIfSharedOrStore(file, list, true, _revision, callback, AddType.ADD_SHARE); } /** * Adds a file that is either associated with the store or is shared. Tries to create * a FileDescriptor for the file and ew. Files are handled differently depending on their AddType. * * * @param file - the file to be added * @param metadata - any LimeXMLDocs associated with this file * @param notify - if true signals the front-end via * ActivityCallback.handleFileManagerEvent() about the Event * @param revision - current version of LimeXMLDocs being used * @param callback - the listener to notify about the event * @param addType - type of add that for this file */ protected void addFileIfSharedOrStore(File file, List<? extends LimeXMLDocument> metadata, boolean notify, int revision, FileEventListener callback, AddType addFileType) { if(LOG.isDebugEnabled()) LOG.debug("Attempting to load store or shared file: " + file); if(callback == null) callback = EMPTY_CALLBACK; // Make sure capitals are resolved properly, etc. try { file = FileUtils.getCanonicalFile(file); } catch (IOException e) { callback.handleFileEvent(new FileManagerEvent(this, addFileType.getFailureType(), file)); return; } // if file is not shareable, also remove it from special files // to share since in that case it's not physically shareable then if (!isFileShareable(file) && !isFileLocatedStoreDirectory(file)) { _individualSharedFiles.remove(file); callback.handleFileEvent(new FileManagerEvent(FileManagerImpl.this, addFileType.getFailureType(), file)); return; } if( isStoreFile(file)){ return; } if(isFileShared(file)) { callback.handleFileEvent(new FileManagerEvent(FileManagerImpl.this, Type.ALREADY_SHARED_FILE, file)); return; } synchronized(this) { if (revision != _revision) { callback.handleFileEvent(new FileManagerEvent(this, addFileType.getFailureType(), file)); return; } _numPendingFiles++; // make sure _pendingFinished does not hold _revision // while we're still adding files _pendingFinished = -1; } fileManagerController.calculateAndCacheUrns(file, getNewUrnCallback(file, metadata, notify, revision, callback, addFileType)); } /** * Constructs a new UrnCallback that will possibly load the file with the given URNs. */ protected UrnCallback getNewUrnCallback(final File file, final List<? extends LimeXMLDocument> metadata, final boolean notify, final int revision, final FileEventListener callback, final AddType addFileType) { return new UrnCallback() { public void urnsCalculated(File f, Set<? extends URN> urns) { FileDesc fd = null; synchronized(FileManagerImpl.this) { if(revision != _revision) { LOG.warn("Revisions changed, dropping share."); callback.handleFileEvent(new FileManagerEvent(FileManagerImpl.this, addFileType.getFailureType(), file)); return; } _numPendingFiles--; // Only load the file if we were able to calculate URNs // assume the fd is being shared if(!urns.isEmpty()) { int fileIndex = _files.size(); fd = createFileDesc(file, urns, fileIndex); } } if(fd == null) { // If URNs was empty, or loading failed, notify... callback.handleFileEvent(new FileManagerEvent(FileManagerImpl.this, addFileType.getFailureType(), file)); return; } // try loading the fd so we can check the LimeXML info loadFile(fd, file, metadata, urns); // check LimeXML to determine if is a store file if (isStoreXML(fd.getXMLDocument())) { addStoreFile(fd, file, urns, addFileType, notify, callback); } else if (addFileType == AddType.ADD_SHARE) { addSharedFile(file, fd, urns, addFileType, notify, callback); } boolean finished = false; synchronized (this) { if(_numPendingFiles == 0) { _pendingFinished = revision; finished = true; } } if (finished) { tryToFinish(); } } public boolean isOwner(Object o) { return o == fileManagerController; } }; } /** * Loads a single shared file. */ protected void loadFile(FileDesc fd, File file, List<? extends LimeXMLDocument> metadata, Set<? extends URN> urns) { } /** * Creates a file descriptor for the given file and places the fd into the set * of LWS file descriptors */ private synchronized void addStoreFile(FileDesc fd, File file, Set<? extends URN> urns, AddType addFileType, final boolean notify, final FileEventListener callback) { if(LOG.isDebugEnabled()) LOG.debug("Sharing file: " + file); // if this file is in a shared folder, add to individual store files if( addFileType == AddType.ADD_SHARE) _data.SPECIAL_STORE_FILES.add(file); //store files are not part of the _files list so recreate fd with invalid index in _files FileDesc fileDesc = createFileDesc(file, urns, STORE_FILEDESC_INDEX); //add the xml doc to the new FileDesc if( fd.getXMLDocument() != null ) fileDesc.addLimeXMLDocument(fd.getXMLDocument()); _storeToFileDescMap.put(file, fileDesc); FileManagerEvent evt = new FileManagerEvent(FileManagerImpl.this, Type.ADD_STORE_FILE, fileDesc); if(notify) // sometimes notify the GUI dispatchFileEvent(evt); callback.handleFileEvent(evt); // always notify the individual callback. } /** * Handles the actual sharing of a file by placing the file descriptor into the set of shared files */ private synchronized void addSharedFile(File file, FileDesc fileDesc, Set<? extends URN> urns, AddType addFileType, final boolean notify, final FileEventListener callback) { if(LOG.isDebugEnabled()) LOG.debug("Sharing file: " + file); // since we created the FD to test the XML for being an instance of LWS, check to make sure // the FD is still valid before continuing if( fileDesc.getIndex() != _files.size()) { LimeXMLDocument doc = fileDesc.getXMLDocument(); fileDesc = createFileDesc(file, urns, _files.size()); if( doc != null ) fileDesc.addLimeXMLDocument(doc); } long fileLength = file.length(); _filesSize += fileLength; _files.add(fileDesc); _fileToFileDescMap.put(file, fileDesc); _numFiles++; //Register this file with its parent directory. File parent = file.getParentFile(); assert parent != null : "Null parent to \""+file+"\""; // files that are forcibly shared over the network // aren't counted or shown. if(SharingUtils.isForcedShareDirectory(parent)) _numForcedFiles++; loadKeywords(_keywordTrie, fileDesc); // Commit the time in the CreactionTimeCache, but don't share // the installer. We populate free LimeWire's with free installers // so we have to make sure we don't influence the what is new // result set. if (!SharingUtils.isForcedShare(file)) { fileManagerController.fileAdded(file, fileDesc.getSHA1Urn()); } // Ensure file can be found by URN lookups this.updateUrnIndex(fileDesc); _needRebuild = true; FileManagerEvent evt = new FileManagerEvent(FileManagerImpl.this, addFileType.getSuccessType(), fileDesc); if(notify) // sometimes notify the GUI dispatchFileEvent(evt); callback.handleFileEvent(evt); // always notify the individual callback. } /** * @param trie to update * @param fd to load keywords from */ private void loadKeywords(StringTrie<IntSet> trie, FileDesc fd) { // Index the filename. For each keyword... String[] keywords = extractKeywords(fd); for (int i = 0; i < keywords.length; i++) { String keyword = keywords[i]; //Ensure the _keywordTrie has a set of indices associated with keyword. IntSet indices = trie.get(keyword); if (indices == null) { indices = new IntSet(); trie.add(keyword, indices); } //Add fileIndex to the set. indices.add(fd.getIndex()); } } /** * Creates a file descriptor for a given file and a set of urns * @param file - file to create descriptor for * @param urns - urns to use * @param index - index to use * @return */ private FileDesc createFileDesc(File file, Set<? extends URN> urns, int index){ FileDesc fileDesc = new FileDesc(file, urns, index); ContentResponseData r = fileManagerController.getResponseDataFor(fileDesc.getSHA1Urn()); // if we had a response & it wasn't good, don't add this FD. if(r != null && !r.isOK()) return null; else return fileDesc; } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#stopSharingFile(java.io.File) */ public synchronized FileDesc stopSharingFile(File file) { try { file = FileUtils.getCanonicalFile(file); } catch (IOException e) { return null; } // remove file already here to heed against race conditions // wrt to filemanager events being handled on other threads boolean removed = _individualSharedFiles.remove(file); FileDesc fd = removeFileIfShared(file); if (fd == null) { fileManagerController.clearPendingShare(file); } else { file = fd.getFile(); // if file was not specially shared, add it to files_not_to_share if (!removed) _data.FILES_NOT_TO_SHARE.add(file); } return fd; } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#removeFileIfShared(java.io.File) */ public synchronized FileDesc removeFileIfShared(File f) { return removeFileIfShared(f, true); } /** * The actual implementation of removeFileIfShared(File) */ protected synchronized FileDesc removeFileIfShared(File f, boolean notify) { //Take care of case, etc. try { f = FileUtils.getCanonicalFile(f); } catch (IOException e) { return null; } // Look for matching file ... FileDesc fd = _fileToFileDescMap.get(f); if (fd == null) return null; int i = fd.getIndex(); assert _files.get(i).getFile().equals(f) : "invariant broken!"; _files.set(i, null); _fileToFileDescMap.remove(f); _needRebuild = true; // If it's an incomplete file, the only reference we // have is the URN, so remove that and be done. // We also return false, because the file was never really // "shared" to begin with. if (fd instanceof IncompleteFileDesc) { removeUrnIndex(fd, false); removeKeywords(_incompleteKeywordTrie, fd); _numIncompleteFiles--; boolean removed = _incompletesShared.remove(i); assert removed : "File "+i+" not found in " + _incompletesShared; // Notify the GUI... if (notify) { FileManagerEvent evt = new FileManagerEvent(this, Type.REMOVE_FILE, fd); dispatchFileEvent(evt); } return fd; } _numFiles--; _filesSize -= fd.getFileSize(); File parent = f.getParentFile(); // files that are forcibly shared over the network aren't counted if(SharingUtils.isForcedShareDirectory(parent)) { notify = false; _numForcedFiles--; } removeKeywords(_keywordTrie, fd); //Remove hash information. removeUrnIndex(fd, true); // Notify the GUI... if (notify) { FileManagerEvent evt = new FileManagerEvent(this, Type.REMOVE_FILE, fd); dispatchFileEvent(evt); } return fd; } private void removeKeywords(StringTrie<IntSet> trie, FileDesc fd) { //Remove references to this from index. String[] keywords = extractKeywords(fd); for (int j = 0; j < keywords.length; j++) { String keyword = keywords[j]; IntSet indices = trie.get(keyword); if (indices != null) { indices.remove(fd.getIndex()); if (indices.size() == 0) trie.remove(keyword); } } } protected synchronized FileDesc removeStoreFile(File f, boolean notify){ //Take care of case, etc. try { f = FileUtils.getCanonicalFile(f); } catch (IOException e) { return null; } // Look for matching file ... FileDesc fd = _storeToFileDescMap.get(f); if (fd == null) return null; _data.SPECIAL_STORE_FILES.remove(f); _storeToFileDescMap.remove(f); // Notify the GUI... if (notify) { FileManagerEvent evt = new FileManagerEvent(this, Type.REMOVE_STORE_FILE, fd); dispatchFileEvent(evt); } return fd; } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#addIncompleteFile(java.io.File, java.util.Set, java.lang.String, long, com.limegroup.gnutella.downloader.VerifyingFile) */ public synchronized void addIncompleteFile(File incompleteFile, Set<? extends URN> urns, String name, long size, VerifyingFile vf) { try { incompleteFile = FileUtils.getCanonicalFile(incompleteFile); } catch(IOException ioe) { //invalid file?... don't add incomplete file. return; } // We want to ensure that incomplete files are never added twice. // This may happen if IncompleteFileManager is deserialized before // FileManager finishes loading ... // So, every time an incomplete file is added, we check to see if // it already was... and if so, ignore it. // This is somewhat expensive, but it is called very rarely, so it's ok for(URN urn : urns) { if (!urn.isSHA1()) continue; // if there were indices for this URN, exit. IntSet shared = _urnMap.get(urn); // nothing was shared for this URN, look at another if (shared == null) continue; for (IntSet.IntSetIterator isIter = shared.iterator(); isIter.hasNext(); ) { int i = isIter.next(); FileDesc desc = _files.get(i); // unshared, keep looking. if (desc == null) continue; String incPath = incompleteFile.getAbsolutePath(); String path = desc.getFile().getAbsolutePath(); // the files are the same, exit. if (incPath.equals(path)) return; } } // no indices were found for any URN associated with this // IncompleteFileDesc... add it. int fileIndex = _files.size(); _incompletesShared.add(fileIndex); IncompleteFileDesc ifd = new IncompleteFileDesc( incompleteFile, urns, fileIndex, name, size, vf); _files.add(ifd); _fileToFileDescMap.put(incompleteFile, ifd); fileURNSUpdated(ifd); _numIncompleteFiles++; _needRebuild = true; dispatchFileEvent(new FileManagerEvent(this, Type.ADD_FILE, ifd)); } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#fileChanged(java.io.File) */ public abstract void fileChanged(File f); /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#validate(com.limegroup.gnutella.FileDesc) */ public void validate(final FileDesc fd) { if(_requestingValidation.add(fd.getSHA1Urn())) { fileManagerController.requestValidation(fd.getSHA1Urn(), new ContentResponseObserver() { public void handleResponse(URN urn, ContentResponseData r) { _requestingValidation.remove(fd.getSHA1Urn()); if(r != null && !r.isOK()) removeFileIfShared(fd.getFile()); } }); } } /////////////////////////////////////////////////////////////////////////// // Search, utility, etc... /////////////////////////////////////////////////////////////////////////// public synchronized void fileURNSUpdated(FileDesc fd) { updateUrnIndex(fd); if (fd instanceof IncompleteFileDesc) { IncompleteFileDesc ifd = (IncompleteFileDesc) fd; if (SharingSettings.ALLOW_PARTIAL_SHARING.getValue() && SharingSettings.LOAD_PARTIAL_KEYWORDS.getValue() && ifd.hasUrnsAndPartialData()) { loadKeywords(_incompleteKeywordTrie, fd); _needRebuild = true; } } } /** * @modifies this * @effects enters the given FileDesc into the _urnMap under all its * reported URNs */ private synchronized void updateUrnIndex(FileDesc fileDesc) { for(URN urn : fileDesc.getUrns()) { if (!urn.isSHA1()) continue; IntSet indices=_urnMap.get(urn); if (indices==null) { indices=new IntSet(); _urnMap.put(urn, indices); } indices.add(fileDesc.getIndex()); } } /** * Utility method to perform standardized keyword extraction for the given * <tt>FileDesc</tt>. This handles extracting keywords according to * locale-specific rules. * * @param fd the <tt>FileDesc</tt> containing a file system path with * keywords to extact * @return an array of keyword strings for the given file */ private static String[] extractKeywords(FileDesc fd) { return StringUtils.split(I18NConvert.instance().getNorm(fd.getPath()), DELIMITERS); } /** * Removes any URN index information for desc * @param purgeState true if any state should also be removed (creation time, altlocs) */ private synchronized void removeUrnIndex(FileDesc fileDesc, boolean purgeState) { for(URN urn : fileDesc.getUrns()) { if (!urn.isSHA1()) continue; //Lookup each of desc's URN's ind _urnMap. //(It better be there!) IntSet indices=_urnMap.get(urn); if (indices == null) { assert fileDesc instanceof IncompleteFileDesc; return; } //Delete index from set. Remove set if empty. indices.remove(fileDesc.getIndex()); if (indices.size()==0 && purgeState) { fileManagerController.lastUrnRemoved(urn); _urnMap.remove(urn); } } } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#renameFileIfSharedOrStore(java.io.File, java.io.File) */ public void renameFileIfSharedOrStore(File oldName, File newName) { renameFileIfSharedOrStore(oldName, newName, null); } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#renameFileIfSharedOrStore(java.io.File, java.io.File, com.limegroup.gnutella.FileEventListener) */ public synchronized void renameFileIfSharedOrStore(final File oldName, final File newName, final FileEventListener callback) { FileDesc toRemove = getFileDescForFile(oldName); if (toRemove == null ) { FileManagerEvent evt = new FileManagerEvent(this, Type.ADD_FAILED_FILE, oldName); dispatchFileEvent(evt); if(callback != null) callback.handleFileEvent(evt); return; } if(LOG.isDebugEnabled()) LOG.debug("Attempting to rename: " + oldName + " to: " + newName); List<LimeXMLDocument> xmlDocs = new LinkedList<LimeXMLDocument>(toRemove.getLimeXMLDocuments()); // if its a shared file, store files all have the same index in their filedescriptor if( toRemove.getIndex() != STORE_FILEDESC_INDEX ) { final FileDesc removed = removeFileIfShared(oldName, false); assert removed == toRemove : "invariant broken."; if (_data.SPECIAL_FILES_TO_SHARE.remove(oldName) && !isFileInCompletelySharedDirectory(newName)) _data.SPECIAL_FILES_TO_SHARE.add(newName); // Prepopulate the cache with new URNs. fileManagerController.addUrns(newName, removed.getUrns()); addFileIfSharedOrStore( newName, xmlDocs, false, _revision, new FileEventListener(){ public void handleFileEvent(FileManagerEvent evt) { if(LOG.isDebugEnabled()) LOG.debug("Add of newFile returned callback: " + evt); // Retarget the event for the GUI. FileManagerEvent newEvt = null; if(evt.isAddEvent()) { FileDesc fd = evt.getFileDescs()[0]; newEvt = new FileManagerEvent(FileManagerImpl.this, Type.RENAME_FILE, removed, fd); } else { newEvt = new FileManagerEvent(FileManagerImpl.this, Type.REMOVE_FILE, removed); } dispatchFileEvent(newEvt); if(callback != null) callback.handleFileEvent(newEvt); } }, AddType.ADD_SHARE); } // its a store files else { final FileDesc removed = removeStoreFile(oldName, false); assert removed == toRemove : "invariant broken."; if (_data.SPECIAL_STORE_FILES.remove(oldName)) _data.SPECIAL_STORE_FILES.add(newName); // Prepopulate the cache with new URNs. fileManagerController.addUrns(newName, removed.getUrns()); addFileIfSharedOrStore(newName, xmlDocs, false, _revision, new FileEventListener() { public void handleFileEvent(FileManagerEvent evt) { FileManagerEvent newEvt = null; if(evt.isAddStoreEvent()) { FileDesc fd = evt.getFileDescs()[0]; newEvt = new FileManagerEvent(FileManagerImpl.this, Type.RENAME_FILE, removed, fd); } else { newEvt = new FileManagerEvent(FileManagerImpl.this, Type.REMOVE_STORE_FILE, removed); } dispatchFileEvent(newEvt); if(callback != null) callback.handleFileEvent(newEvt); } }, AddType.ADD_STORE ); } } /** Ensures that this's index takes the minimum amount of space. Only * affects performance, not correctness; hence no modifies clause. */ private synchronized void trim() { _keywordTrie.trim(new Function<IntSet, IntSet>() { public IntSet apply(IntSet intSet) { intSet.trim(); return intSet; } }); } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#validateSensitiveFile(java.io.File) */ public void validateSensitiveFile(File dir) { _data.SENSITIVE_DIRECTORIES_VALIDATED.add(dir); _data.SENSITIVE_DIRECTORIES_NOT_TO_SHARE.remove(dir); } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#invalidateSensitiveFile(java.io.File) */ public void invalidateSensitiveFile(File dir) { _data.SENSITIVE_DIRECTORIES_VALIDATED.remove(dir); _data.SENSITIVE_DIRECTORIES_NOT_TO_SHARE.add(dir); SharingSettings.DIRECTORIES_TO_SHARE.remove(dir); } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#hasIndividualFiles() */ public boolean hasIndividualFiles() { return !_data.SPECIAL_FILES_TO_SHARE.isEmpty(); } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#hasIndividualStoreFiles() */ public boolean hasIndividualStoreFiles() { return !_data.SPECIAL_STORE_FILES.isEmpty(); } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#hasApplicationSharedFiles() */ public boolean hasApplicationSharedFiles() { File [] files = SharingUtils.APPLICATION_SPECIAL_SHARE.listFiles(); if (files == null) return false; // if at least one of the files in the application special // share are currently shared, return true. for (File f: files) { if (isFileShared(f)) return true; } return false; } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#getIndividualFiles() */ public File[] getIndividualFiles() { Set<File> candidates = _data.SPECIAL_FILES_TO_SHARE; synchronized(candidates) { ArrayList<File> files = new ArrayList<File>(candidates.size()); for(File f : candidates) { if (f.exists()) files.add(f); } if (files.isEmpty()) return new File[0]; else return files.toArray(new File[files.size()]); } } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#getIndividualStoreFiles() */ public File[] getIndividualStoreFiles() { Set<File> candidates = _data.SPECIAL_STORE_FILES; synchronized (candidates) { ArrayList<File> files = new ArrayList<File>(candidates.size()); for(File f : candidates) { if (f.exists()) files.add(f); } if (files.isEmpty()) return new File[0]; else return files.toArray(new File[files.size()]); } } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#isIndividualStore(java.io.File) */ public boolean isIndividualStore(File f) { return _data.SPECIAL_STORE_FILES.contains(f); } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#isIndividualShare(java.io.File) */ public boolean isIndividualShare(File f) { return _data.SPECIAL_FILES_TO_SHARE.contains(f) && SharingUtils.isFilePhysicallyShareable(f) && !SharingUtils.isApplicationSpecialShare(f); } /** * Cleans all stale entries from the Set of individual files. */ private void cleanIndividualFiles() { Set<File> files = _data.SPECIAL_FILES_TO_SHARE; synchronized(files) { for(Iterator<File> i = files.iterator(); i.hasNext(); ) { File f = i.next(); if(!(SharingUtils.isFilePhysicallyShareable(f))) i.remove(); } } } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#isFileShared(java.io.File) */ public synchronized boolean isFileShared(File file) { if (file == null) return false; if (_fileToFileDescMap.get(file) == null) return false; return true; } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#isRareFile(com.limegroup.gnutella.FileDesc) */ public boolean isRareFile(FileDesc fd) { return rareDefinition.evaluate(fd); } /** Returns true if file has a shareable extension. Case is ignored. */ private static boolean hasShareableExtension(File file) { if(file == null) return false; String filename = file.getName(); int begin = filename.lastIndexOf("."); if (begin == -1) return false; String ext = filename.substring(begin + 1).toLowerCase(); return _extensions.contains(ext); } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#isFileInCompletelySharedDirectory(java.io.File) */ public boolean isFileInCompletelySharedDirectory(File f) { File dir = f.getParentFile(); if (dir == null) return false; synchronized (this) { return _completelySharedDirectories.contains(dir); } } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#isSharedDirectory(java.io.File) */ public boolean isFolderShared(File dir) { if (dir == null) return false; synchronized (this) { return _completelySharedDirectories.contains(dir); } } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#isStoreFile(java.io.File) */ public boolean isStoreFile(File file) { if( _storeToFileDescMap.containsKey(file) || _storeDirectories.contains(file)) { return true; } return false; } /** * Returns true if the given file is in a completely shared directory * or if it is specially shared. * NOTE: this does not determine if a file is unshareable as a result of * being a LWS file */ private boolean isFileShareable(File file) { if (!SharingUtils.isFilePhysicallyShareable(file)) return false; if (_individualSharedFiles.contains(file)) return true; if (_data.FILES_NOT_TO_SHARE.contains(file)) return false; if (isFileInCompletelySharedDirectory(file)) { if (file.getName().toUpperCase().startsWith("LIMEWIRE")) return true; if (!hasShareableExtension(file)) return false; return true; } return false; } private boolean isFileLocatedStoreDirectory(File file) { return ( _storeDirectories.contains(file.getParentFile())); } /** * Returns true if the XML doc contains information regarding the LWS */ private boolean isStoreXML(LimeXMLDocument doc) { return doc != null && doc.getLicenseString() != null && doc.getLicenseString().equals(LicenseType.LIMEWIRE_STORE_PURCHASE.name()); } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#isStoreDirectory(java.io.File) */ public boolean isStoreDirectory(File file) { return _storeDirectories.contains(file); } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#isFolderShareable(java.io.File, boolean) */ public boolean isFolderShareable(File folder, boolean includeExcludedDirectories) { if(!folder.isDirectory() || !folder.canRead()) return false; if (folder.equals(SharingSettings.INCOMPLETE_DIRECTORY.getValue())) return false; if (SharingUtils.isApplicationSpecialShareDirectory(folder)) { return false; } // Do not share directories on the do not share list if (includeExcludedDirectories && _data.DIRECTORIES_NOT_TO_SHARE.contains(folder)) return false; // check for system roots File[] faRoots = File.listRoots(); if (faRoots != null && faRoots.length > 0) { for (int i = 0; i < faRoots.length; i++) { if (folder.equals(faRoots[i])) return false; } } // Check for the folder name being 'Cookies', or 'Cookies\Low' [vista] // TODO: Make sure this is i18n-safe String name = folder.getName().toLowerCase(Locale.US); if(name.equals("cookies")) return false; else if(name.equals("low")) { String parent = folder.getParent(); if(parent != null && parent.toLowerCase(Locale.US).equals("cookies")) return false; } return true; } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#getQRT() */ public synchronized QueryRouteTable getQRT() { if(_needRebuild) { qrpUpdater.cancelRebuild(); buildQRT(); _needRebuild = false; } QueryRouteTable qrt = new QueryRouteTable(_queryRouteTable.getSize()); qrt.addAll(_queryRouteTable); return qrt; } /** * build the qrt. Subclasses can add other Strings to the * QRT by calling buildQRT and then adding directly to the * _queryRouteTable variable. (see xml/MetaFileManager.java) */ protected synchronized void buildQRT() { _queryRouteTable = new QueryRouteTable(); if (SearchSettings.PUBLISH_LIME_KEYWORDS.getBoolean()) { for (String entry : SearchSettings.LIME_QRP_ENTRIES.getValue()) _queryRouteTable.addIndivisible(entry); } FileDesc[] fds = getAllSharedFileDescriptors(); for(int i = 0; i < fds.length; i++) { if (fds[i] instanceof IncompleteFileDesc) { if (!SharingSettings.ALLOW_PARTIAL_SHARING.getValue()) continue; if (!SharingSettings.PUBLISH_PARTIAL_QRP.getValue()) continue; IncompleteFileDesc ifd = (IncompleteFileDesc)fds[i]; if (!ifd.hasUrnsAndPartialData()) continue; _queryRouteTable.add(ifd.getFileName()); } else _queryRouteTable.add(fds[i].getPath()); } } ////////////////////////////////// Queries /////////////////////////////// /** * Constant for an empty <tt>Response</tt> array to return when there are * no matches. */ private static final Response[] EMPTY_RESPONSES = new Response[0]; /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#query(com.limegroup.gnutella.messages.QueryRequest) */ public synchronized Response[] query(QueryRequest request) { String str = request.getQuery(); boolean includeXML = shouldIncludeXMLInResponse(request); //Special case: return up to 3 of your 'youngest' files. if (request.isWhatIsNewRequest()) return respondToWhatIsNewRequest(request, includeXML); //Special case: return everything for Clip2 indexing query (" ") and //browse queries ("*.*"). If these messages had initial TTLs too high, //StandardMessageRouter will clip the number of results sent on the //network. Note that some initial TTLs are filterd by GreedyQuery //before they ever reach this point. if (str.equals(INDEXING_QUERY) || str.equals(BROWSE_QUERY)) return respondToIndexingQuery(includeXML); //Normal case: query the index to find all matches. TODO: this //sometimes returns more results (>255) than we actually send out. //That's wasted work. //Trie requires that getPrefixedBy(String, int, int) passes //an already case-changed string. Both search & urnSearch //do this kind of match, so we canonicalize the case for them. str = _keywordTrie.canonicalCase(str); IntSet matches = search(str, null, request.desiresPartialResults()); if(request.getQueryUrns().size() > 0) matches = urnSearch(request.getQueryUrns(),matches); if (matches==null) return EMPTY_RESPONSES; List<Response> responses = new LinkedList<Response>(); final MediaType.Aggregator filter = MediaType.getAggregator(request); LimeXMLDocument doc = request.getRichQuery(); // Iterate through our hit indices to create a list of results. for (IntSet.IntSetIterator iter=matches.iterator(); iter.hasNext();) { int i = iter.next(); FileDesc desc = _files.get(i); assert desc != null : "unexpected null in FileManager for query:\n"+ request; if ((filter != null) && !filter.allow(desc.getFileName())) continue; desc.incrementHitCount(); fileManagerController.handleSharedFileUpdate(desc.getFile()); Response resp = fileManagerController.createResponse(desc); if(includeXML) { addXMLToResponse(resp, desc); if(doc != null && resp.getDocument() != null && !isValidXMLMatch(resp, doc)) continue; } responses.add(resp); } if (responses.size() == 0) return EMPTY_RESPONSES; return responses.toArray(new Response[responses.size()]); } /** * Responds to a what is new request. */ private Response[] respondToWhatIsNewRequest(QueryRequest request, boolean includeXML) { // see if there are any files to send.... // NOTE: we only request up to 3 urns. we don't need to worry // about partial files because we don't add them to the cache. List<URN> urnList = fileManagerController.getNewestUrns(request, 3); if (urnList.size() == 0) return EMPTY_RESPONSES; // get the appropriate responses Response[] resps = new Response[urnList.size()]; for (int i = 0; i < urnList.size(); i++) { URN currURN = urnList.get(i); FileDesc desc = getFileDescForUrn(currURN); // should never happen since we don't add times for IFDs and // we clear removed files... if ((desc==null) || (desc instanceof IncompleteFileDesc)) throw new RuntimeException("Bad Rep - No IFDs allowed!"); // Formulate the response Response r = fileManagerController.createResponse(desc); if(includeXML) addXMLToResponse(r, desc); // Cache it resps[i] = r; } return resps; } /** Responds to a Indexing (mostly BrowseHost) query - gets all the shared * files of this client. */ private Response[] respondToIndexingQuery(boolean includeXML) { //Special case: if no shared files, return null // This works even if incomplete files are shared, because // they are added to _numIncompleteFiles and not _numFiles. if (_numFiles==0) return EMPTY_RESPONSES; //Extract responses for all non-null (i.e., not deleted) files. //Because we ignore all incomplete files, _numFiles continues //to work as the expected size of ret. Response[] ret=new Response[_numFiles-_numForcedFiles]; int j=0; for (int i=0; i<_files.size(); i++) { FileDesc desc = _files.get(i); // If the file was unshared or is an incomplete file, // DO NOT SEND IT. if (desc==null || desc instanceof IncompleteFileDesc || SharingUtils.isForcedShare(desc)) continue; assert j<ret.length : "_numFiles is too small"; ret[j] = fileManagerController.createResponse(desc); if(includeXML) addXMLToResponse(ret[j], desc); j++; } assert j==ret.length : "_numFiles is too large"; return ret; } /** * A normal FileManager will never include XML. * It is expected that MetaFileManager overrides this and returns * true in some instances. */ protected abstract boolean shouldIncludeXMLInResponse(QueryRequest qr); /** * This implementation does nothing. */ protected abstract void addXMLToResponse(Response res, FileDesc desc); /** * Determines whether we should include the response based on XML. */ protected abstract boolean isValidXMLMatch(Response res, LimeXMLDocument doc); /** * Returns a set of indices of files matching q, or null if there are no * matches. Subclasses may override to provide different notions of * matching. The caller of this method must not mutate the returned * value. */ protected IntSet search(String query, IntSet priors, boolean partial) { //As an optimization, we lazily allocate all sets in case there are no //matches. TODO2: we can avoid allocating sets when getPrefixedBy //returns an iterator of one element and there is only one keyword. IntSet ret=priors; //For each keyword in the query.... (Note that we avoid calling //StringUtils.split and take advantage of Trie's offset/limit feature.) for (int i=0; i<query.length(); ) { if (isDelimiter(query.charAt(i))) { i++; continue; } int j; for (j=i+1; j<query.length(); j++) { if (isDelimiter(query.charAt(j))) break; } //Search for keyword, i.e., keywords[i...j-1]. Iterator<IntSet> iter= _keywordTrie.getPrefixedBy(query, i, j); if (SharingSettings.ALLOW_PARTIAL_SHARING.getValue() && SharingSettings.ALLOW_PARTIAL_RESPONSES.getValue() && partial) iter = new MultiIterator<IntSet>(iter,_incompleteKeywordTrie.getPrefixedBy(query, i, j)); if (iter.hasNext()) { //Got match. Union contents of the iterator and store in //matches. As an optimization, if this is the only keyword and //there is only one set returned, return that set without //copying. IntSet matches=null; while (iter.hasNext()) { IntSet s= iter.next(); if (matches==null) { if (i==0 && j==query.length() && !(iter.hasNext())) return s; matches=new IntSet(); } matches.addAll(s); } //Intersect matches with ret. If ret isn't allocated, //initialize to matches. if (ret==null) ret=matches; else ret.retainAll(matches); } else { //No match. Optimizaton: no matches for keyword => failure return null; } //Optimization: no matches after intersect => failure if (ret.size()==0) return null; i=j; } if (ret==null || ret.size()==0) return null; return ret; } /** * Find all files with matching full URNs */ private synchronized IntSet urnSearch(Iterable<URN> urnsIter, IntSet priors) { IntSet ret = priors; for(URN urn : urnsIter) { IntSet hits = _urnMap.get(urn); if(hits!=null) { // double-check hits to be defensive (not strictly needed) IntSet.IntSetIterator iter = hits.iterator(); while(iter.hasNext()) { FileDesc fd = _files.get(iter.next()); // If the file is unshared or an incomplete file // DO NOT SEND IT. if(fd == null || fd instanceof IncompleteFileDesc) continue; if(fd.containsUrn(urn)) { // still valid if(ret==null) ret = new IntSet(); ret.add(fd.getIndex()); } } } } return ret; } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#isFileApplicationShared(java.lang.String) */ public boolean isFileApplicationShared(String name) { File file = new File(SharingUtils.APPLICATION_SPECIAL_SHARE, name); try { file = FileUtils.getCanonicalFile(file); } catch (IOException bad) { return false; } return isFileShared(file); } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#addFileEventListener(com.limegroup.gnutella.FileEventListener) */ public void addFileEventListener(FileEventListener listener) { if (listener == null) { throw new NullPointerException("FileEventListener is null"); } eventListeners.addIfAbsent(listener); } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#removeFileEventListener(com.limegroup.gnutella.FileEventListener) */ public void removeFileEventListener(FileEventListener listener) { if (listener == null) { throw new NullPointerException("FileEventListener is null"); } eventListeners.remove(listener); } /** * dispatches a FileManagerEvent to any registered listeners */ protected void dispatchFileEvent(FileManagerEvent evt) { for(FileEventListener listener : eventListeners) { listener.handleFileEvent(evt); } } /* (non-Javadoc) * @see com.limegroup.gnutella.FileManager#getIndexingIterator(boolean) */ public Iterator<Response> getIndexingIterator(final boolean includeXML) { return new Iterator<Response>() { int startRevision = _revision; /** Points to the index that is to be examined next. */ int index = 0; Response preview; private boolean preview() { assert preview == null; if (_revision != startRevision) { return false; } synchronized (FileManagerImpl.this) { while (index < _files.size()) { FileDesc desc = _files.get(index); index++; // skip, if the file was unshared or is an incomplete file, if (desc == null || desc instanceof IncompleteFileDesc || SharingUtils.isForcedShare(desc)) continue; preview = fileManagerController.createResponse(desc); if(includeXML) addXMLToResponse(preview, desc); return true; } return false; } } public boolean hasNext() { if (_revision != startRevision) { return false; } if (preview != null) { synchronized (FileManagerImpl.this) { if (_files.get(index - 1) == null) { // file was removed in the meantime preview = null; } } } return preview != null || preview(); } public Response next() { if (hasNext()) { Response item = preview; preview = null; return item; } throw new NoSuchElementException(); } public void remove() { throw new UnsupportedOperationException(); } }; } private class QRPUpdater implements SimppListener { private boolean buildInProgress; private final Set<String> qrpWords = new HashSet<String>(); public QRPUpdater() { synchronized(this) { for (String entry : SearchSettings.LIME_QRP_ENTRIES.getValue()) qrpWords.add(entry); } } public synchronized void simppUpdated(int newVersion) { if (buildInProgress) return; Set<String> newWords = new HashSet<String>(); for (String entry : SearchSettings.LIME_QRP_ENTRIES.getValue()) newWords.add(entry); // any change in words? if (newWords.containsAll(qrpWords) && qrpWords.containsAll(newWords)) return; qrpWords.clear(); qrpWords.addAll(newWords); buildInProgress = true; // schedule a rebuild sometime in the next hour fileManagerController.scheduleWithFixedDelay(new Runnable() { public void run() { synchronized(QRPUpdater.this) { if (!buildInProgress) return; buildInProgress = false; _needRebuild = true; } } }, (int)(Math.random() * QRP_DELAY), 0, TimeUnit.MILLISECONDS); } public synchronized void cancelRebuild() { buildInProgress = false; } } /** A bunch of inspectables for FileManager */ @SuppressWarnings("unused") @InspectableContainer private class FMInspectables { /* * 1 - used to create smaller qrp table * 2 - just sends the current table */ private static final int VERSION = 2; private void addVersion(Map<String, Object> m) { m.put("ver", VERSION); } /** An inspectable that returns some info about the QRP */ @InspectionPoint("FileManager QRP info") public final Inspectable QRP = new Inspectable() { public Object inspect() { Map<String, Object> ret = new HashMap<String, Object>(); addVersion(ret); synchronized(FileManagerImpl.this) { ret.put("qrt",getQRT().getRawDump()); } return ret; } }; /** An inspectable that returns stats about hits, uploads & alts */ @InspectionPoint("FileManager h/u/a stats") public final Inspectable FDS = new FDInspectable(false); /** An inspectable that returns stats about hits, uploads & alts > 0 */ @InspectionPoint("FileManager h/u/a stats > 0") public final Inspectable FDSNZ = new FDInspectable(true); /** An inspectable that counts how many shared fds match a custom criteria */ @InspectionPoint("FileManager custom criteria") public final Inspectable CUSTOM = new Inspectable() { public Object inspect() { Map<String, Object> ret = new HashMap<String,Object>(); ret.put("ver",1); ret.put("crit", MessageSettings.CUSTOM_FD_CRITERIA.getValueAsString()); int total = 0; int matched = 0; try { RPNParser parser = new RPNParser(MessageSettings.CUSTOM_FD_CRITERIA.getValue()); synchronized(FileManagerImpl.this) { for (FileDesc fd : getAllSharedFileDescriptors()) { total++; if (parser.evaluate(fd)) matched++; } } } catch (IllegalArgumentException badSimpp) { ret.put("error",badSimpp.toString()); return ret; } ret.put("match",matched); ret.put("total",total); return ret; } }; } /** Inspectable with information about File Descriptors */ private class FDInspectable implements Inspectable { private final boolean nonZero; /** * @param nonZero whether to return only results greater than 0 */ FDInspectable(boolean nonZero) { this.nonZero = nonZero; } public Object inspect() { Map<String, Object> ret = new HashMap<String, Object>(); ret.put("ver", FMInspectables.VERSION); // the actual values ArrayList<Double> hits = new ArrayList<Double>(); ArrayList<Double> uploads = new ArrayList<Double>(); ArrayList<Double> completeUploads = new ArrayList<Double>(); ArrayList<Double> alts = new ArrayList<Double>(); ArrayList<Double> keywords = new ArrayList<Double>(); // differences for t-test ArrayList<Double> altsHits = new ArrayList<Double>(); ArrayList<Double> altsUploads = new ArrayList<Double>(); ArrayList<Double> hitsUpload = new ArrayList<Double>(); ArrayList<Double> hitsKeywords = new ArrayList<Double>(); ArrayList<Double> uploadsToComplete = new ArrayList<Double>(); Map<Integer, FileDesc> topHitsFDs = new TreeMap<Integer, FileDesc>(Comparators.inverseIntegerComparator()); Map<Integer, FileDesc> topUpsFDs = new TreeMap<Integer, FileDesc>(Comparators.inverseIntegerComparator()); Map<Integer, FileDesc> topAltsFDs = new TreeMap<Integer, FileDesc>(Comparators.inverseIntegerComparator()); Map<Integer, FileDesc> topCupsFDs = new TreeMap<Integer, FileDesc>(Comparators.inverseIntegerComparator()); synchronized(FileManagerImpl.this) { FileDesc[] fds = getAllSharedFileDescriptors(); hits.ensureCapacity(fds.length); uploads.ensureCapacity(fds.length); int rare = 0; int total = 0; for(int i = 0; i < fds.length; i++) { if (fds[i] instanceof IncompleteFileDesc) continue; total++; if (isRareFile(fds[i])) rare++; // locking FM->ALM ok. int numAlts = fileManagerController.getAlternateLocationCount(fds[i].getSHA1Urn()); if (!nonZero || numAlts > 0) { alts.add((double)numAlts); topAltsFDs.put(numAlts,fds[i]); } int hitCount = fds[i].getHitCount(); if (!nonZero || hitCount > 0) { hits.add((double)hitCount); topHitsFDs.put(hitCount, fds[i]); } int upCount = fds[i].getAttemptedUploads(); if (!nonZero || upCount > 0) { uploads.add((double)upCount); topUpsFDs.put(upCount, fds[i]); } int cupCount = fds[i].getCompletedUploads(); if (!nonZero || cupCount > 0) { completeUploads.add((double)upCount); topCupsFDs.put(cupCount, fds[i]); } // keywords per fd double keywordsCount = HashFunction.getPrefixes(HashFunction.keywords(fds[i].getPath())).length; keywords.add(keywordsCount); // populate differences if (!nonZero) { int index = hits.size() - 1; hitsUpload.add(hits.get(index) - uploads.get(index)); altsHits.add(alts.get(index) - hits.get(index)); altsUploads.add(alts.get(index) - uploads.get(index)); hitsKeywords.add(hits.get(index) - keywordsCount); uploadsToComplete.add(uploads.get(index) - completeUploads.get(index)); } } ret.put("rare",Double.doubleToLongBits((double)rare / total)); } ret.put("hits",StatsUtils.quickStatsDouble(hits).getMap()); ret.put("hitsh", StatsUtils.getHistogram(hits, 10)); // small, will compress ret.put("ups",StatsUtils.quickStatsDouble(uploads).getMap()); ret.put("upsh", StatsUtils.getHistogram(uploads, 10)); ret.put("cups",StatsUtils.quickStatsDouble(completeUploads).getMap()); ret.put("cupsh", StatsUtils.getHistogram(completeUploads, 10)); ret.put("alts", StatsUtils.quickStatsDouble(alts).getMap()); ret.put("altsh", StatsUtils.getHistogram(alts, 10)); ret.put("kw", StatsUtils.quickStatsDouble(keywords).getMap()); ret.put("kwh", StatsUtils.getHistogram(keywords, 10)); // t-test values ret.put("hut",StatsUtils.quickStatsDouble(hitsUpload).getTTestMap()); ret.put("aht",StatsUtils.quickStatsDouble(altsHits).getTTestMap()); ret.put("aut",StatsUtils.quickStatsDouble(altsUploads).getTTestMap()); ret.put("hkt",StatsUtils.quickStatsDouble(hitsKeywords).getTTestMap()); ret.put("ucut",StatsUtils.quickStatsDouble(uploadsToComplete).getTTestMap()); QueryRouteTable topHits = new QueryRouteTable(); QueryRouteTable topUps = new QueryRouteTable(); QueryRouteTable topCups = new QueryRouteTable(); QueryRouteTable topAlts = new QueryRouteTable(); Iterator<FileDesc> hitIter = topHitsFDs.values().iterator(); Iterator<FileDesc> upIter = topUpsFDs.values().iterator(); Iterator<FileDesc> cupIter = topCupsFDs.values().iterator(); Iterator<FileDesc> altIter = topAltsFDs.values().iterator(); for (int i = 0; i < 10; i++) { if (hitIter.hasNext()) topHits.add(hitIter.next().getPath()); if (upIter.hasNext()) topUps.add(upIter.next().getPath()); if (altIter.hasNext()) topAlts.add(altIter.next().getPath()); if (cupIter.hasNext()) topCups.add(cupIter.next().getPath()); } // we return all qrps, but since they will have very few entries // they will compress very well ret.put("hitsq",topHits.getRawDump()); ret.put("upsq",topUps.getRawDump()); ret.put("cupsq",topCups.getRawDump()); ret.put("altsq",topAlts.getRawDump()); return ret; } } private class RareFileDefinition implements SimppListener { private RPNParser parser; RareFileDefinition() { simppUpdated(0); // TODO cleanup listener leaking fileManagerController.addSimppListener(this); } public synchronized void simppUpdated(int ignored) { parser = new RPNParser(DHTSettings.RARE_FILE_DEFINITION.getValue()); } private synchronized boolean evaluate(FileDesc fd) { try { return parser.evaluate(fd); } catch (IllegalArgumentException badSimpp) { return false; } } } }

The table below shows all metrics for FileManagerImpl.java.

MetricValueDescription
BLOCKS312.00Number of blocks
BLOCK_COMMENT217.00Number of block comment lines
COMMENTS816.00Comment lines
COMMENT_DENSITY 0.59Comment density
COMPARISONS272.00Number of comparison operators
CYCLOMATIC440.00Cyclomatic complexity
DECL_COMMENTS154.00Comments in declarations
DOC_COMMENT365.00Number of javadoc comment lines
ELOC1374.00Effective lines of code
EXEC_COMMENTS134.00Comments in executable code
EXITS278.00Procedure exits
FUNCTIONS140.00Number of function declarations
HALSTEAD_DIFFICULTY131.52Halstead difficulty
HALSTEAD_EFFORT 0.00Halstead effort
INTERFACE_COMPLEXITY412.00Interface complexity
JAVA0001 0.00JAVA0001 Package name does not contain only lower case letters
JAVA0002 0.00JAVA0002 Package name does not begin with a top level domain name or country code
JAVA0003 0.00JAVA0003 Minimize use of on-demand (.*) imports
JAVA0004 0.00JAVA0004 Unnecessary import from java.lang
JAVA0005 0.00JAVA0005 Imports not in specified order
JAVA0006 0.00JAVA0006 Empty finally block
JAVA0007 0.00JAVA0007 Should not declare public field
JAVA0008 2.00JAVA0008 Empty catch block
JAVA0009 0.00JAVA0009 Protected member in final class
JAVA0010 0.00JAVA0010 Non-instantiable class does not contain a non-private static member
JAVA0011 0.00JAVA0011 Abstract class does not contain an abstract method
JAVA0012 0.00JAVA0012 Non-constructor method with same name as declaring class
JAVA0013 0.00JAVA0013 Non-blank final field is not static
JAVA0014 0.00JAVA0014 Class with only static members has non-private constructor
JAVA0015 0.00JAVA0015 Package class contains public nested type
JAVA0016 1.00JAVA0016 Abstract class contains public constructor
JAVA0017 0.00JAVA0017 Class name does not have required form
JAVA0018 0.00JAVA0018 Method name does not have required form
JAVA0019 0.00JAVA0019 Interface name does not have required form
JAVA002022.00JAVA0020 Field name does not have required form
JAVA0021 0.00JAVA0021 Interface method name does not have required form
JAVA0022 0.00JAVA0022 Static final field name does not have required form
JAVA0023 0.00JAVA0023 Empty finalize method
JAVA0024 0.00JAVA0024 Empty class
JAVA0025 0.00JAVA0025 Method override is empty
JAVA0026 0.00JAVA0026 Finalize method with parameters
JAVA0029 0.00JAVA0029 Private method not used
JAVA0030 0.00JAVA0030 Private field not used
JAVA0031 0.00JAVA0031 Case statement not properly closed
JAVA0032 0.00JAVA0032 Switch statement missing default
JAVA0033 0.00JAVA0033 default: not last case in switch statement
JAVA0034133.00JAVA0034 Missing braces in if statement
JAVA0035 5.00JAVA0035 Missing braces in for statement
JAVA0036 0.00JAVA0036 Missing braces in while statement
JAVA0038 0.00JAVA0038 Non-case label in switch statement
JAVA0039 0.00JAVA0039 Break statement with label
JAVA0040 0.00JAVA0040 Switch statement contains N cases (maximum: M)
JAVA0041 0.00JAVA0041 Nested synchronized block
JAVA0042 0.00JAVA0042 Empty synchronized statement
JAVA0043 0.00JAVA0043 Inner class does not use outer class
JAVA0044 0.00JAVA0044 Serializable class with no instance variables
JAVA0045 0.00JAVA0045 Serializable class with only transient fields
JAVA0046 0.00JAVA0046 Name of class not derived from Exception ends with 'Exception'
JAVA0047 0.00JAVA0047 Serializable class derives from invalid base class
JAVA0048 0.00JAVA0048 Name of class derived from Exception does not end with 'Exception'
JAVA0049 0.00JAVA0049 Nested block at depth N (maximum: M)
JAVA0050 0.00JAVA0050 Class derives from java.lang.Error
JAVA0051 0.00JAVA0051 Class derives from java.lang.RuntimeException
JAVA0052 0.00JAVA0052 Class derives from java.lang.Throwable
JAVA0053 0.00JAVA0053 Unused label
JAVA0054 0.00JAVA0054 Inheritance depth N exceeds maximum M
JAVA0055 0.00JAVA0055 Class should be interface
JAVA0056 0.00JAVA0056 Unnecessary abstract modifier for interface or annotation
JAVA0057 0.00JAVA0057 Unnecessary default constructor
JAVA0058 0.00JAVA0058 Constructor calls super()
JAVA0059 0.00JAVA0059 Method override only calls super()
JAVA0061 3.00JAVA0061 Inaccessible member in anonymous class
JAVA0062 0.00JAVA0062 Public class missing public member or protected constructor
JAVA0063 0.00JAVA0063 Identifier name should not contain '$'
JAVA0064 0.00JAVA0064 N variations of identifier name (maximum: M)
JAVA0065 0.00JAVA0065 Unnecessary final modifier for method in final class
JAVA0066 0.00JAVA0066 Unnecessary modifier for interface nested type
JAVA0067 0.00JAVA0067 Array descriptor on identifier name
JAVA0068 0.00JAVA0068 Modifiers not declared in recommended order
JAVA0071 0.00JAVA0071 Strings compared with ==
JAVA0073 0.00JAVA0073 Integer division in floating-point context
JAVA0074 0.00JAVA0074 Use of Object.notify()
JAVA0075 0.00JAVA0075 Method parameter hides field
JAVA0076 8.00JAVA0076 Use of magic number
JAVA0077 1.00JAVA0077 Private field not used in declaring class
JAVA0078 0.00JAVA0078 Floating point values compared with ==
JAVA0079 0.00JAVA0079 Use of instance to reference static member
JAVA0080 0.00JAVA0080 Import declaration not used
JAVA0081 0.00JAVA0081 Boolean literal in comparison
JAVA0082 0.00JAVA0082 Unnecessary widening cast
JAVA0083 0.00JAVA0083 Unnecessary instanceof test
JAVA0084 0.00JAVA0084 Should use compound assignment operator
JAVA0085 0.00JAVA0085 Use of sun.* class
JAVA0087 0.00JAVA0087 Use of Thread.sleep()
JAVA0089 0.00JAVA0089 Use of restricted package
JAVA0092 0.00JAVA0092 Use of restricted type
JAVA0093 0.00JAVA0093 Redundant assignment
JAVA0094 0.00JAVA0094 Field hides a superclass field
JAVA0095 0.00JAVA0095 Uninitialized private field
JAVA0096 0.00JAVA0096 Field in nested class hides outer field
JAVA0098 0.00JAVA0098 Minimize use of implicit field initializers
JAVA0100 1.00JAVA0100 Class contains N non-final fields (maximum: M)
JAVA0101 0.00JAVA0101 Unnecessary modifier for field in interface
JAVA0102 0.00JAVA0102 Last statement in finalize() not super.finalize()
JAVA0103 0.00JAVA0103 Explicit call to finalize()
JAVA0104 0.00JAVA0104 finalize() only calls super.finalize()
JAVA0105 0.00JAVA0105 Duplicate import declaration
JAVA0106 0.00JAVA0106 Unnecessary import from current package
JAVA010859.00JAVA0108 Incorrect javadoc: no @param tag for 'parameter'
JAVA0109 1.00JAVA0109 Incorrect javadoc: no parameter 'parameter'
JAVA011013.00JAVA0110 Incorrect javadoc: no @return tag
JAVA0111 0.00JAVA0111 Incorrect javadoc: @return tag for void method
JAVA0112 0.00JAVA0112 Incorrect javadoc: no exception 'exception' in throws
JAVA0113 1.00JAVA0113 Incorrect javadoc: no @author tag
JAVA0114 1.00JAVA0114 Incorrect javadoc: no @version tag
JAVA0115 0.00JAVA0115 Incorrect javadoc: no @throws or @exception tag for 'exception'
JAVA0116 0.00JAVA0116 Missing javadoc: field 'field'
JAVA0117 6.00JAVA0117 Missing javadoc: method 'method'
JAVA0118 0.00JAVA0118 Missing javadoc: type 'type'
JAVA0119 2.00JAVA0119 Control variable changed within body of for loop
JAVA0123 3.00JAVA0123 Use all three components of for loop
JAVA0125 0.00JAVA0125 Continue statement with label
JAVA0126 0.00JAVA0126 Method declares unchecked exception in throws
JAVA0128 1.00JAVA0128 Public constructor in non-public class
JAVA0130 3.00JAVA0130 Non-static method does not use instance fields
JAVA0131 0.00JAVA0131 Compatible method does not override base
JAVA0132 0.00JAVA0132 Method overload with compatible signature
JAVA0133 0.00JAVA0133 Non-synchronized method overrides synchronized method
JAVA0135 0.00JAVA0135 Only one of Object.equals and Object.hashCode defined: missing 'method'
JAVA0136 1.00JAVA0136 N methods defined in class (maximum: M)
JAVA0137 0.00JAVA0137 Non-abstract class missing constructor
JAVA0138 4.00JAVA0138 N parameters defined for method (maximum: M)
JAVA0139 0.00JAVA0139 Definition of main other than public static void main(java.lang.String[])
JAVA0141 0.00JAVA0141 Unnecessary modifier for method in interface
JAVA014330.00JAVA0143 Synchronized method
JAVA0144 4.00JAVA0144 Line exceeds maximum M characters
JAVA0145438.00JAVA0145 Tab character used in source file
JAVA0150 0.00JAVA0150 java.lang.Error (or subclass) thrown
JAVA0153 0.00JAVA0153 Inefficient conversion of integer to string
JAVA0159 0.00JAVA0159 Inefficient conversion of string to integer
JAVA0160 0.00JAVA0160 Method does not throw specified exception
JAVA0161 0.00JAVA0161 Conditional wait() not in loop
JAVA0163 0.00JAVA0163 Empty statement
JAVA0165 0.00JAVA0165 Conflicting return statement in finally block
JAVA0166 0.00JAVA0166 Generic exception caught
JAVA0167 0.00JAVA0167 ThreadDeath not rethrown
JAVA0169 0.00JAVA0169 Unnecessary catch block: exception 'exception'
JAVA0170 0.00JAVA0170 Caught exception not derived from java.lang.Exception
JAVA0171 0.00JAVA0171 Unused local variable
JAVA0173 5.00JAVA0173 Unused method parameter
JAVA0174 0.00JAVA0174 Assigned local variable never used
JAVA0175 0.00JAVA0175 Successive assignment to variable
JAVA0176 0.00JAVA0176 Local variable name does not have required form
JAVA0177 5.00JAVA0177 Variable declaration missing initializer
JAVA0179 0.00JAVA0179 Local variable hides visible field
JAVA0233 0.00JAVA0233 Definition of serialVersionUID other than 'private static final long serialVersionUID'
JAVA0234 0.00JAVA0234 Class is Serializable but does not define serialVersionUID
JAVA0235 0.00JAVA0235 Class defines serialVersionUID but does not implement Serializable
JAVA0236 0.00JAVA0236 Attempt to clone an object which does not implement Cloneable
JAVA0237 0.00JAVA0237 Class implements Cloneable but does not have public clone method
JAVA0238 0.00JAVA0238 Clone method does not call super.clone()
JAVA0239 0.00JAVA0239 Class declares 'readObject' or 'writeObject' but does not implement Serializable
JAVA0240 0.00JAVA0240 Serializable class which declares readObject or writeObject but not both
JAVA0241 0.00JAVA0241 'readObject' or 'writeObject' should be declared private in Serializable class
JAVA0242 0.00JAVA0242 Transient field in non-Serializable class
JAVA0243 0.00JAVA0243 'readResolve' or 'writeReplace' should be declared private or protected
JAVA0244 0.00JAVA0244 Field or method name in subclass differs only by case from inherited field or method
JAVA0245 0.00JAVA0245 JUnit TestCase with non-trivial constructor
JAVA0246 0.00JAVA0246 JUnit assertXXX statement missing message parameter
JAVA0247 0.00JAVA0247 JUnit 'setUp()' and 'tearDown()' should call super method
JAVA0248 0.00JAVA0248 JUnit method 'setUp' or 'tearDown' with incorrect signature
JAVA0249 0.00JAVA0249 JUnit TestCase 'suite()' should be declared static
JAVA0250 0.00JAVA0250 JUnit TestCase declares testXXX method with incorrect signature
JAVA0251 0.00JAVA0251 Use '%n' for line breaks in printf/format for platform independence
JAVA0252 0.00JAVA0252 'enum' is a Java 1.5 reserved word
JAVA0253 0.00JAVA0253 Not all enum constants consumed in switch statement
JAVA0254 0.00JAVA0254 Use enhanced for loop construct instead of Iterator
JAVA0255 0.00JAVA0255 Result of method invocation not used
JAVA0256 0.00JAVA0256 Assignment of external collection/array to field
JAVA0257 0.00JAVA0257 Use of 'Constant Interface' anti-pattern
JAVA0258 1.00JAVA0258 Implement Iterable for foreach compatibility
JAVA0259 2.00JAVA0259 Return of collection/array field
JAVA0260 0.00JAVA0260 Use 'enum' instead of Enumerated Type pattern
JAVA0261 0.00JAVA0261 Use specialized Enum collection types
JAVA0262 0.00JAVA0262 Use of char in integer context
JAVA0263 0.00JAVA0263 Long literal ends with 'l' instead of 'L'
JAVA0264 1.00JAVA0264 Integer math in long context - check for overflow
JAVA0265 0.00JAVA0265 Use of Throwable.printStackTrace()
JAVA0266 1.00JAVA0266 Use of System.out
JAVA0267 0.00JAVA0267 Use of System.err
JAVA0269 0.00JAVA0269 Contents of StringBuffer never used
JAVA0270 6.00JAVA0270 Use Java 5.0 enhanced for loop construct to iterate over all elements in an array
JAVA0271 0.00JAVA0271 Minimize use of on-demand (.*) static imports
JAVA0272 0.00JAVA0272 Thread.run() called
JAVA0273 0.00JAVA0273 Non-final derivative of Thread calls start() in constructor
JAVA0274 0.00JAVA0274 Serializable class has a synchronized readObject()
JAVA0275 0.00JAVA0275 Serializable class has a synchronized writeObject() and no other synchronized methods
JAVA0276 0.00JAVA0276 Unnecessary use of String constructor
JAVA0277 0.00JAVA0277 Iterator.next() implementation does not throw NoSuchElementException
JAVA0278 0.00JAVA0278 Unnecessary use of Boolean constructor
JAVA0279 0.00JAVA0279 Serialization method readObject or readObjectNoData calls an overridable method
JAVA0280 0.00JAVA0280 IllegalMonitorStateException caught
JAVA0281 0.00JAVA0281 Iterator.next() not called in loop
JAVA0282 4.00JAVA0282 Call to Iterator.next() in loop which does not test Iterator.hasNext()
JAVA0283 0.00JAVA0283 Control variable not updated in loop body
JAVA0284 0.00JAVA0284 Explicit garbage collection
JAVA0285 0.00JAVA0285 Dereference of potentially null variable
JAVA0286 0.00JAVA0286 Dereference of null variable
JAVA0287 0.00JAVA0287 Unnecessary null check
JAVA0288 0.00JAVA0288 Inconsistent null check
LINES2855.00Number of lines in the source file
LINE_COMMENT234.00Number of line comments
LOC1664.00Lines of code
LOGICAL_LINES875.00Number of statements
LOOPS24.00Number of loops
NEST_DEPTH 5.00Maximum nesting depth
OPERANDS4016.00Number of operands
OPERATORS7915.00Number of operators
PARAMS168.00Number of formal parameter declarations
PROGRAM_LENGTH11931.00Halstead program length
PROGRAM_VOCAB1155.00Halstead program vocabulary
PROGRAM_VOLUME 0.00Halstead program volume
RETURNS244.00Number of return points from functions
SIZE108077.00Size of the file in bytes
UNIQUE_OPERANDS1084.00Number of unique operands
UNIQUE_OPERATORS71.00Number of unique operators
WHITESPACE375.00Number of whitespace lines