001:/**
002: *
003: */
004:package net.sf.javaguard;
005:
006:
007:import java.io.*;
008:import java.util.*;
009:import java.util.jar.*;
010:import net.sf.javaguard.classfile.ClassConstants;
011:import net.sf.javaguard.log.Log;
012:import net.sf.javaguard.log.ScreenLogger;
013:
014:
015:/** The purpose of a FileContainer is to ease the handling of a list of files
016: * created either from the entries of a Jar file or from the contents of a
017: * local directory that match a given regular expression.
018: *
019: * @author <a href="mailto:theit@gmx.de">Thorsten Heit</a>
020: */
021:public class JarFileContainer implements FileContainer {
022: /** The Jar file. */
023: private JarFile jarFile;
024: /** A sorted set with file names of the entries of the Jar file. */
025: private SortedSet filenameSet;
026: /** A sorted set with file names that should be ignored in the enumeration. */
027: private Set ignorableFiles;
028:
029: /** The current screen logger. */
030: private static ScreenLogger logger = ScreenLogger.getInstance();
031:
032:
033:
034:
035: /** Creates a new file container to manage the contents of a Jar file.
036: * @param jarFile the Jar file
037: */
038: public JarFileContainer(JarFile jarFile) {
039: setJarFile(jarFile);
040: setFileNameSet(null);
041: setIgnorableFiles(null);
042: }
043:
044:
045:
046:
047: /** Returns the name for the file container. Either the name of the Jar file
048: * or the name of the local directory.
049: * @return the name for the file container
050: */
051: public String getName() {
052: return getJarFile().getName();
053: }
054:
055:
056:
057:
058: /** Returns the Manifest file for the JarFileContainer.
059: * @return Manifest file
060: * @throws IOException if an I/O error occurs
061: */
062: public Manifest getManifest()
063: throws IOException {
064: return getJarFile().getManifest();
065: }
066:
067:
068:
069:
070: /** Returns a sorted set of the file names in this file container. Each
071: * element in the set must be a string in which packages/directories are
072: * separated by "/".
073: * @return a sorted set of file names; must not be null
074: */
075: public SortedSet getFileNameSet() {
076: if (null == filenameSet) {
077: filenameSet = new TreeSet();
078: JarFile jf = getJarFile();
079: if (null != jf) {
080: Enumeration enum = jf.entries();
081: while (enum.hasMoreElements()) {
082: JarEntry jarEntry = (JarEntry) enum.nextElement();
083: // only add non-directory entries
084: if (!jarEntry.isDirectory()) {
085: filenameSet.add(jarEntry.getName());
086: }
087: }
088: }
089: }
090: return filenameSet;
091: }
092:
093:
094: /** Sets the sorted set with file names of the entries of the Jar file.
095: * @param set the sorted set with file names
096: * @see #getFileNameSet
097: */
098: private void setFileNameSet(SortedSet set) {
099: this .filenameSet = set;
100: }
101:
102:
103:
104:
105: /** Stores a sorted set with file names to ignore in an enumeration of the
106: * available elements of this file container.
107: * @param a set a set with file names to ignore
108: * @see #getIgnorableFiles
109: */
110: private void setIgnorableFiles(Set set) {
111: this .ignorableFiles = set;
112: }
113:
114:
115: /** Returns a sorted set with file names to ignore in an enumeration of the
116: * available elements of this file container.
117: * @return a sorted set with file names to ignore; always non-null
118: * @see #setIgnorableFiles
119: */
120: private Set getIgnorableFiles() {
121: if (null == ignorableFiles) {
122: ignorableFiles = new TreeSet();
123: }
124: return ignorableFiles;
125: }
126:
127:
128:
129:
130: /** Removes file entries in the current file container that are already
131: * contained in the given file container.
132: * @param fileContainer the file container to compare to
133: */
134: public void removeDuplicates(FileContainer fileContainer) {
135: // only continue if the given file container is valid and if we have some
136: // elements stored in our Jar file container
137: if (null == fileContainer || getFileNameSet().isEmpty()) return;
138: logger.log(Log.INFO, "Searching for duplicates in Jar file container '" + getName() + "'");
139:
140: SortedSet localFileSet = getFileNameSet();
141: boolean changed = false;
142:
143: if (fileContainer instanceof JarFileContainer) {
144: // walk through the other container's file set and search all elements
145: // that exist in our local set
146: SortedSet set = fileContainer.getFileNameSet();
147: Iterator iter = set.iterator();
148: while (iter.hasNext()) {
149: String name = (String) iter.next();
150: if (localFileSet.contains(name)) {
151: // file exists twice -> let it be ignored in the enumeration
152: getIgnorableFiles().add(name);
153: logger.log(Log.VERBOSE, "Removing entry '" + name + "' from file container");
154: changed = true;
155: }
156: }
157: } else if (fileContainer instanceof LocalDirectoryFileContainer) {
158: // walk through the local file set and search all elements that exist in
159: // the other container's set
160: Iterator iter = localFileSet.iterator();
161: Vector otherFileList = ((LocalDirectoryFileContainer) fileContainer).getLocalFiles();
162: while (iter.hasNext()) {
163: String name = (String) iter.next();
164: for (int i=0; i<otherFileList.size(); i++) {
165: File file = (File) otherFileList.elementAt(i);
166: String str = LocalDirectoryFileContainer.createFilename(file);
167: if (str.endsWith(name)) {
168: getIgnorableFiles().add(name);
169: logger.log(Log.VERBOSE, "Removing entry '" + name + "' from file container");
170: changed = true;
171: }
172: }
173: }
174: } else {
175: throw new IllegalArgumentException("Unknown type of file container: " + fileContainer.getClass());
176: }
177: // if the file list has changed reset the tree set that holds the sorted
178: // list of files
179: if (changed) {
180: setFileNameSet(null);
181: }
182: }
183:
184:
185:
186:
187: /** Returns an enumeration of the elements of the file container. The
188: * <code>nextElement()</code> method of such an enumeration must return a
189: * subclass of {@link FileEntry}.
190: * @return an enumeration of the elements of the file container
191: */
192: public Enumeration enumeration() {
193: return new JarFileEnumeration(getJarFile().entries());
194: }
195:
196:
197:
198:
199: /** Sets the Jar file to manage.
200: * @param jarFile the Jar file to manage
201: * @see #getJarFile
202: */
203: private void setJarFile(JarFile jarFile) {
204: this .jarFile = jarFile;
205: }
206:
207:
208: /** Returns the Jar file to manage.
209: * @return the Jar file
210: * @see #setJarFile
211: */
212: private JarFile getJarFile() {
213: return jarFile;
214: }
215:
216:
217:
218:
219: /** Realises an enumeration of all entries in the Jar file. Subsequent calls
220: * to nextElement() will return only file entries, and no entries from the
221: * META-INF/ directory.
222: */
223: private class JarFileEnumeration implements Enumeration, ClassConstants {
224: /** Holds the main enumeration for the Jar file entries. */
225: Enumeration enumeration;
226: /** Holds the next file entry to return in <code>nextElement</code>. */
227: FileEntry entry = null;
228:
229:
230: /** Creates a new Jar file entry enumeration.
231: * @param enum a valid enumeration of JarEntry objects
232: */
233: JarFileEnumeration(Enumeration enum) {
234: setEnumeration(enum);
235: }
236:
237:
238: /** Tests if this enumeration contains more elements.
239: * @return true if and only if this enumeration object contains at least
240: * one more element to provide; false otherwise
241: */
242: public boolean hasMoreElements() {
243: if (null == getEntry()) {
244: fetchNextEntry();
245: }
246: return null != getEntry();
247: }
248:
249:
250: /** Returns the next element of this enumeration if this enumeration object
251: * has at least one more element to provide.
252: * @return the next element of this enumeration as a {@link FileEntry}
253: * object if at least one more element is available; null if no elements
254: * are left in the enumeration or an error has occured
255: */
256: public Object nextElement() {
257: FileEntry retVal;
258: if (null == getEntry()) {
259: fetchNextEntry();
260: }
261: retVal = getEntry();
262: setEntry(null);
263: return retVal;
264: }
265:
266:
267: /** Creates a {@link FileEntry} object for the next element in the
268: * enumeration.
269: */
270: private void fetchNextEntry() {
271: FileEntry entry = null;
272: if (getEnumeration().hasMoreElements()) {
273: JarEntry inEntry = null;
274: while (getEnumeration().hasMoreElements()) {
275: inEntry = (JarEntry) getEnumeration().nextElement();
276: // continue if the actual file name should be ignored
277: if (getIgnorableFiles().contains(inEntry.getName())) continue;
278: // skip all directory entries and all entries in the META-INF subdir
279: if (!inEntry.isDirectory() && !isMetaInfEntry(inEntry)) break;
280: }
281: if (null != inEntry) {
282: try {
283: entry = new FileEntry(new DataInputStream(
284: new BufferedInputStream(getJarFile().getInputStream(inEntry))),
285: inEntry.getName(), isClassFile(inEntry), inEntry.getSize());
286: } catch (IOException ioex) {
287: logger.println("I/O exception trying to access Jar file entry '" + inEntry.getName() + "'");
288: }
289: }
290: }
291: setEntry(entry);
292: }
293:
294:
295: /** Stores the given {@link FileEntry} object.
296: * @param fileEntry the file entry object to store
297: * @see #getEntry
298: */
299: private void setEntry(FileEntry fileEntry) {
300: this .entry = fileEntry;
301: }
302:
303:
304: /** Returns the file entry object.
305: * @return file entry object
306: * @see #setEntry
307: */
308: private FileEntry getEntry() {
309: return entry;
310: }
311:
312:
313: /** Checks whether the given Jar file entry matches an entry from the
314: * META-INF directory.
315: * @param entry the Jar file entry
316: * @return true if the entry is from
317: */
318: private boolean isMetaInfEntry(JarEntry entry) {
319: String name = entry.getName();
320: if (STREAM_NAME_MANIFEST.equals(name.toUpperCase())
321: || (name.length() > (SIGNATURE_PREFIX.length() + 1 + SIGNATURE_EXT.length())
322: && name.indexOf(SIGNATURE_PREFIX) != -1 && name.endsWith(SIGNATURE_EXT))) {
323: // Don't pass through the manifest or signature files
324: return true;
325: }
326: return false;
327: }
328:
329:
330: /** checks whether the given Jar file entry represents a Java class, i.e.
331: * checks whether the file name ends with ".class".
332: * @param entry the entry from the Jar file
333: * @return true if the file is a class file; false else
334: */
335: private boolean isClassFile(JarEntry entry) {
336: return entry.getName().endsWith(ClassConstants.CLASS_EXT);
337: }
338:
339:
340: /** Stores the current enumeration.
341: * @param enum the enumeration
342: * @see #getEnumeration
343: */
344: private void setEnumeration(Enumeration enum) {
345: this .enumeration = enum;
346: }
347:
348:
349: /** Returns the current enumeration.
350: * @return the current enumeration
351: * @see #setEnumeration
352: */
353: private Enumeration getEnumeration() {
354: return enumeration;
355: }
356: }
357:}
|