blob: b92ed83ace45c700b89328c87237b967e5e7c46b [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.openejb.util;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @version $Rev: 1153797 $ $Date: 2011-08-04 02:09:44 -0700 (Thu, 04 Aug 2011) $
*/
public class References {
public static interface Visitor<T> {
String getName(T t);
Set<String> getReferences(T t);
}
public static <T> List<T> sort(List<T> objects, Visitor<T> visitor) {
if (objects.size() <= 1) {
return objects;
}
final Map<String, Node> nodes = new LinkedHashMap<String, Node>();
// Create nodes
for (T obj : objects) {
String name = visitor.getName(obj);
Node node = new Node(name, obj);
nodes.put(name, node);
}
// Link nodes
for (Node node : nodes.values()) {
for (String name : visitor.getReferences((T) node.object)) {
Node ref = nodes.get(name);
if (ref == null) throw new IllegalArgumentException("No such object in list: "+name);
node.references.add(ref);
node.initialReferences.add(ref);
}
}
boolean circuitFounded = false;
for (Node node : nodes.values()) {
Set<Node> visitedNodes = new HashSet<Node>();
if (!normalizeNodeReferences(node, node, visitedNodes)) {
circuitFounded = true;
break;
}
node.references.addAll(visitedNodes);
}
//detect circus
if (circuitFounded) {
Set<Circuit> circuits = new LinkedHashSet<Circuit>();
for (Node node : nodes.values()) {
findCircuits(circuits, node, new java.util.Stack<Node>());
}
ArrayList<Circuit> list = new ArrayList<Circuit>(circuits);
Collections.sort(list);
List<List> all = new ArrayList<List>();
for (Circuit circuit : list) {
all.add(unwrap(circuit.nodes));
}
throw new CircularReferencesException(all);
}
//Build Double Link Node List
Node rootNode = new Node(null, null);
rootNode.previous = rootNode;
rootNode.next = nodes.values().iterator().next();
for (Node node : nodes.values()) {
node.previous = rootNode.previous;
rootNode.previous.next = node;
node.next = rootNode;
rootNode.previous = node;
}
for (Node node : nodes.values()) {
for (Node reference : node.references) {
swap(node, reference, rootNode);
}
}
List sortedList= new ArrayList(nodes.size());
Node currentNode = rootNode.next;
while(currentNode != rootNode) {
sortedList.add(currentNode.object);
currentNode = currentNode.next;
}
return sortedList;
}
private static boolean normalizeNodeReferences(Node rootNode, Node node, Set<Node> referenceNodes) {
if (node.references.contains(rootNode)) {
return false;
}
for (Node reference : node.references) {
if (!referenceNodes.add(reference)) {
//this reference node has been visited in the past
continue;
}
if (!normalizeNodeReferences(rootNode, reference, referenceNodes)) {
return false;
}
}
return true;
}
private static void swap(Node shouldAfterNode, Node shouldBeforeNode, Node rootNode) {
Node currentNode = shouldBeforeNode;
while(currentNode.next != rootNode) {
if(currentNode.next == shouldAfterNode) {
return;
}
currentNode = currentNode.next;
}
//Remove the shouldAfterNode from list
shouldAfterNode.previous.next = shouldAfterNode.next;
shouldAfterNode.next.previous = shouldAfterNode.previous;
//Insert the node immediately after the shouldBeforeNode
shouldAfterNode.previous = shouldBeforeNode;
shouldAfterNode.next = shouldBeforeNode.next;
shouldBeforeNode.next = shouldAfterNode;
shouldAfterNode.next.previous = shouldAfterNode;
}
private static <T> List<T> unwrap(List<Node> nodes) {
ArrayList<T> referees = new ArrayList<T>(nodes.size());
for (Node node : nodes) {
referees.add((T) node.object);
}
return referees;
}
private static void findCircuits(Set<Circuit> circuits, Node node, java.util.Stack<Node> stack) {
if (stack.contains(node)) {
int fromIndex = stack.indexOf(node);
int toIndex = stack.size();
ArrayList<Node> circularity = new ArrayList<Node>(stack.subList(fromIndex, toIndex));
// add ending node to list so a full circuit is shown
circularity.add(node);
Circuit circuit = new Circuit(circularity);
circuits.add(circuit);
return;
}
stack.push(node);
for (Node reference : node.initialReferences) {
findCircuits(circuits, reference, stack);
}
stack.pop();
}
private static class Node implements Comparable<Node> {
private final String name;
private Object object;
private final List<Node> initialReferences = new ArrayList<Node>();
private final Set<Node> references = new HashSet<Node>();
private Node next;
private Node previous;
public Node(String name, Object object) {
this.name = name;
this.object = object;
}
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final Node node = (Node) o;
return name.equals(node.name);
}
public int hashCode() {
return name.hashCode();
}
public int compareTo(Node o) {
return this.name.compareTo(o.name);
}
public String toString() {
return name;
//return "Node("+ name +" : "+ Join.join(",", unwrap(initialReferences))+")";
}
}
private static class Circuit implements Comparable<Circuit> {
private final List<Node> nodes;
private final List<Node> atomic;
public Circuit(List<Node> nodes) {
this.nodes = nodes;
atomic = new ArrayList<Node>(nodes);
atomic.remove(atomic.size()-1);
Collections.sort(atomic);
}
public List<Node> getNodes() {
return nodes;
}
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final Circuit circuit = (Circuit) o;
if (!atomic.equals(circuit.atomic)) return false;
return true;
}
public int hashCode() {
return atomic.hashCode();
}
public int compareTo(Circuit o) {
int i = atomic.size() - o.atomic.size();
if (i != 0) return i;
Iterator<Node> iterA = atomic.listIterator();
Iterator<Node> iterB = o.atomic.listIterator();
while (iterA.hasNext() && iterB.hasNext()) {
Node a = iterA.next();
Node b = iterB.next();
i = a.compareTo(b);
if (i != 0) return i;
}
return 0;
}
public String toString() {
return "Circuit(" + Join.join(",", unwrap(nodes)) + ")";
}
}
}