/* * Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Nuxeo - initial API and implementation * * $Id$ */ package org.eclipse.ecr.runtime.deploy; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Vector; /** * * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> * TODO this was copied from nuxeo.commons and fixed - should put it back with all modifs */ // TODO handle dependencies cycles. public class DependencyTree<K, T> implements Iterable<DependencyTree.Entry<K, T>> { private final Map<K, Entry<K, T>> registry; // the sorted list of resolved entries. // given an element e from that list it is ensured that any element at the left // of 'e' doesn't depends on it private final List<Entry<K, T>> resolved; public DependencyTree() { registry = new Hashtable<K, Entry<K, T>>(); resolved = new Vector<Entry<K, T>>(); } @Override public Iterator<Entry<K, T>> iterator() { return registry.values().iterator(); } public Entry<K,T> add(K key, T object, K ... requires) { return add(key, object, Arrays.asList(requires)); } public Entry<K,T> add(K key, T object, Collection<K> requires) { Entry<K, T> entry = registry.get(key); if (entry == null) { entry = new Entry<K, T>(key, object); registry.put(key, entry); } else if (entry.object == null) { entry.object = object; } else { //TODO object already exists return entry; } updateDependencies(entry, requires); registered(entry); // resolve it if no pending requirements if (entry.canEnterResolvedState()) { resolve(entry); } return entry; } public void add(K key, T object) { add(key, object, (Collection<K>) null); } public void remove(K key) { Entry<K, T> entry = registry.get(key); // do not remove entry if (entry != null) { unregister(entry); entry.object = null; entry.waitsFor = null; } } public void unregister(Entry <K, T> entry) { if (entry.isResolved()) { unresolve(entry); } unregistered(entry); } public Entry<K, T> getEntry(K key) { return registry.get(key); } public T get(K key) { Entry<K, T> entry = registry.get(key); return entry != null ? entry.object : null; } public T getResolved(K key) { Entry<K, T> entry = registry.get(key); return entry != null && entry.isResolved() ? entry.object : null; } private void resolveEntry(Entry<K, T> entry) { //synchronize () { resolved.add(entry); entry.isResolved = true; //} // notify listener resolved(entry); } private void unresolveEntry(Entry<K, T> entry) { //synchronize () { resolved.remove(entry); entry.isResolved = false; //} unresolved(entry); } public void resolve(Entry<K, T> entry) { resolveEntry(entry); // resolve any dependent entry if they are waiting only for me Set<Entry<K, T>> deps = entry.getDependsOnMe(); if (deps != null) { for (Entry<K, T> dep : deps) { dep.removeWaitingFor(entry); if (dep.canEnterResolvedState()) { resolve(dep); // resolve the dependent entry } } } } public void unresolve(Entry<K, T> entry) { // unresolve any dependent object Set<Entry<K, T>> deps = entry.getDependsOnMe(); if (deps != null) { for (Entry<K, T> dep : deps) { dep.addWaitingFor(entry); if (dep.isResolved()) { unresolve(dep); // unresolve the dependent entry } } } unresolveEntry(entry); } public boolean isPhantom(K key) { Entry<K, T> entry = registry.get(key); return entry != null && entry.isPhantom(); } public boolean isRegistered(K key) { Entry<K, T> entry = registry.get(key); return entry != null && entry.isRegistered(); } public boolean isResolved(K key) { Entry<K, T> entry = registry.get(key); return entry != null && entry.isResolved(); } public Collection<Entry<K, T>> getEntries() { return registry.values(); } public List<T> getRegisteredObjects() { List<T> list = new ArrayList<T>(); Collection<Entry<K, T>> entries = getEntries(); for (Entry<K, T> entry : entries) { list.add(entry.object); } return list; } public List<Entry<K, T>> getPendingEntries() { List<Entry<K, T>> result = new ArrayList<Entry<K, T>>(); for (Map.Entry<K, Entry<K, T>> entry : registry.entrySet()) { Entry<K, T> val = entry.getValue(); if (!val.isResolved()) { result.add(val); } } return result; } public List<T> getPendingObjects() { List<T> list = new ArrayList<T>(); List<Entry<K, T>> entries = getPendingEntries(); for (Entry<K, T> entry : entries) { list.add(entry.object); } return list; } public List<Entry<K, T>> getMissingRequirements() { List<Entry<K, T>> result = new ArrayList<Entry<K, T>>(); for (Map.Entry<K, Entry<K, T>> entry : registry.entrySet()) { Entry<K, T> val = entry.getValue(); if (!val.isRegistered()) { result.add(val); } } return result; } /** * Entries are sorted so an entry never depends on entries on its right. */ public List<Entry<K, T>> getResolvedEntries() { return resolved; } public List<T> getResolvedObjects() { List<T> list = new ArrayList<T>(); List<Entry<K, T>> entries = resolved; for (Entry<K, T> entry : entries) { list.add(entry.object); } return list; } public void clear() { for (Entry<K, T> entry : resolved) { entry = registry.remove(entry.key); if (entry != null) { entry.isResolved = false; unresolved(entry); unregistered(entry); } } resolved.clear(); Iterator<Entry<K, T>> it = registry.values().iterator(); while (it.hasNext()) { Entry<K, T> entry = it.next(); it.remove(); if (entry.isRegistered()) { unregistered(entry); } } } protected void updateDependencies(Entry<K, T> entry, Collection<K> requires) { if (requires != null) { for (K req : requires) { Entry<K, T> reqEntry = registry.get(req); if (reqEntry != null) { if (reqEntry.isResolved()) { // requirement satisfied -> continue reqEntry.addDependsOnMe(entry); continue; } } else { reqEntry = new Entry<K, T>(req, null); // placeholder entry registry.put(req, reqEntry); } // dependencies not satisfied reqEntry.addDependsOnMe(entry); entry.addWaitingFor(reqEntry); } } } protected void registered(Entry<K, T> entry) { } protected void unregistered(Entry<K, T> entry) { } protected void resolved(Entry<K, T> entry) { } protected void unresolved(Entry<K, T> entry) { } public static final int PHANTOM = 0; public static final int REGISTERED = 1; public static final int RESOLVED = 3; public static class Entry<K, T> { private final K key; private T object; private Set<Entry<K, T>> waitsFor; private Set<Entry<K, T>> dependsOnMe; private boolean isResolved = false; public Entry(K key, T object) { this.key = key; this.object = object; } public boolean isPhantom() { return object == null; } public boolean isRegistered() { return object != null; } public boolean isResolved() { return isResolved; } public final boolean canEnterResolvedState() { return !isResolved && object != null && waitsFor == null; } public final void addWaitingFor(Entry<K, T> entry) { if (waitsFor == null) { waitsFor = new HashSet<Entry<K, T>>(); } waitsFor.add(entry); } public final void removeWaitingFor(Entry<K, T> key) { if (waitsFor != null) { waitsFor.remove(key); if (waitsFor.isEmpty()) { waitsFor = null; } } } public final void addDependsOnMe(Entry<K, T> entry) { if (dependsOnMe == null) { dependsOnMe = new HashSet<Entry<K, T>>(); } dependsOnMe.add(entry); } public Set<Entry<K, T>>getDependsOnMe() { return dependsOnMe; } public Set<Entry<K, T>> getWaitsFor() { return waitsFor; } public final T get() { return object; } public K getKey() { return key; } @Override public String toString() { return key.toString(); } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (obj == this) { return true; } if (!(obj instanceof Entry)) { return false; } return key.equals(((Entry<?,?>) obj).key); } @Override public int hashCode() { return key.hashCode(); } } }