/**
* JavaGuard -- an obfuscation package for Java classfiles.
*
* Copyright (c) 2002 Thorsten Heit (theit@gmx.de)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* The author may be contacted at theit@gmx.de.
*
*
* $Id: LocalDirectoryFileContainer.java,v 1.5 2002/06/03 16:50:45 glurk Exp $
*/
package net.sf.javaguard;
import java.io.*;
import java.util.*;
import java.util.jar.Manifest;
import org.apache.oro.io.Perl5FilenameFilter;
import org.apache.oro.text.MalformedCachePatternException;
import net.sf.javaguard.classfile.ClassConstants;
import net.sf.javaguard.log.Log;
import net.sf.javaguard.log.ScreenLogger;
/** The purpose of a FileContainer is to ease the handling of a list of files
* created either from the entries of a Jar file or from the contents of a
* local directory that match a given regular expression.
*
* @author <a href="mailto:theit@gmx.de">Thorsten Heit</a>
*/
public class LocalDirectoryFileContainer implements FileContainer {
/** The name of the file container. Either the name of the assigned Jar file
* or the name of the local directory.
*/
private File directory;
/** A vector containing all files from the local directory that match the
* given regular expression.
*/
private Vector localFiles;
/** Holds the sorted list of files in the local directory. */
private SortedSet filenameSet;
/** The current screen logger. */
private static ScreenLogger logger = ScreenLogger.getInstance();
/** Creates a new instance of FileContainer to manage the contents of a local
* directory.
* @param dir the local directory
* @param regex a string with a regular expression
* @throws MalformedCachePatternException if there is an error in compiling
* the regular expression
*/
public LocalDirectoryFileContainer(File dir, String regex)
throws MalformedCachePatternException {
setDirectory(dir);
setFileNameSet(null);
setLocalFiles(new Vector(25, 25));
createFileList(dir, new Perl5FilenameFilter(regex));
}
/** Sets the directory for the file container.
* @param dir the directory for the file container.
* @see #getDirectory
*/
private void setDirectory(File dir) {
this.directory = dir;
}
/** Returns the directory for the file container.
* @return the directory for the file container
* @see #setDirectory
*/
private File getDirectory() {
return directory;
}
/** Returns the name for the file container. Either the name of the Jar file
* or the name of the local directory.
* @return the name for the file container
*/
public String getName() {
return getDirectory().getName();
}
/** Returns the Manifest file for the LocalDirectoryFileContainer. This
* function is currently not supported; it always returns null.
* @return Manifest file; always null
* @throws IOException if an I/O error occurs
*/
public Manifest getManifest()
throws IOException {
return null;
}
/** Returns a sorted set of the file names in this file container. Each
* element in the set must be a string in which packages/directories are
* separated by "/".
* @return a sorted set of file names; must not be null
*/
public SortedSet getFileNameSet() {
if (null == filenameSet) {
filenameSet = new TreeSet();
Vector vec = getLocalFiles();
for (int i=0; i<vec.size(); i++) {
File file = (File) vec.elementAt(i);
filenameSet.add(createFilename(file));
}
}
return filenameSet;
}
/** Sets the sorted set with file names of the entries of the Jar file.
* @param set the sorted set with file names
* @see #getFileNameSet
*/
private void setFileNameSet(SortedSet set) {
this.filenameSet = set;
}
/** Removes file entries in the current file container that are already
* contained in the given file container.
* @param fileContainer the file container to compare to
*/
public void removeDuplicates(FileContainer fileContainer) {
// only continue if the given file container is valid and if we have some
// elements stored in our local file container
if (null == fileContainer || getLocalFiles().isEmpty()) return;
logger.log(Log.INFO, "Searching for duplicates in local directory file container '" + getName() + "'");
boolean changed = false;
if (fileContainer instanceof LocalDirectoryFileContainer) {
LocalDirectoryFileContainer ldfc = (LocalDirectoryFileContainer) fileContainer;
// only continue when we have root directories!
if (null == getDirectory() || null == ldfc.getDirectory()) return;
// first compare the root directories of the local file containers
String str1 = createFilename(getDirectory());
String str2 = createFilename(ldfc.getDirectory());
// if no root directory is a prefix of the other one we can stop because
// the file lists are disjunct
if (!str1.startsWith(str2) && !str2.startsWith(str1)) return;
// walk through the local list of files and check whether class files
// are already contained in the other set so they can safely be removed
// from our set
SortedSet fileSet = ldfc.getFileNameSet();
Vector localFiles = getLocalFiles();
for (int i=0; i<localFiles.size(); i++) {
File file = (File) localFiles.elementAt(i);
String str = createFilename(file);
// if the other fileset contains the file's name remove it from the
// local list
if (fileSet.contains(str)) {
logger.log(Log.VERBOSE, "Removing entry '" + str + "' from file container");
localFiles.removeElementAt(i);
i--; // because we just removed one element...
changed = true;
}
}
} else if (fileContainer instanceof JarFileContainer) {
SortedSet fileSet = fileContainer.getFileNameSet();
Vector localfiles = getLocalFiles();
// iterate each element of the other file container
Iterator iter = fileSet.iterator();
while (iter.hasNext()) {
String className = (String) iter.next();
// check whether the local file list contains a file that equals the
// actual name, i.e. whether a local file ends with the actual name
for (int i=0; i<localFiles.size(); i++) {
File file = (File) localFiles.elementAt(i);
String fileName = createFilename(file);
if (fileName.endsWith(className)) {
logger.log(Log.VERBOSE, "Removing entry '" + className + "' from file container");
localFiles.removeElementAt(i);
i--; // because we just removed one element...
changed = true;
break;
}
}
}
} else {
throw new IllegalArgumentException("Unknown type of file container: " + fileContainer.getClass());
}
// if the file list has changed reset the tree set that holds the sorted
// list of files
if (changed) {
setFileNameSet(null);
}
}
/** Returns an enumeration of the elements of the file container. The
* <code>nextElement()</code> method of such an enumeration must return a
* subclass of {@link FileEntry}.
* @return an enumeration of the elements of the file container
*/
public Enumeration enumeration() {
return new LocalDirectoryFileEnumeration(getLocalFiles().elements());
}
/** Adds the list of files under the given subdirectory to the current list
* of files.
* @param dir the start directory
* @param regex the regular expression
*/
private void createFileList(File dir, Perl5FilenameFilter regex) {
if (null != dir && null != regex) {
File[] filelist = dir.listFiles();
if (null != filelist) {
for (int i=0; i<filelist.length; i++) {
if (filelist[i].isDirectory()) {
// recursively walk through the tree
createFileList(filelist[i], regex);
} else {
if (regex.accept(filelist[i])) {
addLocalFile(filelist[i]);
}
}
}
}
}
}
/** Sets the vector that holds the list of files contained in a local
* directory.
* @param vec vector that will hold the files
* @see #getLocalFiles
*/
private void setLocalFiles(Vector vec) {
this.localFiles = vec;
}
/** Returns the vector that holds the list of files in a local directory.
* @return vector that holds the list of files; always non-null
* @see #setLocalFiles
*/
Vector getLocalFiles() {
if (null == localFiles) {
localFiles = new Vector();
}
return localFiles;
}
/** Adds a local file to the list of available local files.
* @param file the file to add to the list of local files
*/
private void addLocalFile(File file) {
getLocalFiles().addElement(file);
}
/** Checks whether the given file name represents a Java class, i.e. checks
* whether the file name ends with ".class".
* @param file the file to check
* @return true if the file is a class file; false else
* @see #isClassFile(String)
*/
private static boolean isClassFile(File file) {
return isClassFile(file.getAbsolutePath());
}
/** Checks whether the given string represents a Java class, i.e. checks
* whether it ends with ".class".
* @param str the string to check
* @return true if the string represents the name of a class file; false else
*/
private static boolean isClassFile(String str) {
return str.endsWith(ClassConstants.CLASS_EXT);
}
/** Returns the name of the given file as a string. If possible the canonical
* form is used, otherwise the absolute path name is returned.
* @param file the file whose name should be returned
* @return the name of the given file as a string
*/
static String createFilename(File file) {
String str;
try {
// try to return the canonical path
str = file.getCanonicalPath();
} catch (IOException ioex) {
// if not possible use the absolute path
str = file.getAbsolutePath();
}
// replace the directory separator characters by a "/"
return str.replace(File.pathSeparatorChar, '/');
}
/** A data structure that holds a file entry.
*/
private class LocalDirectoryFileEnumeration implements Enumeration {
/** Holds the enumeration of available local files. */
Enumeration enumeration;
/** Holds the next file entry to return in <code>nextElement</code>. */
FileEntry entry = null;
/** Creates a new local file entry enumeration.
* @param enum a valid enumeration of File objects
*/
LocalDirectoryFileEnumeration(Enumeration enum) {
setEnumeration(enum);
}
/** Tests if this enumeration contains more elements.
* @return true if and only if this enumeration object contains at least
* one more element to provide; false otherwise
*/
public boolean hasMoreElements() {
if (null == getEntry()) {
fetchNextEntry();
}
return null != getEntry();
}
/** Returns the next element of this enumeration if this enumeration object
* has at least one more element to provide.
* @return the next element of this enumeration as a {@link FileEntry}
* object if at least one more element is available; null if no elements
* are left in the enumeration or an error has occured
*/
public Object nextElement() {
FileEntry retVal;
if (null == getEntry()) {
fetchNextEntry();
}
retVal = getEntry();
setEntry(null);
return retVal;
}
/** Creates a {@link FileEntry} object for the next element in the
* enumeration.
*/
private void fetchNextEntry() {
FileEntry entry = null;
if (getEnumeration().hasMoreElements()) {
File file = (File) getEnumeration().nextElement();
try {
String name;
try {
name = file.getCanonicalPath();
} catch (IOException ioex) {
// if it's not possible to receive the canonical name of the file
// just take the absolute path name
name = file.getAbsolutePath();
}
entry = new FileEntry(new DataInputStream(new FileInputStream(file)),
name, isClassFile(file), file.length());
} catch (FileNotFoundException fnfex) {
// should never happen because we've created the file list before
// we access the getNextEntry() function
logger.println("File not found: " + file.getName());
}
}
setEntry(entry);
}
/** Stores the given {@link FileEntry} object.
* @param fileEntry the file entry object to store
* @see #getEntry
*/
private void setEntry(FileEntry fileEntry) {
this.entry = fileEntry;
}
/** Returns the file entry object.
* @return file entry object
* @see #setEntry
*/
private FileEntry getEntry() {
return entry;
}
/** Stores the current enumeration.
* @param enum the enumeration
* @see #getEnumeration
*/
private void setEnumeration(Enumeration enum) {
this.enumeration = enum;
}
/** Returns the current enumeration.
* @return the current enumeration
* @see #setEnumeration
*/
private Enumeration getEnumeration() {
return enumeration;
}
}
}
|