| /* |
| * |
| * 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); |
| } |
| } |
| } |