package net.sourceforge.javautil.classloader.source;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.jar.Manifest;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import net.sourceforge.javautil.classloader.impl.ClassInfo;
import net.sourceforge.javautil.classloader.impl.ClassSearchInfo;
import net.sourceforge.javautil.classloader.impl.PackageSearchInfo;
import net.sourceforge.javautil.common.ClassNameUtil;
import net.sourceforge.javautil.common.IOUtil;
import net.sourceforge.javautil.common.Refreshable;
import net.sourceforge.javautil.common.exception.ThrowableManagerRegistry;
import net.sourceforge.javautil.common.io.IVirtualArtifact;
import net.sourceforge.javautil.common.io.VirtualArtifactException;
import net.sourceforge.javautil.common.io.VirtualArtifactNotFoundException;
import net.sourceforge.javautil.common.io.VirtualArtifactSystem;
import net.sourceforge.javautil.common.io.IVirtualDirectory;
import net.sourceforge.javautil.common.io.VirtualDirectoryVisitorContext;
import net.sourceforge.javautil.common.io.IVirtualFile;
import net.sourceforge.javautil.common.io.IVirtualPath;
import net.sourceforge.javautil.common.io.impl.ArtifactCollector;
import net.sourceforge.javautil.common.io.impl.DirectoryRoot;
import net.sourceforge.javautil.common.io.impl.DirectoryFile;
import net.sourceforge.javautil.common.io.impl.SimplePath;
import net.sourceforge.javautil.common.io.impl.SystemDirectory;
import net.sourceforge.javautil.common.visitor.IVisitorSimple;
/**
* A class source based on a {@link IVirtualDirectory}.
*
* @author elponderador
* @author $Author: ponderator $
* @version $Id: VirtualDirectoryClassSource.java 2534 2010-11-05 04:18:32Z ponderator $
*/
public class VirtualDirectoryClassSource extends ClassSource implements Refreshable {
public static final String MEMORY_REPOSITORY_VAS = "net.sf.javautil.classloader.memory";
public static final String DEFAULT_MEMORY_REPOSITORY = "default";
/**
* @param repositoryName The repository name
* @return True if the repository exists, otherwise false
*/
public static boolean isExistsRepository (String repositoryName) {
return VirtualArtifactSystem.get(MEMORY_REPOSITORY_VAS, true).isExists(new SimplePath(repositoryName));
}
/**
* @param repositoryName The repository name
* @param name The name of the jar
* @return True if the jar exists in the specified repository, otherwise false
*/
public static boolean isExistsJar (String repositoryName, String name) {
return VirtualArtifactSystem.get(MEMORY_REPOSITORY_VAS, true).isExists(new SimplePath(repositoryName + "/" + name));
}
/**
* @param repositoryName The repository to retreive
* @param autoCreate True if the repository should be created if it does not already exist, otherwise false
* @return The in memory repository
*/
public static IVirtualDirectory getInMemoryRepository (String repositoryName, boolean autoCreate) {
IVirtualDirectory repository = VirtualArtifactSystem.get(MEMORY_REPOSITORY_VAS, true).getDirectory(repositoryName, autoCreate);
if (repository == null) throw new IllegalArgumentException("Could not find repository: " + repositoryName);
return repository;
}
/**
* @param repositoryName The repository from which to get the jar
* @param name The name of the jar
* @return The directory/jar
*/
public static IVirtualDirectory getInMemoryJar (String repositoryName, String name) {
IVirtualDirectory jar = getInMemoryRepository(repositoryName, false).getDirectory(name);
if (jar == null) throw new IllegalArgumentException("Could not find repository jar: " + repositoryName + ":" + name);
return jar;
}
/**
* @param repositoryName The repository to remove
*/
public static void removeInMemoryRepository (String repositoryName) {
VirtualArtifactSystem.get(MEMORY_REPOSITORY_VAS, true).remove(repositoryName);
}
/**
* This assumes the default repository: {@value #DEFAULT_MEMORY_REPOSITORY}.
*
* @see #getFromInMemoryRepository(String, String)
*/
public static VirtualDirectoryClassSource getFromInMemoryRepository (String name) {
return getFromInMemoryRepository(DEFAULT_MEMORY_REPOSITORY, name);
}
/**
* @param repositoryName The repository in which to make the in memory jar
* @param name The name of the jar
* @return The previously made class source/in memory jar
*
* @see #createInMemoryJar(String, String, ZipInputStream)
*/
public static VirtualDirectoryClassSource getFromInMemoryRepository (String repositoryName, String name) {
return new VirtualDirectoryClassSource( getInMemoryJar(repositoryName, name) );
}
/**
* This will simple wrap the input stream passed with a {@link ZipInputStream}.
*
* @see #createInMemoryJar(String, String, ZipInputStream)
*/
public static VirtualDirectoryClassSource createInMemoryJar (String repositoryName, String name, InputStream input) {
return createInMemoryJar(repositoryName, name, new ZipInputStream(input));
}
/**
* @param repositoryName The name of the in memory repository where the jar will be created
* @param name The name of the jar
* @param input The input from which to read the jar
* @return A class source pointing to the in memory jar
*/
public static VirtualDirectoryClassSource createInMemoryJar (String repositoryName, String name, ZipInputStream input) {
IVirtualDirectory directory = getInMemoryRepository(repositoryName, true).getDirectory(name, true);
try {
ZipEntry entry = null;
do {
entry = input.getNextEntry();
if (entry != null) {
if (entry.getName().endsWith("/")) {
directory.createDirectory(new SimplePath(entry.getName()));
} else
directory.createFile(new SimplePath(entry.getName()), IOUtil.read(input, null, false));
}
} while (entry != null);
} catch (IOException e) {
throw ThrowableManagerRegistry.caught(e);
} finally {
try { input.close(); } catch (IOException e) {
ThrowableManagerRegistry.caught(e);
}
}
return new VirtualDirectoryClassSource(directory);
}
public static final Pattern pathSeparator = Pattern.compile("[/\\\\]");
protected final IVirtualDirectory directory;
protected final List<String> packages = new ArrayList<String>(5);
protected boolean searched = false;
protected long lastModified = System.currentTimeMillis();
protected Manifest manifest;
public VirtualDirectoryClassSource(IVirtualDirectory directory) {
super(directory.getPath().toString("/"));
this.directory = directory;
}
public Manifest getManifest() {
if (this.manifest != null) return this.manifest;
return super.getManifest();
}
/**
* This allows one to set an override for the manifest file returned for this class source.
*
* @param manifest The manifest file override
*
* @see #getManifest()
*/
public void setManifest(Manifest manifest) { this.manifest = manifest; }
/**
* @return The directory this class source is based on
*/
public IVirtualDirectory getDirectory() { return directory; }
@Override public IVirtualArtifact getVirtualArtifact() { return directory; }
@Override public long getLastModified() { return this.directory.getRecursiveLastModified(); }
@Override public long getLastModifiedClass() {
Set<IVirtualFile> classFiles = this.directory.accept( (ArtifactCollector<IVirtualFile>)
new ArtifactCollector()
.setIncludeDirectories(false)
.addInclusionPatternFilter(Pattern.compile(".*\\.class"))
.setCollectionDefault(false)
).getCollected();
long stamp = -1;
for (IVirtualFile file : classFiles) {
if (file.getLastModified() > stamp) stamp = file.getLastModified();
}
return stamp;
}
@Override public ClassInfo getClassInfo(ClassSearchInfo info) throws ClassNotFoundException {
try {
return new ClassInfo(info, this);
} catch (VirtualArtifactNotFoundException e) {
throw new ClassNotFoundException(info.getFullClassName());
}
}
@Override public Collection<String> getResourceNames() {
return directory.accept(new ClassFileVisitor(false)).names;
}
@Override public Collection<String> getClassNames() {
return directory.accept(new ClassFileVisitor(true)).names;
}
@Override public List<String> getClassNamesForPackage(PackageSearchInfo info) {
IVirtualArtifact pkgdir = this.getPathedArtifact(info.getPackagePath());
List<String> classNames = new ArrayList<String>();
if (pkgdir instanceof IVirtualDirectory) {
Iterator<IVirtualArtifact> artifacts = ((IVirtualDirectory)pkgdir).getArtifacts();
while (artifacts.hasNext()) {
IVirtualArtifact artifact = artifacts.next();
if (ClassNameUtil.isClassSource( artifact.getName() )) {
IVirtualPath relative = directory.getRelativePath(artifact);
classNames.add( ClassNameUtil.toClassName(relative.toString("/")) );
}
}
}
return classNames;
}
@Override public Collection<String> getPackages() {
PackageNameVisitor pkgs = new PackageNameVisitor();
directory.accept(pkgs);
return pkgs.packages;
}
@Override public URL getResource(String resourceName) {
try {
return this.getPathedArtifact(resourceName).getURL();
} catch (VirtualArtifactNotFoundException e) {
return null;
}
}
@Override public ClassSource clone() throws CloneNotSupportedException {
VirtualDirectoryClassSource vds = new VirtualDirectoryClassSource( this.directory );
vds.setManifest(manifest);
return vds;
}
@Override public URL getURL() { return directory.getURL(); }
@Override public boolean hasClass(ClassSearchInfo info) {
try {
return this.getClassInfo(info) != null;
} catch (ClassNotFoundException e) {
return false;
}
}
@Override public boolean hasPackage(PackageSearchInfo info) {
return this.hasParentPackage(info.getPackageName());
}
@Override public boolean hasParentPackage(String packageName) {
try {
return directory.getArtifact(new SimplePath(packageName, "\\.")) instanceof IVirtualDirectory;
} catch (VirtualArtifactNotFoundException e) {
return false;
}
}
@Override public boolean hasParentResource(String parentResource) {
try {
return this.getPathedArtifact(parentResource) instanceof IVirtualDirectory;
} catch (VirtualArtifactNotFoundException e) {
return false;
}
}
@Override public boolean hasResource(String resourceName) {
try {
return this.getPathedArtifact(resourceName) != null;
} catch (VirtualArtifactNotFoundException e) {
return false;
}
}
@Override public boolean hasSearchedAll() { return this.searched; }
@Override public boolean isHasBeenModified() {
return this.getLastModified() > this.lastModified;
}
@Override public boolean isHasClassesBeenModified() {
return this.getLastModifiedClass() > this.lastModified;
}
@Override public byte[] loadInternal(ClassSearchInfo info) throws ClassNotFoundException {
try {
return IOUtil.read(((IVirtualFile)this.getArtifact(info)).getInputStream(), this.getBuffer());
} catch (IOException e) {
throw ThrowableManagerRegistry.caught(e);
} catch (VirtualArtifactNotFoundException e) {
throw new ClassNotFoundException(info.getFullClassName(), e);
}
}
private IVirtualArtifact getPathedArtifact (String path) { return directory.getArtifact(this.getPath(path)); }
private IVirtualArtifact getArtifact (ClassSearchInfo info) { return directory.getArtifact(this.getPath(info.getClassPath())); }
private IVirtualPath getPath (String path) { return new SimplePath(path, pathSeparator); }
public void refresh() {
this.lastModified = this.directory.getRecursiveLastModified();
this.packages.clear();
this.searched = false;
}
@Override public void reload() {
this.refresh();
}
/**
* A class name collector.
*
* @author elponderador
* @author $Author: ponderator $
* @version $Id: VirtualDirectoryClassSource.java 2534 2010-11-05 04:18:32Z ponderator $
*/
public class ClassFileVisitor implements IVisitorSimple<VirtualDirectoryVisitorContext> {
protected boolean classes = true;
private List<String> names = new ArrayList<String>();
public ClassFileVisitor(boolean classes) { this.classes = classes; }
public void visit(VirtualDirectoryVisitorContext ctx) {
if (classes && ctx.getVisited().getName().contains("-")) { ctx.skip(); return; }
boolean isClass = ClassNameUtil.isClassSource(ctx.getVisited().getName());
if (classes && isClass) names.add(ClassNameUtil.toClassName( directory.getRelativePath(ctx.getVisited()).toString("/") ));
else if (!classes && !isClass) names.add(directory.getRelativePath(ctx.getVisited()).toString("/"));
}
}
/**
* A package name collector.
*
* @author elponderador
* @author $Author: ponderator $
* @version $Id: VirtualDirectoryClassSource.java 2534 2010-11-05 04:18:32Z ponderator $
*/
public class PackageNameVisitor implements IVisitorSimple<VirtualDirectoryVisitorContext> {
private List<String> packages = new ArrayList<String>();
public void visit(VirtualDirectoryVisitorContext ctx) {
if (ctx.getVisited().getName().contains("-")) { ctx.skip(); return; }
if (ctx.getVisited() instanceof IVirtualDirectory && ctx.getVisited() != directory)
packages.add(ClassNameUtil.toPackageName( directory.getRelativePath(ctx.getVisited()).toString("/") ));
}
}
}