001:/**
002: *
003: */
004:package net.sf.javaguard;
005:
006:
007:import java.io.*;
008:import java.util.*;
009:import java.util.jar.Manifest;
010:import org.apache.oro.io.Perl5FilenameFilter;
011:import org.apache.oro.text.MalformedCachePatternException;
012:import net.sf.javaguard.classfile.ClassConstants;
013:import net.sf.javaguard.log.Log;
014:import net.sf.javaguard.log.ScreenLogger;
015:
016:
017:/** The purpose of a FileContainer is to ease the handling of a list of files
018: * created either from the entries of a Jar file or from the contents of a
019: * local directory that match a given regular expression.
020: *
021: * @author <a href="mailto:theit@gmx.de">Thorsten Heit</a>
022: */
023:public class LocalDirectoryFileContainer implements FileContainer {
024: /** The name of the file container. Either the name of the assigned Jar file
025: * or the name of the local directory.
026: */
027: private File directory;
028: /** A vector containing all files from the local directory that match the
029: * given regular expression.
030: */
031: private Vector localFiles;
032: /** Holds the sorted list of files in the local directory. */
033: private SortedSet filenameSet;
034: /** The current screen logger. */
035: private static ScreenLogger logger = ScreenLogger.getInstance();
036:
037:
038:
039:
040: /** Creates a new instance of FileContainer to manage the contents of a local
041: * directory.
042: * @param dir the local directory
043: * @param regex a string with a regular expression
044: * @throws MalformedCachePatternException if there is an error in compiling
045: * the regular expression
046: */
047: public LocalDirectoryFileContainer(File dir, String regex)
048: throws MalformedCachePatternException {
049: setDirectory(dir);
050: setFileNameSet(null);
051: setLocalFiles(new Vector(25, 25));
052: createFileList(dir, new Perl5FilenameFilter(regex));
053: }
054:
055:
056:
057:
058: /** Sets the directory for the file container.
059: * @param dir the directory for the file container.
060: * @see #getDirectory
061: */
062: private void setDirectory(File dir) {
063: this .directory = dir;
064: }
065:
066:
067: /** Returns the directory for the file container.
068: * @return the directory for the file container
069: * @see #setDirectory
070: */
071: private File getDirectory() {
072: return directory;
073: }
074:
075:
076: /** Returns the name for the file container. Either the name of the Jar file
077: * or the name of the local directory.
078: * @return the name for the file container
079: */
080: public String getName() {
081: return getDirectory().getName();
082: }
083:
084:
085:
086:
087: /** Returns the Manifest file for the LocalDirectoryFileContainer. This
088: * function is currently not supported; it always returns null.
089: * @return Manifest file; always null
090: * @throws IOException if an I/O error occurs
091: */
092: public Manifest getManifest()
093: throws IOException {
094: return null;
095: }
096:
097:
098:
099:
100: /** Returns a sorted set of the file names in this file container. Each
101: * element in the set must be a string in which packages/directories are
102: * separated by "/".
103: * @return a sorted set of file names; must not be null
104: */
105: public SortedSet getFileNameSet() {
106: if (null == filenameSet) {
107: filenameSet = new TreeSet();
108: Vector vec = getLocalFiles();
109: for (int i=0; i<vec.size(); i++) {
110: File file = (File) vec.elementAt(i);
111: filenameSet.add(createFilename(file));
112: }
113: }
114: return filenameSet;
115: }
116:
117:
118: /** Sets the sorted set with file names of the entries of the Jar file.
119: * @param set the sorted set with file names
120: * @see #getFileNameSet
121: */
122: private void setFileNameSet(SortedSet set) {
123: this .filenameSet = set;
124: }
125:
126:
127:
128:
129: /** Removes file entries in the current file container that are already
130: * contained in the given file container.
131: * @param fileContainer the file container to compare to
132: */
133: public void removeDuplicates(FileContainer fileContainer) {
134: // only continue if the given file container is valid and if we have some
135: // elements stored in our local file container
136: if (null == fileContainer || getLocalFiles().isEmpty()) return;
137: logger.log(Log.INFO, "Searching for duplicates in local directory file container '" + getName() + "'");
138:
139: boolean changed = false;
140: if (fileContainer instanceof LocalDirectoryFileContainer) {
141: LocalDirectoryFileContainer ldfc = (LocalDirectoryFileContainer) fileContainer;
142: // only continue when we have root directories!
143: if (null == getDirectory() || null == ldfc.getDirectory()) return;
144: // first compare the root directories of the local file containers
145: String str1 = createFilename(getDirectory());
146: String str2 = createFilename(ldfc.getDirectory());
147: // if no root directory is a prefix of the other one we can stop because
148: // the file lists are disjunct
149: if (!str1.startsWith(str2) && !str2.startsWith(str1)) return;
150: // walk through the local list of files and check whether class files
151: // are already contained in the other set so they can safely be removed
152: // from our set
153: SortedSet fileSet = ldfc.getFileNameSet();
154: Vector localFiles = getLocalFiles();
155: for (int i=0; i<localFiles.size(); i++) {
156: File file = (File) localFiles.elementAt(i);
157: String str = createFilename(file);
158: // if the other fileset contains the file's name remove it from the
159: // local list
160: if (fileSet.contains(str)) {
161: logger.log(Log.VERBOSE, "Removing entry '" + str + "' from file container");
162: localFiles.removeElementAt(i);
163: i--; // because we just removed one element...
164: changed = true;
165: }
166: }
167: } else if (fileContainer instanceof JarFileContainer) {
168: SortedSet fileSet = fileContainer.getFileNameSet();
169: Vector localfiles = getLocalFiles();
170: // iterate each element of the other file container
171: Iterator iter = fileSet.iterator();
172: while (iter.hasNext()) {
173: String className = (String) iter.next();
174: // check whether the local file list contains a file that equals the
175: // actual name, i.e. whether a local file ends with the actual name
176: for (int i=0; i<localFiles.size(); i++) {
177: File file = (File) localFiles.elementAt(i);
178: String fileName = createFilename(file);
179: if (fileName.endsWith(className)) {
180: logger.log(Log.VERBOSE, "Removing entry '" + className + "' from file container");
181: localFiles.removeElementAt(i);
182: i--; // because we just removed one element...
183: changed = true;
184: break;
185: }
186: }
187: }
188: } else {
189: throw new IllegalArgumentException("Unknown type of file container: " + fileContainer.getClass());
190: }
191: // if the file list has changed reset the tree set that holds the sorted
192: // list of files
193: if (changed) {
194: setFileNameSet(null);
195: }
196: }
197:
198:
199:
200:
201: /** Returns an enumeration of the elements of the file container. The
202: * <code>nextElement()</code> method of such an enumeration must return a
203: * subclass of {@link FileEntry}.
204: * @return an enumeration of the elements of the file container
205: */
206: public Enumeration enumeration() {
207: return new LocalDirectoryFileEnumeration(getLocalFiles().elements());
208: }
209:
210:
211:
212:
213: /** Adds the list of files under the given subdirectory to the current list
214: * of files.
215: * @param dir the start directory
216: * @param regex the regular expression
217: */
218: private void createFileList(File dir, Perl5FilenameFilter regex) {
219: if (null != dir && null != regex) {
220: File[] filelist = dir.listFiles();
221: if (null != filelist) {
222: for (int i=0; i<filelist.length; i++) {
223: if (filelist[i].isDirectory()) {
224: // recursively walk through the tree
225: createFileList(filelist[i], regex);
226: } else {
227: if (regex.accept(filelist[i])) {
228: addLocalFile(filelist[i]);
229: }
230: }
231: }
232: }
233: }
234: }
235:
236:
237:
238:
239: /** Sets the vector that holds the list of files contained in a local
240: * directory.
241: * @param vec vector that will hold the files
242: * @see #getLocalFiles
243: */
244: private void setLocalFiles(Vector vec) {
245: this .localFiles = vec;
246: }
247:
248:
249: /** Returns the vector that holds the list of files in a local directory.
250: * @return vector that holds the list of files; always non-null
251: * @see #setLocalFiles
252: */
253: Vector getLocalFiles() {
254: if (null == localFiles) {
255: localFiles = new Vector();
256: }
257: return localFiles;
258: }
259:
260:
261: /** Adds a local file to the list of available local files.
262: * @param file the file to add to the list of local files
263: */
264: private void addLocalFile(File file) {
265: getLocalFiles().addElement(file);
266: }
267:
268:
269:
270:
271: /** Checks whether the given file name represents a Java class, i.e. checks
272: * whether the file name ends with ".class".
273: * @param file the file to check
274: * @return true if the file is a class file; false else
275: * @see #isClassFile(String)
276: */
277: private static boolean isClassFile(File file) {
278: return isClassFile(file.getAbsolutePath());
279: }
280:
281:
282: /** Checks whether the given string represents a Java class, i.e. checks
283: * whether it ends with ".class".
284: * @param str the string to check
285: * @return true if the string represents the name of a class file; false else
286: */
287: private static boolean isClassFile(String str) {
288: return str.endsWith(ClassConstants.CLASS_EXT);
289: }
290:
291:
292: /** Returns the name of the given file as a string. If possible the canonical
293: * form is used, otherwise the absolute path name is returned.
294: * @param file the file whose name should be returned
295: * @return the name of the given file as a string
296: */
297: static String createFilename(File file) {
298: String str;
299: try {
300: // try to return the canonical path
301: str = file.getCanonicalPath();
302: } catch (IOException ioex) {
303: // if not possible use the absolute path
304: str = file.getAbsolutePath();
305: }
306: // replace the directory separator characters by a "/"
307: return str.replace(File.pathSeparatorChar, '/');
308: }
309:
310:
311:
312:
313: /** A data structure that holds a file entry.
314: */
315: private class LocalDirectoryFileEnumeration implements Enumeration {
316: /** Holds the enumeration of available local files. */
317: Enumeration enumeration;
318: /** Holds the next file entry to return in <code>nextElement</code>. */
319: FileEntry entry = null;
320:
321:
322: /** Creates a new local file entry enumeration.
323: * @param enum a valid enumeration of File objects
324: */
325: LocalDirectoryFileEnumeration(Enumeration enum) {
326: setEnumeration(enum);
327: }
328:
329:
330: /** Tests if this enumeration contains more elements.
331: * @return true if and only if this enumeration object contains at least
332: * one more element to provide; false otherwise
333: */
334: public boolean hasMoreElements() {
335: if (null == getEntry()) {
336: fetchNextEntry();
337: }
338: return null != getEntry();
339: }
340:
341:
342: /** Returns the next element of this enumeration if this enumeration object
343: * has at least one more element to provide.
344: * @return the next element of this enumeration as a {@link FileEntry}
345: * object if at least one more element is available; null if no elements
346: * are left in the enumeration or an error has occured
347: */
348: public Object nextElement() {
349: FileEntry retVal;
350: if (null == getEntry()) {
351: fetchNextEntry();
352: }
353: retVal = getEntry();
354: setEntry(null);
355: return retVal;
356: }
357:
358:
359: /** Creates a {@link FileEntry} object for the next element in the
360: * enumeration.
361: */
362: private void fetchNextEntry() {
363: FileEntry entry = null;
364: if (getEnumeration().hasMoreElements()) {
365: File file = (File) getEnumeration().nextElement();
366: try {
367: String name;
368: try {
369: name = file.getCanonicalPath();
370: } catch (IOException ioex) {
371: // if it's not possible to receive the canonical name of the file
372: // just take the absolute path name
373: name = file.getAbsolutePath();
374: }
375: entry = new FileEntry(new DataInputStream(new FileInputStream(file)),
376: name, isClassFile(file), file.length());
377: } catch (FileNotFoundException fnfex) {
378: // should never happen because we've created the file list before
379: // we access the getNextEntry() function
380: logger.println("File not found: " + file.getName());
381: }
382: }
383: setEntry(entry);
384: }
385:
386:
387: /** Stores the given {@link FileEntry} object.
388: * @param fileEntry the file entry object to store
389: * @see #getEntry
390: */
391: private void setEntry(FileEntry fileEntry) {
392: this .entry = fileEntry;
393: }
394:
395:
396: /** Returns the file entry object.
397: * @return file entry object
398: * @see #setEntry
399: */
400: private FileEntry getEntry() {
401: return entry;
402: }
403:
404:
405:
406:
407: /** Stores the current enumeration.
408: * @param enum the enumeration
409: * @see #getEnumeration
410: */
411: private void setEnumeration(Enumeration enum) {
412: this .enumeration = enum;
413: }
414:
415:
416: /** Returns the current enumeration.
417: * @return the current enumeration
418: * @see #setEnumeration
419: */
420: private Enumeration getEnumeration() {
421: return enumeration;
422: }
423: }
424:}
|