package ecologylab.bigsemantics.metametadata; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import ecologylab.bigsemantics.metadata.Metadata; import ecologylab.generic.HashMapArrayList; /** * A class to hold dependency management of metametadata scalars for extraction. * @author twhite * */ public class ScalarDependencyManager { //TODO: Clean up all of the predicate logic with some Google Guava stuff. private HashMapArrayList<String, MetaMetadataField> fieldSet; public ScalarDependencyManager(HashMapArrayList<String, MetaMetadataField> fields) { this.fieldSet = fields; } private List<String> getNoIncomingNodes(HashMap<String, List<String>> list) { List<String> ourList = new ArrayList<String>(); for(String en : list.keySet()) { if(list.get(en).isEmpty()) { ourList.add(en); } } return ourList; } private HashMapArrayList<String, MetaMetadataScalarField> getScalars(HashMapArrayList<String, MetaMetadataField> fields) { HashMapArrayList<String, MetaMetadataScalarField> scalars = new HashMapArrayList<String, MetaMetadataScalarField>(); for(String s : fields.keySet()) { MetaMetadataField mmf = fields.get(s); if(mmf instanceof MetaMetadataScalarField) { scalars.put(s, (MetaMetadataScalarField)mmf); } } return scalars; } private HashMapArrayList<String, MetaMetadataField> getNonScalars(HashMapArrayList<String, MetaMetadataField> fields) { HashMapArrayList<String, MetaMetadataField> nonScalars = new HashMapArrayList<String, MetaMetadataField>(); for(String s : fields.keySet()) { MetaMetadataField mmf = fields.get(s); if(!(mmf instanceof MetaMetadataScalarField)) { nonScalars.put(s, mmf); } } return nonScalars; } /** * Attempts to perform a topological sort of the Fields to sort by dependnencies. * @param metadata The metadata that dependencies are connected to * @return A sorted fieldset (such that values which depend on other values are extracted /after/ their dependencies * @throws ScalarDependencyException If the dependencies have a cycle, will throw an exception. */ public HashMapArrayList<String, MetaMetadataField> sortFieldSetByDependencies(Metadata metadata) throws ScalarDependencyException { HashMapArrayList<String, MetaMetadataField> ourCopy = new HashMapArrayList<String, MetaMetadataField>(); ourCopy.putAll(fieldSet); HashMapArrayList<String, MetaMetadataScalarField> ourScalars = getScalars(ourCopy); HashMapArrayList<String, MetaMetadataField> ourNonScalars = getNonScalars(ourCopy); HashMap<String, List<String>> incomingEdges = new HashMap<String, List<String>>(); for(String s: ourScalars.keySet()) { incomingEdges.put(s, new ArrayList<String>()); } for(String s: ourScalars.keySet()) { MetaMetadataScalarField mmsf = ourScalars.get(s); if(mmsf.hasValueDependencies()) { for(MetaMetadataValueField dep : mmsf.getValueDependencies()) { String Name = dep.getScalarField(metadata).getName(); incomingEdges.get(Name).add(mmsf.getName()); } } } List<String> noIncomingNodes = getNoIncomingNodes(incomingEdges); List<MetaMetadataScalarField> visited = new ArrayList<MetaMetadataScalarField>(); while(!noIncomingNodes.isEmpty()) { String key = noIncomingNodes.remove(0); MetaMetadataScalarField mmsf = (MetaMetadataScalarField)ourCopy.get(key); visited.add(mmsf); for(MetaMetadataValueField mmvf : mmsf.getValueDependencies()) { String WithIncomingEdge = mmvf.getScalarField(metadata).getName(); incomingEdges.get(WithIncomingEdge).remove(key); if(incomingEdges.get(WithIncomingEdge).isEmpty()) { noIncomingNodes.add(WithIncomingEdge); } } } if(visited.size() == ourScalars.size()) // If these aren't equal, we have a cycle. { HashMapArrayList<String, MetaMetadataField> sortedFieldSet = new HashMapArrayList<String, MetaMetadataField>(); // Put all non-scalar values in, since they can't have dependencies. sortedFieldSet.putAll(ourNonScalars); // We have to flip the topological sort we conducted to get the right order for our purposes. Collections.reverse(visited); for(MetaMetadataScalarField f : visited) { sortedFieldSet.put(f.getName(), f); } return sortedFieldSet; }else{ throw new ScalarDependencyException(); //We have a cycle. } } }