/* * Copyright (c) 2012 EMC Corporation * All Rights Reserved */ package com.emc.storageos.db.common; import com.emc.storageos.db.client.impl.ColumnField; import com.emc.storageos.db.client.model.DataObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.*; /** * Class for building/keeping all the dependency information for different DataObjects in DB */ public class DependencyTracker { private static final Logger log = LoggerFactory.getLogger(DependencyTracker.class); public class Dependency { private Class<? extends DataObject> _type; private ColumnField _field; public Dependency(Class<? extends DataObject> type, ColumnField field) { _type = type; _field = field; } public Class<? extends DataObject> getType() { return _type; } public ColumnField getColumnField() { return _field; } } private Map<Class<? extends DataObject>, List<Dependency>> _dependencyMap; private List<Class<? extends DataObject>> _excluded; private Map<Integer, List<Class<? extends DataObject>>> _levels; public DependencyTracker() { _dependencyMap = new HashMap<Class<? extends DataObject>, List<Dependency>>(); _excluded = new ArrayList<Class<? extends DataObject>>(); } public void addDependency(Class<? extends DataObject> refType, Class<? extends DataObject> onType, ColumnField field) { if (_excluded.contains(refType)) { return; } if (!_dependencyMap.containsKey(refType)) { _dependencyMap.put(refType, new ArrayList<Dependency>()); } _dependencyMap.get(refType).add(new Dependency(onType, field)); } public void includeClass(Class<? extends DataObject> refType) { if (!_dependencyMap.containsKey(refType)) { _dependencyMap.put(refType, new ArrayList<Dependency>()); } } public void excludeClass(Class<? extends DataObject> refType) { if (_dependencyMap.containsKey(refType)) { _dependencyMap.remove(refType); } _excluded.add(refType); } public List<Class<? extends DataObject>> getExcludedTypes() { return Collections.unmodifiableList(_excluded); } public List<Dependency> getDependencies(Class<? extends DataObject> clazz) { return _dependencyMap.containsKey(clazz) ? Collections.unmodifiableList(_dependencyMap.get(clazz)) : new ArrayList<Dependency>(); } public void buildDependencyLevels() { _levels = new HashMap<Integer, List<Class<? extends DataObject>>>(); Integer level = 0; List<Class<? extends DataObject>> visited = new ArrayList(); Set<Class<? extends DataObject>> all = new HashSet(_dependencyMap.keySet()); while (all.size() != visited.size()) { _levels.put(level, new ArrayList<Class<? extends DataObject>>()); for (Class<? extends DataObject> entry : all) { if (visited.contains(entry)) { continue; // already handled } List<Dependency> dependencies = _dependencyMap.get(entry); // first level - leaf nodes, should not have dependents if (level == 0 && !dependencies.isEmpty()) { continue; } boolean addDependency = true; for (Dependency dependency : dependencies) { if (visited.contains(dependency.getType()) && !_levels.get(level).contains(dependency.getType())) { // visited, but, not on the same level - satisfied dependency continue; } else if (dependency.getType().equals(entry) || _excluded.contains(dependency.getType())) { // special cases - satisfied dependency // 1. self reference - only true for TenantOrg for now, // since the root tenant is never deleted, it can go on one level // 2. reference to excluded type continue; } else { // else - missing dependency, I am not on this level addDependency = false; break; } } // we are here, check if we missed any dependencies if (addDependency) { _levels.get(level).add(entry); visited.add(entry); } } assert (!_levels.get(level).isEmpty()); level++; } } public int getLevels() { return _levels.keySet().size(); } public List<Class<? extends DataObject>> getTypesInLevel(int level) { return (_levels.containsKey(level)) ? Collections.unmodifiableList(_levels.get(level)) : new ArrayList<Class<? extends DataObject>>(); } @Override public String toString() { Iterator<Map.Entry<Class<? extends DataObject>, List<Dependency>>> iterator = _dependencyMap.entrySet().iterator(); StringBuilder str = new StringBuilder(); str.append("\n"); while (iterator.hasNext()) { Map.Entry<Class<? extends DataObject>, List<Dependency>> entry = iterator.next(); str.append(entry.getKey().getSimpleName() + " depends on: "); if (entry.getValue().isEmpty()) { str.append("None"); } else { for (Iterator<Dependency> it2 = entry.getValue().iterator(); it2.hasNext();) { Dependency dependency = it2.next(); str.append("\n\t" + dependency.getType().getSimpleName() + ":" + dependency.getColumnField().getName()); } } str.append("\n"); } str.append("\n"); Iterator<Map.Entry<Integer, List<Class<? extends DataObject>>>> iterator2 = _levels.entrySet().iterator(); while (iterator2.hasNext()) { Map.Entry<Integer, List<Class<? extends DataObject>>> levelEntry = iterator2.next(); str.append("Level " + levelEntry.getKey().toString()); str.append("\n"); for (Iterator<Class<? extends DataObject>> clazzIt = levelEntry.getValue().iterator(); clazzIt.hasNext();) { str.append("\n\t").append(clazzIt.next().getSimpleName()); } str.append("\n\n"); } return str.toString(); } }