All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.databene.commons.depend.DependencyModel Maven / Gradle / Ivy

/*
 * Copyright (C) 2004-2014 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.
 * @author Volker Bergmann
 * @since 0.3.04
 * @param 
 */
@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;
    }
*/
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy