/* Copyright (C) 2009 Mobile Sorcery AB This program is free software; you can redistribute it and/or modify it under the terms of the Eclipse Public License v1.0. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Eclipse Public License v1.0 for more details. You should have received a copy of the Eclipse Public License v1.0 along with this program. It is also available at http://www.eclipse.org/legal/epl-v10.html */ package com.mobilesorcery.sdk.internal.dependencies; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.core.runtime.CoreException; import com.mobilesorcery.sdk.core.CoreMoSyncPlugin; public class DependencyManager<T> { /** * Represents a delta of dependencies. * @see {@link #applyDelta(com.mobilesorcery.sdk.internal.dependencies.DependencyManager.Delta)} * * @param <T> */ public static class Delta<T> extends DependencyManager<T> { Delta() { super(); } } public static final int DEPTH_INFINITE = Integer.MAX_VALUE; private HashMap<T, HashSet<T>> dependencyMap = new HashMap<T, HashSet<T>>(); private HashMap<T, HashSet<T>> reverseDependencyMap = new HashMap<T, HashSet<T>>(); private final Set<T> emptySet = Collections.unmodifiableSet(new HashSet<T>()); public List<T> computeDependenciesOf(T obj) throws CoreException { Set<T> result = getDependenciesOf(obj); if (result == null) { return null; } return new ArrayList<T>(result); } public Set<T> getAllDependees() { return dependencyMap.keySet(); } public Set<T> getDependenciesOf(T obj) { Set<T> result = dependencyMap.get(obj); result = result == null ? emptySet : result; return new HashSet<T>(result); } public Set<T> getDependenciesOf(Collection<T> list) { HashSet<T> result = new HashSet<T>(); for (T obj : list) { HashSet<T> intermediateResult = dependencyMap.get(obj); if (intermediateResult != null) { result.addAll(intermediateResult); } } return result; } public Set<T> getReverseDependenciesOf(T obj) { HashSet<T> result = reverseDependencyMap.get(obj); return result == null ? emptySet : result; } public void addDependency(T from, T to) { if (from != null && to != null) { Set<T> dependencies = lazyInit(dependencyMap, from, new HashSet<T>()); Set<T> reverseDependencies = lazyInit(reverseDependencyMap, to, new HashSet<T>()); dependencies.add(to); reverseDependencies.add(from); } } public void addDependencies(T from, Collection<T> toList) { for (T to : toList) { addDependency(from, to); } } public void removeDependency(T from, T to) { Set<T> dependencies = lazyInit(dependencyMap, from, new HashSet<T>()); Set<T> reverseDependencies = lazyInit(reverseDependencyMap, to, new HashSet<T>()); dependencies.remove(to); reverseDependencies.remove(from); } public void clearDependencies(Collection<T> objs) { for (T obj : objs) { clearDependencies(obj); } } /** * Clears all dependencies for a given object. * If B depends on A, then clearDependencies(A) * will remove this dependency. The reverse is not true, * so if A depends on B the dependency is kept. * That is, only outgoing edges are cleared; incoming edges * are kept. * @param obj */ public void clearDependencies(T obj) { Set<T> dependencies = new HashSet<T>(getDependenciesOf(obj)); for (T dependency : dependencies) { removeDependency(obj, dependency); } dependencyMap.remove(obj); } public void setDependencies(T from, Collection<T> toList) { clearDependencies(from); addDependencies(from, toList); } public void addDependencies(T from, IDependencyProvider<T> provider) throws CoreException { Map<T, Collection<T>> dependencies = provider.computeDependenciesOf(from); if (CoreMoSyncPlugin.getDefault().isDebugging()) { CoreMoSyncPlugin.trace("Setting dependencies of {0} to {1}", from, dependencies); } for (T dependency : dependencies.keySet()) { addDependencies(dependency, dependencies.get(dependency)); } } public void addDependencies(Collection<T> fromList, IDependencyProvider<T> provider) throws CoreException { for (T from : fromList) { addDependencies(from, provider); } } /*private void setDependencies(Collection<T> fromList, IDependencyProvider<T> provider) throws CoreException { for (T from : fromList) { setDependencies(from, provider); } }*/ private <K, V> V lazyInit(Map<K, V> map, K key, V valueIfNoKey) { V value = map.get(key); if (value == null) { value = valueIfNoKey; map.put(key, value); } return value; } public Set<T> getReverseDependenciesOf(T obj, int depth) { HashSet<T> result = new HashSet<T>(); innerGetReverseDependenciesOf(obj, depth, result, new HashSet<T>()); return result; } public Set<T> getReverseDependenciesOf(List<T> objs, int depth) { HashSet<T> result = new HashSet<T>(); for (T obj : objs) { innerGetReverseDependenciesOf(obj, depth, result, new HashSet<T>()); } return result; } public void clear() { dependencyMap.clear(); reverseDependencyMap.clear(); } /** * Create a new delta to be used as a working copy * by clients. * @return */ public Delta<T> createDelta() { return new Delta<T>(); } /** * <p>Applies all dependencies represented by <code>delta</code> to this manager.</p> * <p>If the delta has a defined dependency for an object, then those dependencies will * be cleared and replaced with the delta's value.</p> * @param delta */ public void applyDelta(DependencyManager.Delta<T> delta) { for (T dependee : delta.getAllDependees()) { setDependencies(dependee, delta.getDependenciesOf(dependee)); } } private void innerGetReverseDependenciesOf(T obj, int depth, Set<T> result, Set<T> alreadyProcessed) { if (depth <= 0) { return; } Set<T> reverseDependencies = new HashSet<T>(getReverseDependenciesOf(obj)); result.addAll(reverseDependencies); for (T reverseDependency : reverseDependencies) { if (!alreadyProcessed.contains(reverseDependency)) { alreadyProcessed.add(reverseDependency); innerGetReverseDependenciesOf(reverseDependency, depth - 1, result, alreadyProcessed); } } } public String toString() { // For debugging. StringBuffer result = new StringBuffer(); for (T dependency : dependencyMap.keySet()) { result.append(dependency); result.append(" --> "); result.append(getDependenciesOf(dependency)); result.append("\n"); } return result.toString(); } }