blob: 766fd8b536a2927ca4cb8a8e5949b8cece6fc706 [file] [log] [blame]
/*
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.royale.compiler.internal.projects;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.apache.royale.compiler.common.DependencyType;
import org.apache.royale.compiler.common.DependencyTypeSet;
import org.apache.royale.compiler.exceptions.CircularDependencyException;
import org.apache.royale.compiler.exceptions.LibraryCircularDependencyException;
import org.apache.royale.compiler.internal.graph.Graph;
import org.apache.royale.compiler.internal.graph.GraphEdge;
import org.apache.royale.compiler.internal.graph.TopologicalSort;
import org.apache.royale.compiler.units.ICompilationUnit;
/**
* A dependency graph of libraries. Each vertex on the graph represents a
* library. Each outgoing edge represents a set of classes that cause the "from"
* vertex to be dependent on the "to" vertex.
*/
public class LibraryDependencyGraph
{
/**
* Class to hold information about an edge in the LibraryDependencyGraph.
*
*/
static final class Edge extends GraphEdge<String> implements Comparable<Edge>
{
private Map<String, DependencyTypeSet> dependencies;
private DependencyTypeSet dependencySet;
/**
* @param referencingLibrary
* @param declaringLibrary
*/
private Edge(String referencingLibrary, String declaringLibrary)
{
super(referencingLibrary, declaringLibrary);
this.dependencySet = DependencyTypeSet.noneOf();
this.dependencies = new HashMap<String, DependencyTypeSet>();
}
/**
*/
public boolean getIsInheritanceDependency()
{
return dependencySet.contains(DependencyType.INHERITANCE);
}
/**
*/
public boolean getIsSignatureDependency()
{
return dependencySet.contains(DependencyType.SIGNATURE);
}
/**
*/
public boolean getIsNamespaceDependency()
{
return dependencySet.contains(DependencyType.NAMESPACE);
}
/**
*/
public boolean getIsExpressionDependency()
{
return dependencySet.contains(DependencyType.EXPRESSION);
}
/**
* @param set A set of dependencies
* @return True if any of the union of the parameter set and this Edge's dependencySet is non-null.
*/
public boolean typeInSet(DependencyTypeSet set)
{
for (DependencyType t : set)
{
if (dependencySet.contains(t))
return true;
}
return false;
}
/**
* Adds a dependency of a set of {@link DependencyType} on a definition
* with qname to this Edge.
*
* @param qname The definition qualified name that is depended on
* @param types {@link DependencyType}'s to add to this edge.
*/
public void addDependency(String qname, DependencyTypeSet types)
{
DependencyTypeSet typeSet = dependencies.get(qname);
if (typeSet != null)
{
DependencyTypeSet newTypeSet = DependencyTypeSet.copyOf(typeSet);
newTypeSet.addAll(types);
this.dependencies.put(qname, newTypeSet);
}
else
{
this.dependencies.put(qname, types);
}
dependencySet.addAll(types);
}
/**
* Adds an anonymous dependency of a {@link DependencyType} on a definition
* to this Edge.
* @param type {@link DependencyType}'s to add to this edge.
*/
public void addDependency(DependencyType type)
{
dependencySet.add(type);
}
// Adding toString method for debugging.
@Override
public String toString()
{
String result = getFrom() + " -> " + getTo() + " [ ";
if (getIsInheritanceDependency())
result += "inheritance ";
if (getIsSignatureDependency())
result += "signature ";
if (getIsNamespaceDependency())
result += "namespace ";
if (getIsExpressionDependency())
result += "expression ";
result += "]";
return result;
}
/**
* @return A map of all named dependee qnames of this edge to the
* {@link DependencyType} that they depend on.
*/
public Map<String, DependencyTypeSet> getNamedDependencies()
{
return this.dependencies;
}
@Override
public int compareTo(Edge edge2)
{
int fromCompare = getTo().compareTo(edge2.getTo());
if (fromCompare == 0)
{
return getTo().compareTo(edge2.getTo());
}
else
{
return fromCompare;
}
}
public DependencyTypeSet getAllDependencies()
{
return dependencySet;
}
}
/**
* Create an empty library dependency graph.
*/
public LibraryDependencyGraph()
{
graph = new Graph<String, Edge>();
lock = new ReentrantReadWriteLock();
}
private final Graph<String, Edge> graph;
private final ReadWriteLock lock;
/**
* Adds a dependency to the dependency graph.
*
* @param depender The absolute path name of the library containing a
* reference to a definition defined by the other library.
* @param dependee The absolute path name of the library with a definition referred to by
* the other library.
*/
public void addDependency(String depender,
String dependee,
Map<String, DependencyTypeSet> dependencies)
{
lock.writeLock().lock();
try
{
Edge e = getEdge(depender, dependee);
for (Map.Entry<String, DependencyTypeSet> entry : dependencies.entrySet())
{
e.addDependency(entry.getKey(), entry.getValue());
}
}
finally
{
lock.writeLock().unlock();
}
}
/**
* Add a {@link ICompilationUnit} to the dependency graph.
*/
public void addLibrary(String library)
{
// no need to grab the lock, as this should only ever be called from
// a single thread at any given time.
graph.addVertex(library);
}
/**
* Get the edge between two given libraries.
*/
private Edge getEdge(String dependentLibrary, String dependeeLibrary)
{
Edge result = graph.getEdge(dependentLibrary, dependeeLibrary);
if (result == null)
{
result = new Edge(dependentLibrary, dependeeLibrary);
graph.setEdge(result);
}
return result;
}
/**
* @param library An {@link ICompilationUnit} to be checked
* @return True if the {@link ICompilationUnit} unit exists in this {@link DependencyGraph}
*/
public boolean contains(String library)
{
return graph.getVertices().contains(library);
}
/**
* Finds the named dependencies between two compilation units.
*
* @param from The depender {@link ICompilationUnit}
* @param to The dependee {@link ICompilationUnit}
* @return A copied non-synchronous {@link Map} from definition
* qname to a {@link DependencyTypeSet}, representing the
* dependencies between two {@link ICompilationUnit}s.
*/
public Map<String, DependencyTypeSet> getDependencySet(String from, String to)
{
return new HashMap<String, DependencyTypeSet>(getEdge(from, to).getNamedDependencies());
}
/**
* Finds the dependencies between two compilation units.
*
* @param from The depender {@link ICompilationUnit}
* @param to The dependee {@link ICompilationUnit}
* @return A copy of a {@link DependencyTypeSet} that is active
* between the two compilation units
*/
public DependencyTypeSet getDependencyTypes(String from, String to)
{
return DependencyTypeSet.copyOf(getEdge(from, to).getAllDependencies());
}
public List<String> getDependencyOrder() throws LibraryCircularDependencyException
{
// Sort the dependency tree of swcs.
List<String> sortedSWCs;
try
{
sortedSWCs = TopologicalSort.sort(graph,
new Comparator<String>()
{
@Override
public int compare(String o1, String o2)
{
return o1.compareTo(o2);
}
});
}
catch (CircularDependencyException e)
{
// Rethrow the exception as a LibraryCircularDependencyException.
List<String> circularDependency = new ArrayList<String>(e.getCircularDependency().size());
for (Object dependency : e.getCircularDependency())
{
circularDependency.add((String)dependency);
}
throw new LibraryCircularDependencyException("The libraries contain a circular dependency.",
circularDependency);
}
return sortedSWCs;
}
/**
* Get the set of all libraries a given library is dependent on.
*
* @param libraryPath The absolute path of a library
* @return A set of libraries that <code>libraryPath</code> depends on.
* Each {@link String} in the set is the absolute path of a library.
*/
public Set<String> getDependencies(String libraryPath)
{
Set<String> libraries = new HashSet<String>();
// Get the outgoing nodes of libraries.
Set<Edge> edges = graph.getOutgoingEdges(libraryPath);
for (Edge edge : edges)
{
libraries.add(edge.getTo());
}
return libraries;
}
}