blob: 03bd9cc64572dada7c8f6af5b08aa1fccfee294b [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.flex.abc;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.HashSet;
import java.util.Set;
import org.apache.flex.abc.semantics.InstanceInfo;
import org.apache.flex.abc.semantics.Name;
/**
* ClassDependencySort contains data structures and algorithms to sort a list of
* classes into dependency order.
*/
public abstract class ClassDependencySort
{
/**
* The objects being sorted need to show an InstanceInfo.
*/
public static interface IInstanceInfoProvider
{
public InstanceInfo getInstanceInfo();
}
/**
* Perform a topological sort by inheritance.
*
* @return a Collection of classes, sorted so that classes appear before
* their superclasses or inherited interfaces.
* @throws IllegalStateException if there are circular dependencies.
*/
public static <T extends IInstanceInfoProvider> Collection<T> getSorted(Collection<T> classes)
{
ArrayList<T> sortedClasses = new ArrayList<T>();
// First establish dependency relationships between classes.
ArrayList<DependencyTracker<T>> workingSet = new ArrayList<DependencyTracker<T>>();
for (T raw_x : classes)
{
DependencyTracker<T> x = new DependencyTracker<T>(raw_x);
for (DependencyTracker<T> y : workingSet)
x.establishDependency(y);
workingSet.add(x);
}
// Perform a simple set-oriented topological sort:
// yield any class that has no dependencies,remove
// that class from its dependents' dependencies,
// iterate until done.
boolean classProcessed;
do
{
classProcessed = false;
Iterator<DependencyTracker<T>> it = workingSet.iterator();
while (it.hasNext())
{
DependencyTracker<T> n = it.next();
if (n.dependencies.isEmpty())
{
classProcessed = true;
sortedClasses.add(n.payload);
// Remove this dependency from dependents' graphs.
for (DependencyTracker<T> dep : n.dependents)
dep.dependencies.remove(n);
it.remove();
}
}
}
while (classProcessed);
if (!workingSet.isEmpty())
throw new IllegalStateException("cycle in class dependency graph");
return sortedClasses;
}
/**
* Track the dependency relationships of an object being sorted.
*/
static class DependencyTracker<T extends IInstanceInfoProvider>
{
/**
* @param payload - the object to be sorted.
*/
DependencyTracker(T payload)
{
this.payload = payload;
}
/**
* The object being sorted.
*/
T payload;
/**
* The set of objects that depend on this.payload
*/
Set<DependencyTracker<T>> dependents = new HashSet<DependencyTracker<T>>();
/**
* The set of objects that this.payload depends upon.
*/
Set<DependencyTracker<T>> dependencies = new HashSet<DependencyTracker<T>>();
/**
* Establish dependency relationships between this node and another.
*/
void establishDependency(DependencyTracker<T> other)
{
InstanceInfo myInstance = this.payload.getInstanceInfo();
InstanceInfo otherInstance = other.payload.getInstanceInfo();
if (myInstance.superName != null && myInstance.superName.equals(otherInstance.name))
this.makeDependentOf(other);
else if (otherInstance.superName != null && otherInstance.superName.equals(myInstance.name))
other.makeDependentOf(this);
for (Name myInterface : myInstance.interfaceNames)
{
if (myInterface != null && myInterface.equals(otherInstance.name))
this.makeDependentOf(other);
}
for (Name otherInterface : otherInstance.interfaceNames)
{
if (otherInterface.equals(myInstance.name))
other.makeDependentOf(this);
}
if (this.dependencies.contains(other) && other.dependencies.contains(this))
{
throw new IllegalStateException("simple cycle in dependency graph");
}
}
/**
* Note that another object is dependent on this.payload.
*
* @param other - the DependencyTracker tracking the other object.
*/
void makeDependentOf(DependencyTracker<T> other)
{
assert other != this;
this.dependencies.add(other);
other.dependents.add(this);
}
}
}