org.databene.commons.depend.DependencyModel Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of databene-commons Show documentation
Show all versions of databene-commons Show documentation
'databene commons' is an open source Java library by Volker Bergmann.
It provides extensions to the Java core library by utility classes, abstract concepts
and concrete implementations.
/*
* Copyright (C) 2004-2015 Volker Bergmann ([email protected]).
* All rights reserved.
*
* Licensed 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.databene.commons.depend;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.databene.commons.depend.NodeState.*;
/**
* Orders objects by dependency.
* @param the type of the objects to process
* @author Volker Bergmann
* @since 0.3.04
*/
@SuppressWarnings("static-method")
public class DependencyModel> {
private static final Logger LOGGER = LoggerFactory.getLogger(DependencyModel.class);
private Map> nodeMappings;
public DependencyModel() {
this.nodeMappings = new HashMap>();
}
public void addNode(E object) {
nodeMappings.put(object, new Node(object));
}
public List dependencyOrderedObjects(boolean acceptingCycles) {
// set up dependencies
for (Node node : nodeMappings.values()) {
E subject = node.getSubject();
for (int i = 0; i < subject.countProviders(); i++) {
E provider = subject.getProvider(i);
Node providerNode = nodeMappings.get(provider);
if (providerNode == null)
throw new IllegalStateException("Node is not part of model: " + provider);
providerNode.addClient(node);
node.addProvider(providerNode, subject.requiresProvider(i));
}
}
// set up lists for processing
List> heads = new ArrayList>();
List> tails = new ArrayList>();
List> pending = new ArrayList>(nodeMappings.size());
List> orderedNodes = new ArrayList>(nodeMappings.size());
List> incompletes = new ArrayList>();
try {
// determine types and extract islands
Iterator> iterator = nodeMappings.values().iterator();
while (iterator.hasNext()) {
Node node = iterator.next();
if (node.hasForeignClients()) {
if (node.hasForeignProviders()) {
pending.add(node);
} else {
node.initialize();
heads.add(node);
}
} else {
if (node.hasForeignProviders()) {
tails.add(node);
} else {
node.initialize();
orderedNodes.add(node);
}
}
}
// extract heads
orderedNodes.addAll(heads);
// sort remaining nodes
while (pending.size() > 0) {
boolean found = extractNodes(pending, INITIALIZABLE, orderedNodes, null);
if (!found)
found = extractNodes(pending, PARTIALLY_INITIALIZABLE, orderedNodes, incompletes);
if (!found) {
if (acceptingCycles) {
// force one node
Node node = findForceable(pending);
LOGGER.debug("forcing " + node);
pending.remove(node);
node.force();
orderedNodes.add(node);
incompletes.add(node);
} else
throw new CyclicDependencyException("Cyclic dependency in " + pending);
}
postProcessNodes(incompletes);
}
if (incompletes.size() > 0)
throw new IllegalStateException("Incomplete nodes left: " + incompletes);
// extract tails
for (Node tail : tails)
tail.initialize();
orderedNodes.addAll(tails);
// done
if (LOGGER.isDebugEnabled())
LOGGER.debug("ordered to " + orderedNodes);
// map result
List result = new ArrayList(orderedNodes.size());
for (Node node : orderedNodes) {
E subject = node.getSubject();
if (node.getState() != INITIALIZED)
throw new IllegalStateException("Node '" + subject
+ "' is expected to be in INITIALIZED state, found: " + node.getState());
result.add(subject);
}
return result;
} catch (RuntimeException e) {
if (!(e instanceof CyclicDependencyException))
logState(pending);
throw e;
}
}
private void postProcessNodes(List> nodes) {
LOGGER.debug("post processing nodes: {}", nodes);
Iterator> iterator = nodes.iterator();
while (iterator.hasNext()) {
Node node = iterator.next();
switch (node.getState()) {
case PARTIALLY_INITIALIZABLE: LOGGER.debug("Initializing {} partially", node);
node.initializePartially();
break;
case INITIALIZED: LOGGER.debug("Initializing {} partially", node);
node.initializePartially();
break;
case INITIALIZABLE: LOGGER.debug("Initializing {}", node);
node.initialize();
iterator.remove();
break;
default: break;
}
}
}
private boolean extractNodes(List> source, NodeState requiredState, List> target, List> incompletes) {
LOGGER.debug("extracting nodes from {}", source);
Iterator> iterator;
boolean found = false;
iterator = source.iterator();
while (iterator.hasNext()) {
Node node = iterator.next();
if (node.getState() == requiredState) {
iterator.remove();
switch (requiredState) {
case INITIALIZABLE: LOGGER.debug("Initializing {}", node);
node.initialize();
break;
case PARTIALLY_INITIALIZABLE: LOGGER.debug("Initializing {} partially", node);
node.initializePartially();
if (incompletes != null)
incompletes.add(node);
break;
default: throw new IllegalArgumentException("state not supported: " + requiredState);
}
if (target != null)
target.add(node);
found = true;
}
}
return found;
}
private void logState(List> intermediates) {
LOGGER.error(intermediates.size() + " unresolved intermediates on DependencyModel error: ");
for (Node node : intermediates)
LOGGER.error(node.toString());
}
private Node findForceable(List> candidates) {
for (Node candidate : candidates)
if (candidate.getState() == FORCEABLE)
return candidate;
return candidates.get(0);
}
/*
private Node breakCycle(List> remainingNodes, List> availableNodes, boolean hard) {
for (Node node : remainingNodes) {
if (available(node))
logger.warn("Cyclic dependency in " + tmpList + ". Breaking cycle by extracting " + node);
result.add(node);
tmpList.remove(0);
}
}
private Node findAvailableNode(List> tmpList, List> availableNodes) {
for (Node node : tmpList)
if (available(node, availableNodes))
return node;
return null;
}
private boolean available(Node candidate, List> availableNodes) {
for (Node usedNode : candidate.getProviders())
if (!availableNodes.contains(usedNode))
return false;
return true;
}
*/
}