package com.constellio.model.utils; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import com.constellio.model.entities.records.Record; import com.constellio.model.entities.schemas.Metadata; import com.constellio.model.entities.schemas.MetadataSchema; import com.constellio.model.entities.schemas.MetadataSchemaTypes; import com.constellio.model.entities.schemas.MetadataValueType; import org.apache.commons.collections.map.HashedMap; public class DependencyUtils<V> { public boolean hasCyclicDependencies(Map<V, Set<V>> dependenciesMap) { try { validateNoCyclicDependencies(dependenciesMap); return false; } catch (DependencyUtilsRuntimeException.CyclicDependency e) { return true; } } public void validateNoCyclicDependencies(Map<V, Set<V>> dependenciesMap) { sortByDependency(dependenciesMap, new Comparator<V>() { @Override public int compare(V o1, V o2) { return 0; } }); } public List<V> sortByDependency(Map<V, Set<V>> dependenciesMap, Comparator<V> tieComparator) { return sortByDependency(dependenciesMap, new DependencyUtilsParams().sortTieUsing(tieComparator)); } public List<V> sortByDependency(Map<V, Set<V>> dependenciesMap) { return sortByDependency(dependenciesMap, new DependencyUtilsParams().sortUsingDefaultComparator()); } public List<V> sortByDependencyWithoutTieSort(Map<V, Set<V>> dependenciesMap) { return sortByDependency(dependenciesMap, new DependencyUtilsParams()); } public List<V> sortByDependency(Map<V, Set<V>> dependenciesMap, DependencyUtilsParams params) { Map<V, Set<V>> dependenciesMapCopy = copyInModifiableMap(dependenciesMap); removeSelfDependencies(dependenciesMapCopy); removeDependenciesToOtherElements(dependenciesMapCopy); List<V> sortedElements = new ArrayList<>(); while (!dependenciesMapCopy.isEmpty()) { IterationResults<V> iterationResults = getMetadatasWithoutDependencies(dependenciesMapCopy, params); if (iterationResults.valuesWithoutDependencies.isEmpty()) { List<V> cycleElements = getCyclicElements(dependenciesMapCopy.keySet()); throw new DependencyUtilsRuntimeException.CyclicDependency(cycleElements); } else { if (params.isSortTie()) { Collections.sort(iterationResults.valuesWithoutDependencies, params.<V>getTieComparator()); } sortedElements.addAll(iterationResults.valuesWithoutDependencies); } dependenciesMapCopy = iterationResults.valuesWithDependencies; } return sortedElements; } private void removeSelfDependencies(Map<V, Set<V>> dependenciesMapCopy) { for (V key : dependenciesMapCopy.keySet()) { dependenciesMapCopy.get(key).remove(key); } } private void removeDependenciesToOtherElements(Map<V, Set<V>> dependenciesMapCopy) { for (V key : dependenciesMapCopy.keySet()) { Set<V> values = new HashSet<>(dependenciesMapCopy.get(key)); Iterator<V> valuesIterator = values.iterator(); while (valuesIterator.hasNext()) { if (!dependenciesMapCopy.keySet().contains(valuesIterator.next())) { valuesIterator.remove(); } } dependenciesMapCopy.put(key, values); } } private List<V> getCyclicElements(Set<V> keySet) { List<V> elements = new ArrayList<V>(); elements.addAll(keySet); return elements; } private Map<V, Set<V>> copyInModifiableMap(Map<V, Set<V>> metadatasWithLocalCodeDependencies) { Map<V, Set<V>> metadatas = new HashMap<>(); for (Map.Entry<V, Set<V>> entry : metadatasWithLocalCodeDependencies.entrySet()) { metadatas.put(entry.getKey(), new HashSet<V>(entry.getValue())); } return metadatas; } private IterationResults<V> getMetadatasWithoutDependencies(Map<V, Set<V>> metadatas, DependencyUtilsParams params) { List<V> valuesWithoutDependencies = new ArrayList<>(); Map<V, Set<V>> valuesWithDependencies = new HashMap<>(); for (Map.Entry<V, Set<V>> entry : metadatas.entrySet()) { if (entry.getValue().isEmpty()) { valuesWithoutDependencies.add(entry.getKey()); } else { valuesWithDependencies.put(entry.getKey(), entry.getValue()); } } for (V nextElement : valuesWithoutDependencies) { for (Map.Entry<V, Set<V>> otherMetadataEntry : valuesWithDependencies.entrySet()) { otherMetadataEntry.getValue().remove(nextElement); } } if (valuesWithoutDependencies.isEmpty() && !valuesWithDependencies.isEmpty() && params.isTolerateCyclicDependencies()) { return getMetadatasWithoutLesserDependencies(metadatas); } else { return new IterationResults<>(valuesWithoutDependencies, valuesWithDependencies); } } private IterationResults<V> getMetadatasWithoutLesserDependencies(Map<V, Set<V>> metadatas) { boolean moreDepenciesToAdd = true; while (moreDepenciesToAdd) { moreDepenciesToAdd = false; for (Map.Entry<V, Set<V>> entry : metadatas.entrySet()) { Set<V> valuesToAdd = new HashSet<>(); for (V value : entry.getValue()) { valuesToAdd.addAll(metadatas.get(value)); } valuesToAdd.add(entry.getKey()); int sizeBefore = entry.getValue().size(); entry.getValue().addAll(valuesToAdd); int sizeAfter = entry.getValue().size(); moreDepenciesToAdd |= sizeBefore != sizeAfter; } } int minCounterOfDependencies = 10000000; for (Map.Entry<V, Set<V>> entry : metadatas.entrySet()) { int currentValue = entry.getValue().size(); if (currentValue < minCounterOfDependencies) { minCounterOfDependencies = currentValue; } } List<V> valuesWithLesserDependencies = new ArrayList<>(); Map<V, Set<V>> valuesWithDependencies = new HashMap<>(); for (Map.Entry<V, Set<V>> entry : metadatas.entrySet()) { if (entry.getValue().size() == minCounterOfDependencies) { valuesWithLesserDependencies.add(entry.getKey()); } else { valuesWithDependencies.put(entry.getKey(), metadatas.get(entry.getKey())); } } for (V nextElement : valuesWithLesserDependencies) { for (Map.Entry<V, Set<V>> otherMetadataEntry : valuesWithDependencies.entrySet()) { otherMetadataEntry.getValue().remove(nextElement); } } return new IterationResults<>(valuesWithLesserDependencies, valuesWithDependencies); } private static class IterationResults<V> { private List<V> valuesWithoutDependencies; private Map<V, Set<V>> valuesWithDependencies; private IterationResults(List<V> valuesWithoutDependencies, Map<V, Set<V>> valuesWithDependencies) { this.valuesWithoutDependencies = valuesWithoutDependencies; this.valuesWithDependencies = valuesWithDependencies; } } }