package net.sourceforge.javautil.classloader.resolver;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import net.sourceforge.javautil.classloader.resolver.ClassPackageResolverNetworkNode.Type;
import net.sourceforge.javautil.classloader.resolver.impl.ClassPackageDependencyReferenceImpl;
import net.sourceforge.javautil.classloader.source.ClassSource;
/**
* This is the root of an entire resolution process. This network based resolution composite IS NOT thread safe.
*
* @author elponderador
* @author $Author$
* @version $Id$
*/
public class ClassPackageResolverContext {
protected Logger log = Logger.getLogger(ClassPackageResolverContext.class.getName());
protected Map<String, ClassPackageResolverNetworkNode> index = new HashMap<String, ClassPackageResolverNetworkNode>();
protected List<IClassPackageDependencyReference> stack = new ArrayList<IClassPackageDependencyReference>();
protected List<IClassPackageDescriptor> descriptorStack = new ArrayList<IClassPackageDescriptor>();
protected boolean resolveDependencies = true;
public ClassPackageResolverContext () {}
public ClassPackageResolverContext (IClassPackageDescriptor descriptor) {
this.descriptorStack.add(descriptor);
}
public ClassPackageResolverContext (IClassPackageReference reference) {
this.stack.add(new ClassPackageDependencyReferenceImpl(reference));
}
/**
* @return The current node being resolved
*/
public IClassPackageDependencyReference getCurrentDependency () { return stack.size() == 0 ? null : stack.get(0); }
/**
* @return The parent dependency that is referencing the {@link #getCurrentDependency()}, or null if on first level
*/
public IClassPackageDependencyReference getParentDependency () { return stack.size() <= 1 ? null : stack.get(1); }
/**
* @return The descriptor related to the {@link #getCurrentDependency()}, or null if no descriptor was provided
*/
public IClassPackageDescriptor getDescriptor () { return this.descriptorStack.size() == 0 ? null : descriptorStack.get(0); }
public boolean isResolveDependencies() { return resolveDependencies; }
public void setResolveDependencies(boolean resolveDependencies) {
this.resolveDependencies = resolveDependencies;
}
public int getStackSize () {
return this.stack.size();
}
/**
* @return A list, possibly empty, of all non-null descriptors provided
*/
public Set<IClassPackageDescriptor> getDescriptors () {
Set<IClassPackageDescriptor> descriptors = new HashSet<IClassPackageDescriptor>();
for (IClassPackageDescriptor desc : this.descriptorStack) {
if (desc != null) descriptors.add(desc);
}
return descriptors;
}
/**
* @param reference The reference in question
* @return A node with already resolved information if available, otherwise false
*/
public ClassPackageResolverNetworkNode getResolved (IClassArtifactReference reference) { return index.get(reference.toArtifactString()); }
/**
* This can be used to detect circular references.
*
* @param reference The reference in question
* @return True if a reference to this artifact is already on the stack, otherwise false
*/
public boolean isInStack (IClassArtifactReference reference) {
if (stack.size() > 1) {
for (int i=1; i<stack.size(); i++) {
if (reference.compareTo(stack.get(i)) == 0) return true;
}
}
return false;
}
/**
* @param reference The reference in question
* @return True if the reference has been excluded by one of the nodes in the stack, otherwise false
*/
public boolean isExcluded (IClassArtifactReference reference) {
for (int s=0; s<stack.size(); s++) {
IClassPackageDependencyReference node = stack.get(s);
for (int i=0; i<node.getExclusions().size(); i++) {
if (node.getExclusions().get(i).compareTo(reference) == 0) return true;
}
}
return false;
}
/**
* @param reference The reference for the new node
* @param descriptor The descriptor used for this, or null if not provided
* @return This for chaining purposes
*/
public ClassPackageResolverContext pushNode (IClassPackageDependencyReference reference, IClassPackageDescriptor descriptor) {
this.stack.add(0, reference);
this.descriptorStack.add(0, descriptor);
return this;
}
/**
* This will remove the top-most node from the stack.
*
* @return This for chaining purposes
*/
public ClassPackageResolverContext popNode () {
this.stack.remove(0);
this.descriptorStack.remove(0);
return this;
}
public List<ClassPackageResolverNetworkNode> getAvailable () {
List<ClassPackageResolverNetworkNode> nodes = new ArrayList<ClassPackageResolverNetworkNode>();
for (ClassPackageResolverNetworkNode node : index.values()) {
if (node.getType() != Type.Unavailable) nodes.add(node);
}
return nodes;
}
public List<ClassPackageResolverNetworkNode> getDownloadable () {
List<ClassPackageResolverNetworkNode> nodes = new ArrayList<ClassPackageResolverNetworkNode>();
for (ClassPackageResolverNetworkNode node : index.values()) {
if (node.getType() == Type.Downloadable) nodes.add(node);
}
return nodes;
}
public void remove (ClassPackageResolverNetworkNode node) {
index.remove(node.getReference().toArtifactString());
for (ClassPackageResolverNetworkNode dependency : node.getDependencies()) {
dependency.getRelationships().remove(node);
if (dependency.getRelationships().size() == 0) remove(dependency);
}
}
public void relate (ClassPackageResolverNetworkNode node) {
if (stack.size() <= 1) node.setPrimary(true);
else {
node.getRelationships().add( index.get(stack.get(1).toArtifactString()) );
index.get(stack.get(1).toArtifactString()).getDependencies().add(node);
}
}
public void setAvailable (IClassPackageResolver resolver, IClassPackageRepositoryLocal repository, ClassSource resolution, IClassPackageDescriptor descriptor) {
ClassPackageResolverNetworkNode node = new ClassPackageResolverNetworkNode(this, this.getCurrentDependency());
node.setType(Type.Available);
node.setResolution(resolution);
node.setDescriptor(descriptor);
this.register(node);
}
public void setDownloadable (IClassPackageResolver resolver, IClassPackageRepositoryLocalImportable local, IClassPackageRepositoryRemote remote) {
ClassPackageResolverNetworkNode node = new ClassPackageResolverNetworkNode(this, this.getCurrentDependency());
node.setType(Type.Downloadable);
node.setLocal(local);
node.setRemote(remote);
this.register(node);
}
public void setUnavailable () {
ClassPackageResolverNetworkNode node = new ClassPackageResolverNetworkNode(this, this.getCurrentDependency());
node.setType(Type.Unavailable);
this.register(node);
log.warning("Could not resolve: " + this.getCurrentDependency());
}
protected void register (ClassPackageResolverNetworkNode node) {
if (this.index.containsKey(node.getReference().toArtifactString()))
throw new ClassPackageException(node.getReference(), "For node: " + node.getReference() + "/" + node + " there is already an index: " +
this.index.get(node.getReference().toArtifactString()));
this.index.put(node.getReference().toArtifactString(), node);
this.relate(node);
}
}