]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/genericrelation/DependenciesRelation.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.db.layer0 / src / org / simantics / db / layer0 / genericrelation / DependenciesRelation.java
index d3c44903b3c05235c41e3c40ec357b064fdf1008..9ec12047c6c501a0dc2a58a836c3f69d073a10fe 100644 (file)
-/*******************************************************************************\r
- * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
- * in Industry THTH ry.\r
- * All rights reserved. This program and the accompanying materials\r
- * are made available under the terms of the Eclipse Public License v1.0\r
- * which accompanies this distribution, and is available at\r
- * http://www.eclipse.org/legal/epl-v10.html\r
- *\r
- * Contributors:\r
- *     VTT Technical Research Centre of Finland - initial API and implementation\r
- *******************************************************************************/\r
-package org.simantics.db.layer0.genericrelation;\r
-\r
-import java.util.ArrayList;\r
-import java.util.Arrays;\r
-import java.util.Collection;\r
-import java.util.Collections;\r
-import java.util.HashSet;\r
-import java.util.List;\r
-import java.util.Map;\r
-import java.util.UUID;\r
-\r
-import org.simantics.databoard.Bindings;\r
-import org.simantics.databoard.util.ObjectUtils;\r
-import org.simantics.datatypes.literal.GUID;\r
-import org.simantics.db.AsyncReadGraph;\r
-import org.simantics.db.ChangeSet;\r
-import org.simantics.db.ChangeSet.StatementChange;\r
-import org.simantics.db.MetadataI;\r
-import org.simantics.db.ReadGraph;\r
-import org.simantics.db.RequestProcessor;\r
-import org.simantics.db.Resource;\r
-import org.simantics.db.Session;\r
-import org.simantics.db.Statement;\r
-import org.simantics.db.WriteGraph;\r
-import org.simantics.db.common.Indexing;\r
-import org.simantics.db.common.changeset.GenericChangeListener;\r
-import org.simantics.db.common.request.IndexRoot;\r
-import org.simantics.db.common.request.ReadRequest;\r
-import org.simantics.db.common.request.SuperTypeString;\r
-import org.simantics.db.common.request.TypeString;\r
-import org.simantics.db.common.request.UnaryRead;\r
-import org.simantics.db.common.utils.Logger;\r
-import org.simantics.db.common.utils.NameUtils;\r
-import org.simantics.db.event.ChangeListener;\r
-import org.simantics.db.exception.DatabaseException;\r
-import org.simantics.db.layer0.adapter.GenericRelation;\r
-import org.simantics.db.layer0.adapter.GenericRelationIndex;\r
-import org.simantics.db.layer0.genericrelation.DependencyChanges.Change;\r
-import org.simantics.db.layer0.genericrelation.DependencyChanges.ComponentAddition;\r
-import org.simantics.db.layer0.genericrelation.DependencyChanges.ComponentModification;\r
-import org.simantics.db.layer0.genericrelation.DependencyChanges.ComponentRemoval;\r
-import org.simantics.db.layer0.genericrelation.DependencyChanges.LinkChange;\r
-import org.simantics.db.procedure.AsyncContextMultiProcedure;\r
-import org.simantics.db.procedure.AsyncContextProcedure;\r
-import org.simantics.db.service.CollectionSupport;\r
-import org.simantics.db.service.DirectQuerySupport;\r
-import org.simantics.db.service.GraphChangeListenerSupport;\r
-import org.simantics.db.service.ManagementSupport;\r
-import org.simantics.db.service.SerialisationSupport;\r
-import org.simantics.layer0.Layer0;\r
-import org.simantics.operation.Layer0X;\r
-import org.simantics.utils.datastructures.Pair;\r
-import org.simantics.utils.logging.TimeLogger;\r
-\r
-public class DependenciesRelation extends UnsupportedRelation implements GenericRelationIndex {\r
-\r
-       private static final boolean DEBUG = false;\r
-       static final boolean DEBUG_LISTENERS = false;\r
-       private static final boolean PROFILE = false;\r
-\r
-       @SuppressWarnings("unchecked")\r
-       private final static Pair<String, String>[] fields = new Pair[] {\r
-               Pair.make(Dependencies.FIELD_MODEL, "Long"),\r
-               Pair.make(Dependencies.FIELD_PARENT, "Long"),\r
-               Pair.make(Dependencies.FIELD_RESOURCE, "Long"),\r
-               Pair.make(Dependencies.FIELD_NAME, "String"),\r
-               Pair.make(Dependencies.FIELD_TYPES, "Text"),\r
-               Pair.make(Dependencies.FIELD_GUID, "Text")\r
-       };\r
-\r
-       final Resource resource;\r
-\r
-       public DependenciesRelation(ReadGraph graph, Resource resource) {\r
-               this.resource = resource;\r
-               synchronized(this) {\r
-                       Session session = graph.getSession();\r
-                       DependenciesListenerStore store = session.peekService(DependenciesListenerStore.class);\r
-                       if(store == null) session.registerService(DependenciesListenerStore.class, new DependenciesListenerStore());\r
-               }\r
-       }\r
-\r
-       class Process {\r
-\r
-               final ArrayList<Entry> result = new ArrayList<Entry>();\r
-               final AsyncContextMultiProcedure<Resource, Resource> structure;\r
-               final AsyncContextProcedure<Entry, String> names;\r
-               final AsyncContextProcedure<Entry, Resource> type;\r
-\r
-               Process(ReadGraph graph, final Resource resource) throws DatabaseException {\r
-\r
-                       final Layer0 L0 = Layer0.getInstance(graph);\r
-                       final DirectQuerySupport dqs = graph.getService(DirectQuerySupport.class);\r
-                       final CollectionSupport cs = graph.getService(CollectionSupport.class);\r
-\r
-                       names = dqs.compilePossibleRelatedValue(graph, L0.HasName, new AsyncContextProcedure<Entry, String>() {\r
-\r
-                               @Override\r
-                               public void execute(AsyncReadGraph graph, Entry entry, String name) {\r
-                                       entry.name = name;\r
-                               }\r
-\r
-                               @Override\r
-                               public void exception(AsyncReadGraph graph, Throwable throwable) {\r
-                                       Logger.defaultLogError(throwable);\r
-                               }\r
-\r
-                       });\r
-\r
-                       type = new AsyncContextProcedure<Entry, Resource>() {\r
-\r
-                               @Override\r
-                               public void execute(AsyncReadGraph graph, Entry entry, Resource type) {\r
-                                       entry.principalType = type;\r
-                               }\r
-\r
-                               @Override\r
-                               public void exception(AsyncReadGraph graph, Throwable throwable) {\r
-                                       Logger.defaultLogError(throwable);\r
-                               }\r
-\r
-                       };\r
-\r
-                       structure = dqs.compileForEachObject(graph, L0.ConsistsOf, new AsyncContextMultiProcedure<Resource, Resource>() {\r
-\r
-                               @Override\r
-                               public void execute(AsyncReadGraph graph, Resource parent, Resource child) {\r
-                                       // WORKAROUND: don't browse virtual child resources\r
-                                       if(!child.isPersistent()) return;\r
-                                       Entry entry = new Entry(parent, child, "", "", "");\r
-                                       result.add(entry);\r
-                                       dqs.forEachObjectCompiled(graph, child, child, structure);\r
-                                       dqs.forPossibleRelatedValueCompiled(graph, child, entry, names);\r
-                                       dqs.forPossibleDirectType(graph, child, entry, type);\r
-                               }\r
-\r
-                               @Override\r
-                               public void finished(AsyncReadGraph graph) {\r
-                               }\r
-\r
-                               @Override\r
-                               public void exception(AsyncReadGraph graph, Throwable throwable) {\r
-                                       Logger.defaultLogError(throwable);\r
-                               }\r
-\r
-                       });\r
-\r
-                       graph.syncRequest(new ReadRequest() {\r
-\r
-                               @Override\r
-                               public void run(ReadGraph graph) throws DatabaseException {\r
-                                       dqs.forEachObjectCompiled(graph, resource, resource, structure);\r
-                               }\r
-\r
-                       });\r
-\r
-            Map<Resource, String> typeStrings = cs.createMap(String.class);\r
-                       for(Entry e : result) {\r
-                               if(e.principalType != null) {\r
-                                   String typeString = typeStrings.get(e.principalType);\r
-                                   if(typeString == null) {\r
-                                       typeString = graph.syncRequest(new SuperTypeString(e.principalType));\r
-                                       if (typeString.isEmpty()) {\r
-                                           Logger.defaultLogError(new DatabaseException("No name for type " + NameUtils.getURIOrSafeNameInternal(graph, e.resource) + " (" + e.resource + ")"));\r
-                                       }\r
-                                       typeStrings.put(e.principalType, typeString);\r
-                                   }\r
-                                   e.types = typeString;\r
-                               } else {\r
-                                   e.types = graph.syncRequest(new TypeString(L0, graph.getTypes(e.resource)));\r
-                               }\r
-                               GUID id = graph.getPossibleRelatedValue(e.resource, L0.identifier, GUID.BINDING);\r
-                               if(id != null)\r
-                                       e.id = id.indexString();\r
-                               else \r
-                                       e.id = "";\r
-                       }\r
-\r
-                       //SessionGarbageCollection.gc(null, graph.getSession(), false, null);\r
-                       \r
-               }\r
-\r
-       }\r
-\r
-       public ArrayList<Entry> find(ReadGraph graph, final Resource model) throws DatabaseException {\r
-               return new Process(graph, model).result;\r
-       }\r
-\r
-       @Override\r
-       public GenericRelation select(String bindingPattern, Object[] constants) {\r
-               checkSelectionArguments(bindingPattern, constants, new String[] { Dependencies.getBindingPattern() });\r
-               final long subjectId = (Long)constants[0];\r
-               return new UnsupportedRelation() {\r
-\r
-                       @Override\r
-                       public boolean isRealizable() {\r
-                               return true;\r
-                       }\r
-\r
-                       @Override\r
-                       final public List<Object[]> realize(ReadGraph graph) throws DatabaseException {\r
-\r
-                               long time = System.nanoTime();\r
-\r
-                SerialisationSupport ss = graph.getService(SerialisationSupport.class);\r
-\r
-                               Resource subject = ss.getResource(subjectId); \r
-                               \r
-                               Collection<Entry> entries = find(graph, subject);\r
-\r
-                               long time2 = System.nanoTime();\r
-\r
-                               if (PROFILE)\r
-                                       System.out.println("Found " + entries.size() + " dependencies in " + 1e-6 * (time2 - time) + "ms for " + graph.getPossibleURI(subject) + ".");\r
-\r
-                               ArrayList<Object[]> result = new ArrayList<Object[]>();\r
-                               for (Entry entry : entries) {\r
-                                       result.add(new Object[] { ss.getRandomAccessId(entry.parent), ss.getRandomAccessId(entry.resource), entry.name, entry.types, entry.id });\r
-                               }\r
-                               return result;\r
-\r
-                       }\r
-\r
-               };\r
-       }\r
-\r
-       @Override\r
-       public Pair<String, String>[] getFields() {\r
-               return fields;\r
-       }\r
-\r
-       @Override\r
-       public List<Map<String, Object>> query(RequestProcessor session, String search, String bindingPattern, Object[] constants, int maxResultCount) {\r
-               if(!Dependencies.getBindingPattern().equals(bindingPattern)) throw new IllegalArgumentException("DependenciesRelation supports indexing only with 'bfffff'");\r
-               IndexedRelations indexer = session.getService(IndexedRelations.class);\r
-               return indexer.query(null, search, session, resource, (Resource)constants[0], maxResultCount);\r
-       }\r
-       \r
-       @Override\r
-       public List<Resource> queryResources(RequestProcessor session, String search, String bindingPattern, Object[] constants, int maxResultCount) {\r
-               if(!Dependencies.getBindingPattern().equals(bindingPattern)) throw new IllegalArgumentException("DependenciesRelation supports indexing only with 'bfffff'");\r
-               IndexedRelations indexer = session.getService(IndexedRelations.class);\r
-               return indexer.queryResources(null, search, session, resource, (Resource)constants[0], maxResultCount);\r
-       }\r
-\r
-       @Override\r
-       public List<Map<String, Object>> list(RequestProcessor session, String bindingPattern, Object[] constants, int maxResultCount) {\r
-               if(!Dependencies.getBindingPattern().equals(bindingPattern)) throw new IllegalArgumentException("DependenciesRelation supports indexing only with 'bfffff'");\r
-               IndexedRelations indexer = session.getService(IndexedRelations.class);\r
-               return indexer.query(null, null, session, resource, (Resource)constants[0], maxResultCount);\r
-       }\r
-\r
-       public static class DependencyChangesRequest extends UnaryRead<ChangeSet, DependencyChanges> {\r
-\r
-               @SuppressWarnings("unused")\r
-               final private static boolean LOG = false;\r
-\r
-               public DependencyChangesRequest(ChangeSet parameter) {\r
-                       super(parameter);\r
-               }\r
-\r
-               @Override\r
-               public DependencyChanges perform(ReadGraph graph) throws DatabaseException {\r
-\r
-                       DependencyChangesWriter w = new DependencyChangesWriter(graph);\r
-                       Layer0 l0 = w.l0;\r
-                       Resource changeInformation = graph.getPossibleResource("http://www.simantics.org/Modeling-1.2/changeInformation/Inverse");\r
-\r
-                       for (Resource value : parameter.changedValues()) {\r
-                               Statement modifiedComponent = graph.getPossibleStatement(value, l0.PropertyOf);\r
-                               if (modifiedComponent == null\r
-                                               || modifiedComponent.getPredicate().equals(changeInformation))\r
-                                       continue;\r
-                               //System.err.println("+comp modi " + NameUtils.getSafeName(graph, renamedComponent, true));\r
-                               w.addComponentModification(modifiedComponent.getObject());\r
-                       }\r
-                       for (Resource value : parameter.changedResources()) {\r
-                               // No more info => need to check further\r
-                               if(!graph.isImmutable(value))\r
-                                       w.addComponentModification(value);\r
-                       }\r
-                       for (StatementChange change : parameter.changedStatements()) {\r
-                               //System.err.println("-stm " + NameUtils.getSafeName(graph, change.getSubject(), true) + " " + NameUtils.getSafeName(graph, change.getPredicate(), true) + " " + NameUtils.getSafeName(graph, change.getObject(), true));\r
-                               Resource subject = change.getSubject();\r
-                               Resource predicate = change.getPredicate();\r
-                               Resource object = change.getObject();\r
-                               if(!object.isPersistent()) continue;\r
-                               if (predicate.equals(l0.ConsistsOf)) {\r
-                                       if (change.isClaim())\r
-                                               w.addComponentAddition(subject, object);\r
-                                       else \r
-                                               w.addComponentRemoval(subject, object);\r
-                               } else if (predicate.equals(l0.IsLinkedTo)) {\r
-                                       w.addLinkChange(subject);\r
-                               } else /*if (graph.isSubrelationOf(predicate, l0.DependsOn))*/ {\r
-                                       //System.err.println("-modi " + NameUtils.getSafeName(graph, subject, true));\r
-                                       w.addComponentModification(subject);\r
-                               } \r
-                       }\r
-                       return w.getResult();\r
-               }\r
-\r
-       };\r
-\r
-       private static int trackers = 0;\r
-       \r
-       private static ChangeListener listener;\r
-\r
-       public static void assertFinishedTracking() {\r
-           if(trackers != 0) throw new IllegalStateException("Trackers should be 0 (was " + trackers + ")");\r
-       }\r
-       \r
-       @Override\r
-       public synchronized void untrack(RequestProcessor processor, final Resource model) {\r
-\r
-           trackers--;\r
-           \r
-           if(trackers < 0) throw new IllegalStateException("Dependency tracking reference count is broken");\r
-           \r
-           if(trackers == 0) {\r
-               \r
-               if(listener == null) throw new IllegalStateException("Dependency tracking was not active");\r
-           \r
-               GraphChangeListenerSupport changeSupport = processor.getService(GraphChangeListenerSupport.class);\r
-               changeSupport.removeMetadataListener(listener);\r
-               listener = null;\r
-                       \r
-           }\r
-           \r
-       }\r
-\r
-       @Override\r
-       public synchronized void trackAndIndex(RequestProcessor processor, Resource model__) {\r
-\r
-           if(trackers == 0) {\r
-\r
-               if(listener != null) throw new IllegalStateException("Dependency tracking was active");\r
-\r
-               listener = new GenericChangeListener<DependencyChangesRequest, DependencyChanges>() {\r
-\r
-                   @Override\r
-                   public boolean preEventRequest() {\r
-                       return !Indexing.isDependenciesIndexingDisabled();\r
-                   }\r
-\r
-                   @Override\r
-                   public void onEvent(ReadGraph graph, MetadataI metadata, DependencyChanges event) throws DatabaseException {\r
-\r
-                       TimeLogger.log(DependenciesRelation.class, "trackAndIndex.onEvent: starting index update processing");\r
-\r
-                       if(DEBUG)\r
-                           System.err.println("Adding metadata " + event + " in revision " + graph.getService(ManagementSupport.class).getHeadRevisionId());\r
-\r
-                       WriteGraph w = (WriteGraph)graph;\r
-                       if(!event.isEmpty())\r
-                               w.addMetadata(event);\r
-\r
-                       final Session session = graph.getSession();\r
-                       final IndexedRelations indexer = session.getService(IndexedRelations.class);\r
-                       Layer0 L0 = Layer0.getInstance(graph);\r
-                       SerialisationSupport ss = graph.getService(SerialisationSupport.class);\r
-\r
-                       for(Map.Entry<Resource, Change[]>  modelEntry : event.get().entrySet()) {\r
-\r
-                           final Resource model = modelEntry.getKey();\r
-                           final Change[] changes = modelEntry.getValue();\r
-\r
-                           boolean linkChange = false;\r
-\r
-                           Collection<Object[]> _additions = Collections.emptyList();\r
-                           Collection<Object> _removals = Collections.emptyList();\r
-                           Collection<Object> _replacementKeys = Collections.emptyList();\r
-                           Collection<Object[]> _replacementObjects = Collections.emptyList();\r
-                           Collection<Pair<String, String>> _typeChanges = Collections.emptyList();\r
-\r
-                           if(DEBUG) System.out.println("MODEL: " + NameUtils.getSafeLabel(graph, model));\r
-                           //                final Change[] changes = event.get(model);\r
-                           if(DEBUG) System.out.println("  CHANGES: " + Arrays.toString(changes));\r
-                           if (changes != null) {\r
-                               _additions = new ArrayList<Object[]>();\r
-                               _removals = new ArrayList<Object>();\r
-                               _replacementKeys = new ArrayList<Object>();\r
-                               _replacementObjects = new ArrayList<Object[]>();\r
-                               _typeChanges = new HashSet<Pair<String, String>>();\r
-\r
-                               for (Change _entry : changes) {\r
-                                   if (_entry instanceof ComponentAddition) {\r
-                                       ComponentAddition entry = (ComponentAddition)_entry;\r
-                                       final String name = graph.getPossibleRelatedValue(entry.component, L0.HasName, Bindings.STRING);\r
-                                       final GUID id = graph.getPossibleRelatedValue(entry.component, L0.identifier, GUID.BINDING);\r
-                                       final String types = graph.syncRequest(new TypeString(L0, graph.getTypes(entry.component)));\r
-                                       if (name != null && types != null) {\r
-                                               if(!entry.isValid(graph)) continue;\r
-                                           Resource parent = graph.getPossibleObject(entry.component, L0.PartOf);\r
-                                           if (parent != null) {\r
-                                               _additions.add(new Object[] { ss.getRandomAccessId(parent), ss.getRandomAccessId(entry.component), name, types, id != null ? id.indexString() : "" });\r
-                                           } else {\r
-                                                   //System.err.println("resource " + entry.component + ": no parent for entry " + name + " " + types);\r
-                                           }\r
-                                       } else {\r
-                                           //System.err.println("resource " + entry.component + ": " + name + " " + types);\r
-                                       }\r
-                                   } else if(_entry instanceof ComponentModification) {\r
-                                       ComponentModification entry = (ComponentModification)_entry;\r
-                                       final String name = graph.getPossibleRelatedValue(entry.component, L0.HasName, Bindings.STRING);\r
-                                       final GUID id = graph.getPossibleRelatedValue(entry.component, L0.identifier, GUID.BINDING);\r
-                                       if(graph.isInstanceOf(entry.component, L0.Type)) {\r
-                                           SerialisationSupport support = session.getService(SerialisationSupport.class);\r
-                                           _typeChanges.add(new Pair<String, String>(name, String.valueOf(support.getRandomAccessId((Resource) entry.component))));\r
-                                       } else {\r
-                                           final String types = graph.syncRequest(new TypeString(L0, graph.getTypes(entry.component)));\r
-                                           if (name != null && types != null) {\r
-                                               Resource part = graph.getPossibleObject(entry.component, L0.PartOf);\r
-                                               if(part != null) {\r
-                                                   _replacementKeys.add(ss.getRandomAccessId(entry.component));\r
-                                                   _replacementObjects.add(new Object[] { ss.getRandomAccessId(part), \r
-                                                           ss.getRandomAccessId(entry.component), name, types, id != null ? id.indexString() : "" });\r
-                                               }\r
-                                           }\r
-                                       }\r
-                                   } else if (_entry instanceof ComponentRemoval) {\r
-                                       ComponentRemoval entry = (ComponentRemoval)_entry;\r
-                                       if(!entry.isValid(graph)) continue;\r
-                                       _removals.add(ss.getRandomAccessId(((ComponentRemoval)_entry).component));\r
-                                   } else if (_entry instanceof LinkChange) {\r
-                                       linkChange = true;\r
-                                   }\r
-                               }\r
-                           }\r
-\r
-                           final boolean reset = linkChange || event.hasUnresolved;\r
-                           //System.err.println("dependencies(" + NameUtils.getSafeLabel(graph, model) + "): reset=" + reset + " linkChange=" + linkChange + " unresolved=" + event.hasUnresolved );\r
-\r
-                           if (reset || !_additions.isEmpty() || !_removals.isEmpty() || !_replacementKeys.isEmpty() || !_typeChanges.isEmpty()) {\r
-\r
-                               TimeLogger.log(DependenciesRelation.class, "trackAndIndex.onEvent: starting index update");\r
-\r
-                               final Collection<Object[]> additions = _additions;\r
-                               final Collection<Object> removals = _removals;\r
-                               final Collection<Object> replacementKeys = _replacementKeys;\r
-                               final Collection<Object[]> replacementObjects = _replacementObjects; \r
-                               final boolean typeNameChanges = typeNameChanges(graph, indexer, model, _typeChanges);\r
-\r
-                            final UUID pending = Indexing.makeIndexPending();\r
-\r
-                            {\r
-                                {\r
-                                       try {\r
-                                           boolean didChange = false;\r
-                                           // Unresolved and linkChanges are not relevant any more\r
-                                           boolean doReset = typeNameChanges;\r
-\r
-                                           if (doReset) {\r
-\r
-                                            if(DEBUG) {\r
-                                                System.err.println("resetIndex " + reset + " " + typeNameChanges);\r
-                                            }\r
-\r
-                                               indexer.removeAll(null, graph, DependenciesRelation.this, resource, model);\r
-                                               didChange = true;\r
-\r
-                                           } else {\r
-\r
-                                               if (!replacementKeys.isEmpty() && (replacementKeys.size() == replacementObjects.size())) {\r
-                                                   if(DEBUG) {\r
-                                                       System.out.println(replacementKeys.size() + " index replacements: " + replacementKeys);\r
-                                                   }\r
-                                                   didChange |= indexer.replace(null, graph, DependenciesRelation.this, resource, model, Dependencies.FIELD_RESOURCE, replacementKeys, replacementObjects);\r
-                                               }\r
-                                               if (!removals.isEmpty()) {\r
-                                                   if(DEBUG) {\r
-                                                       System.out.println(removals.size() + " index removals: " + removals);\r
-                                                   }\r
-                                                   indexer.remove(null, graph, DependenciesRelation.this, resource, model, Dependencies.FIELD_RESOURCE, removals);\r
-                                                   didChange = true;\r
-                                               }\r
-                                               if (!additions.isEmpty()) {\r
-                                                   if(DEBUG) {\r
-                                                       for(Object[] os : additions) System.err.println("Adding to index " + model + ": " + Arrays.toString(os));\r
-                                                   }\r
-                                                   //System.out.println(additions.size() + " index insertions");\r
-                                                   indexer.insert(null, graph, DependenciesRelation.this, resource, model, additions);\r
-                                                   didChange = true;\r
-                                               }\r
-\r
-                                           }\r
-\r
-                                           if (didChange)\r
-                                               // TODO: because this data is ran with\r
-                                               // ThreadUtils.getBlockingWorkExecutor()\r
-                                               // fireListeners needs to use peekService,\r
-                                               // not getService since there is no\r
-                                               // guarantee that the session isn't being\r
-                                               // disposed while this method is executing.\r
-                                               fireListeners(graph, model);\r
-\r
-                                       } catch (Throwable t) {\r
-                                           // Just to know if something unexpected happens here.\r
-                                           Logger.defaultLogError("Dependencies index update failed for model "\r
-                                                   + model + " and relation " + resource + ".", t);\r
-                                           t.printStackTrace();\r
-\r
-                                           // NOTE: Last resort: failure to update index\r
-                                           // properly results in removal of the whole index.\r
-                                           // This is the only thing that can be done\r
-                                           // at this point to ensure that the index will\r
-                                           // return correct results in the future, through\r
-                                           // complete reinitialization. \r
-                                           //indexer.removeAll(null, session, DependenciesRelation.this, resource, model);\r
-                                       } finally {\r
-                                           Indexing.releaseIndexPending(pending);\r
-                                           Indexing.clearCaches(model);\r
-                                       }\r
-                                }\r
-                            }\r
-\r
-                               TimeLogger.log(DependenciesRelation.class, "trackAndIndex.onEvent: index update done");\r
-                           }\r
-                       }\r
-\r
-                   }\r
-\r
-               };\r
-\r
-               GraphChangeListenerSupport changeSupport = processor.getService(GraphChangeListenerSupport.class);\r
-               changeSupport.addMetadataListener(listener);\r
-\r
-           }\r
-\r
-           trackers++;\r
-\r
-       }\r
-\r
-       private boolean typeNameChanges(ReadGraph graph, IndexedRelations indexer,\r
-                       Resource model, final Collection<Pair<String, String>> typeChanges)\r
-                       throws DatabaseException {\r
-               if (typeChanges.isEmpty())\r
-                       return false;\r
-\r
-               for (Pair<String, String> nr : typeChanges) {\r
-                       String query = Dependencies.FIELD_RESOURCE + ":[" + nr.second + " TO " + nr.second + "]";\r
-                       //System.out.println("query: " + query);\r
-                       List<Map<String, Object>> results = indexer.query(null, query, graph, resource, model, Integer.MAX_VALUE);\r
-                       if (results.size() != 1) {\r
-                               return true;\r
-                       } else {\r
-                               Map<String, Object> result = results.get(0);\r
-                               if (!ObjectUtils.objectEquals(result.get(Dependencies.FIELD_NAME), nr.first)) {\r
-                                       return true;\r
-                               }\r
-                       }\r
-//                     System.err.println("Type " + nr.first + " was unchanged.");\r
-               }\r
-               return false;\r
-       }\r
-\r
-       @Override\r
-       public void addListener(RequestProcessor processor, Resource model, Runnable observer) {\r
-               DependenciesListenerStore store = processor.getSession().getService(DependenciesListenerStore.class);\r
-               store.addListener(model, observer);\r
-       }\r
-\r
-       @Override\r
-       public void removeListener(RequestProcessor processor, Resource model, Runnable observer) {\r
-               DependenciesListenerStore store = processor.getSession().getService(DependenciesListenerStore.class);\r
-               store.removeListener(model, observer);\r
-       }\r
-\r
-       void fireListeners(RequestProcessor processor, Resource model) {\r
-               DependenciesListenerStore store = processor.getSession().peekService(DependenciesListenerStore.class);\r
-               if (store != null)\r
-                       store.fireListeners(model);\r
-       }\r
-\r
-       @Override\r
-       public void reset(RequestProcessor processor, Resource input) {\r
-               if (DEBUG) {\r
-                       System.out.println("DependenciesRelation.reset: " + input);\r
-                       new Exception("DependenciesRelation.reset(" + listener + ")").printStackTrace(System.out);\r
-               }\r
-               DependenciesListenerStore store = processor.getSession().getService(DependenciesListenerStore.class);\r
-               store.fireListeners(input);\r
-       }\r
-\r
-       public static void addSubtree(ReadGraph graph, Resource root) throws DatabaseException {\r
-\r
-               Resource indexRoot = graph.syncRequest(new IndexRoot(root));\r
-               addSubtree(graph, indexRoot, root);\r
-\r
-       }\r
-\r
-       public static void addSubtree(ReadGraph graph, Resource indexRoot, Resource subtreeRoot) throws DatabaseException {\r
-               \r
-               DependenciesRelation dr = new DependenciesRelation(graph, indexRoot);\r
-        SerialisationSupport ss = graph.getService(SerialisationSupport.class);\r
-\r
-        ArrayList<Entry> entries = dr.find(graph, subtreeRoot);\r
-        entries.add(new Entry(graph, subtreeRoot));\r
-\r
-               ArrayList<Object[]> result = new ArrayList<Object[]>(entries.size());\r
-               for (Entry entry : entries) {\r
-                       result.add(new Object[] { ss.getRandomAccessId(entry.parent), ss.getRandomAccessId(entry.resource), entry.name, entry.types, entry.id });\r
-               }\r
-\r
-               Layer0X L0X = Layer0X.getInstance(graph);\r
-        IndexedRelations indexer = graph.getService(IndexedRelations.class);\r
-        indexer.insert(null, graph, dr, L0X.DependenciesRelation, indexRoot, result);\r
-               \r
-       }\r
-       \r
-}\r
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management
+ * in Industry THTH ry.
+ * 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:
+ *     VTT Technical Research Centre of Finland - initial API and implementation
+ *******************************************************************************/
+package org.simantics.db.layer0.genericrelation;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import org.simantics.databoard.Bindings;
+import org.simantics.databoard.util.ObjectUtils;
+import org.simantics.datatypes.literal.GUID;
+import org.simantics.db.AsyncReadGraph;
+import org.simantics.db.ChangeSet;
+import org.simantics.db.ChangeSet.StatementChange;
+import org.simantics.db.MetadataI;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.RequestProcessor;
+import org.simantics.db.Resource;
+import org.simantics.db.Session;
+import org.simantics.db.Statement;
+import org.simantics.db.WriteGraph;
+import org.simantics.db.common.Indexing;
+import org.simantics.db.common.changeset.GenericChangeListener;
+import org.simantics.db.common.request.IndexRoot;
+import org.simantics.db.common.request.ReadRequest;
+import org.simantics.db.common.request.SuperTypeString;
+import org.simantics.db.common.request.TypeString;
+import org.simantics.db.common.request.UnaryRead;
+import org.simantics.db.common.utils.Logger;
+import org.simantics.db.common.utils.NameUtils;
+import org.simantics.db.event.ChangeListener;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.layer0.adapter.GenericRelation;
+import org.simantics.db.layer0.adapter.GenericRelationIndex;
+import org.simantics.db.layer0.genericrelation.DependencyChanges.Change;
+import org.simantics.db.layer0.genericrelation.DependencyChanges.ComponentAddition;
+import org.simantics.db.layer0.genericrelation.DependencyChanges.ComponentModification;
+import org.simantics.db.layer0.genericrelation.DependencyChanges.ComponentRemoval;
+import org.simantics.db.layer0.genericrelation.DependencyChanges.LinkChange;
+import org.simantics.db.procedure.AsyncContextMultiProcedure;
+import org.simantics.db.procedure.AsyncContextProcedure;
+import org.simantics.db.service.CollectionSupport;
+import org.simantics.db.service.DirectQuerySupport;
+import org.simantics.db.service.GraphChangeListenerSupport;
+import org.simantics.db.service.ManagementSupport;
+import org.simantics.db.service.SerialisationSupport;
+import org.simantics.layer0.Layer0;
+import org.simantics.operation.Layer0X;
+import org.simantics.utils.datastructures.Pair;
+import org.simantics.utils.logging.TimeLogger;
+
+public class DependenciesRelation extends UnsupportedRelation implements GenericRelationIndex {
+
+       private static final boolean DEBUG = false;
+       static final boolean DEBUG_LISTENERS = false;
+       private static final boolean PROFILE = false;
+
+       @SuppressWarnings("unchecked")
+       private final static Pair<String, String>[] fields = new Pair[] {
+               Pair.make(Dependencies.FIELD_MODEL, "Long"),
+               Pair.make(Dependencies.FIELD_PARENT, "Long"),
+               Pair.make(Dependencies.FIELD_RESOURCE, "Long"),
+               Pair.make(Dependencies.FIELD_NAME, "String"),
+               Pair.make(Dependencies.FIELD_TYPES, "Text"),
+               Pair.make(Dependencies.FIELD_GUID, "Text")
+       };
+
+       final Resource resource;
+
+       public DependenciesRelation(ReadGraph graph, Resource resource) {
+               this.resource = resource;
+               synchronized(this) {
+                       Session session = graph.getSession();
+                       DependenciesListenerStore store = session.peekService(DependenciesListenerStore.class);
+                       if(store == null) session.registerService(DependenciesListenerStore.class, new DependenciesListenerStore());
+               }
+       }
+
+       class Process {
+
+               final ArrayList<Entry> result = new ArrayList<Entry>();
+               final AsyncContextMultiProcedure<Resource, Resource> structure;
+               final AsyncContextProcedure<Entry, String> names;
+               final AsyncContextProcedure<Entry, Resource> type;
+
+               Process(ReadGraph graph, final Resource resource) throws DatabaseException {
+
+                       final Layer0 L0 = Layer0.getInstance(graph);
+                       final DirectQuerySupport dqs = graph.getService(DirectQuerySupport.class);
+                       final CollectionSupport cs = graph.getService(CollectionSupport.class);
+
+                       names = dqs.compilePossibleRelatedValue(graph, L0.HasName, new AsyncContextProcedure<Entry, String>() {
+
+                               @Override
+                               public void execute(AsyncReadGraph graph, Entry entry, String name) {
+                                       entry.name = name;
+                               }
+
+                               @Override
+                               public void exception(AsyncReadGraph graph, Throwable throwable) {
+                                       Logger.defaultLogError(throwable);
+                               }
+
+                       });
+
+                       type = new AsyncContextProcedure<Entry, Resource>() {
+
+                               @Override
+                               public void execute(AsyncReadGraph graph, Entry entry, Resource type) {
+                                       entry.principalType = type;
+                               }
+
+                               @Override
+                               public void exception(AsyncReadGraph graph, Throwable throwable) {
+                                       Logger.defaultLogError(throwable);
+                               }
+
+                       };
+
+                       structure = dqs.compileForEachObject(graph, L0.ConsistsOf, new AsyncContextMultiProcedure<Resource, Resource>() {
+
+                               @Override
+                               public void execute(AsyncReadGraph graph, Resource parent, Resource child) {
+                                       // WORKAROUND: don't browse virtual child resources
+                                       if(!child.isPersistent()) return;
+                                       Entry entry = new Entry(parent, child, "", "", "");
+                                       result.add(entry);
+                                       dqs.forEachObjectCompiled(graph, child, child, structure);
+                                       dqs.forPossibleRelatedValueCompiled(graph, child, entry, names);
+                                       dqs.forPossibleDirectType(graph, child, entry, type);
+                               }
+
+                               @Override
+                               public void finished(AsyncReadGraph graph) {
+                               }
+
+                               @Override
+                               public void exception(AsyncReadGraph graph, Throwable throwable) {
+                                       Logger.defaultLogError(throwable);
+                               }
+
+                       });
+
+                       graph.syncRequest(new ReadRequest() {
+
+                               @Override
+                               public void run(ReadGraph graph) throws DatabaseException {
+                                       dqs.forEachObjectCompiled(graph, resource, resource, structure);
+                               }
+
+                       });
+
+            Map<Resource, String> typeStrings = cs.createMap(String.class);
+                       for(Entry e : result) {
+                               if(e.principalType != null) {
+                                   String typeString = typeStrings.get(e.principalType);
+                                   if(typeString == null) {
+                                       typeString = graph.syncRequest(new SuperTypeString(e.principalType));
+                                       if (typeString.isEmpty()) {
+                                           Logger.defaultLogError(new DatabaseException("No name for type " + NameUtils.getURIOrSafeNameInternal(graph, e.resource) + " (" + e.resource + ")"));
+                                       }
+                                       typeStrings.put(e.principalType, typeString);
+                                   }
+                                   e.types = typeString;
+                               } else {
+                                   e.types = graph.syncRequest(new TypeString(L0, graph.getTypes(e.resource)));
+                               }
+                               GUID id = graph.getPossibleRelatedValue(e.resource, L0.identifier, GUID.BINDING);
+                               if(id != null)
+                                       e.id = id.indexString();
+                               else 
+                                       e.id = "";
+                       }
+
+                       //SessionGarbageCollection.gc(null, graph.getSession(), false, null);
+                       
+               }
+
+       }
+
+       public ArrayList<Entry> find(ReadGraph graph, final Resource model) throws DatabaseException {
+               return new Process(graph, model).result;
+       }
+
+       @Override
+       public GenericRelation select(String bindingPattern, Object[] constants) {
+               checkSelectionArguments(bindingPattern, constants, new String[] { Dependencies.getBindingPattern() });
+               final long subjectId = (Long)constants[0];
+               return new UnsupportedRelation() {
+
+                       @Override
+                       public boolean isRealizable() {
+                               return true;
+                       }
+
+                       @Override
+                       final public List<Object[]> realize(ReadGraph graph) throws DatabaseException {
+
+                               long time = System.nanoTime();
+
+                SerialisationSupport ss = graph.getService(SerialisationSupport.class);
+
+                               Resource subject = ss.getResource(subjectId); 
+                               
+                               Collection<Entry> entries = find(graph, subject);
+
+                               long time2 = System.nanoTime();
+
+                               if (PROFILE)
+                                       System.out.println("Found " + entries.size() + " dependencies in " + 1e-6 * (time2 - time) + "ms for " + graph.getPossibleURI(subject) + ".");
+
+                               ArrayList<Object[]> result = new ArrayList<Object[]>();
+                               for (Entry entry : entries) {
+                                       result.add(new Object[] { ss.getRandomAccessId(entry.parent), ss.getRandomAccessId(entry.resource), entry.name, entry.types, entry.id });
+                               }
+                               return result;
+
+                       }
+
+               };
+       }
+
+       @Override
+       public Pair<String, String>[] getFields() {
+               return fields;
+       }
+
+       @Override
+       public List<Map<String, Object>> query(RequestProcessor session, String search, String bindingPattern, Object[] constants, int maxResultCount) {
+               if(!Dependencies.getBindingPattern().equals(bindingPattern)) throw new IllegalArgumentException("DependenciesRelation supports indexing only with 'bfffff'");
+               IndexedRelations indexer = session.getService(IndexedRelations.class);
+               return indexer.query(null, search, session, resource, (Resource)constants[0], maxResultCount);
+       }
+       
+       @Override
+       public List<Resource> queryResources(RequestProcessor session, String search, String bindingPattern, Object[] constants, int maxResultCount) {
+               if(!Dependencies.getBindingPattern().equals(bindingPattern)) throw new IllegalArgumentException("DependenciesRelation supports indexing only with 'bfffff'");
+               IndexedRelations indexer = session.getService(IndexedRelations.class);
+               return indexer.queryResources(null, search, session, resource, (Resource)constants[0], maxResultCount);
+       }
+
+       @Override
+       public List<Map<String, Object>> list(RequestProcessor session, String bindingPattern, Object[] constants, int maxResultCount) {
+               if(!Dependencies.getBindingPattern().equals(bindingPattern)) throw new IllegalArgumentException("DependenciesRelation supports indexing only with 'bfffff'");
+               IndexedRelations indexer = session.getService(IndexedRelations.class);
+               return indexer.query(null, null, session, resource, (Resource)constants[0], maxResultCount);
+       }
+
+       public static class DependencyChangesRequest extends UnaryRead<ChangeSet, DependencyChanges> {
+
+               @SuppressWarnings("unused")
+               final private static boolean LOG = false;
+
+               public DependencyChangesRequest(ChangeSet parameter) {
+                       super(parameter);
+               }
+
+               @Override
+               public DependencyChanges perform(ReadGraph graph) throws DatabaseException {
+
+                       DependencyChangesWriter w = new DependencyChangesWriter(graph);
+                       Layer0 l0 = w.l0;
+                       Resource changeInformation = graph.getPossibleResource("http://www.simantics.org/Modeling-1.2/changeInformation/Inverse");
+
+                       for (Resource value : parameter.changedValues()) {
+                               Statement modifiedComponent = graph.getPossibleStatement(value, l0.PropertyOf);
+                               if (modifiedComponent == null
+                                               || modifiedComponent.getPredicate().equals(changeInformation))
+                                       continue;
+                               //System.err.println("+comp modi " + NameUtils.getSafeName(graph, renamedComponent, true));
+                               w.addComponentModification(modifiedComponent.getObject());
+                       }
+                       for (Resource value : parameter.changedResources()) {
+                               // No more info => need to check further
+                               if(!graph.isImmutable(value))
+                                       w.addComponentModification(value);
+                       }
+                       for (StatementChange change : parameter.changedStatements()) {
+                               //System.err.println("-stm " + NameUtils.getSafeName(graph, change.getSubject(), true) + " " + NameUtils.getSafeName(graph, change.getPredicate(), true) + " " + NameUtils.getSafeName(graph, change.getObject(), true));
+                               Resource subject = change.getSubject();
+                               Resource predicate = change.getPredicate();
+                               Resource object = change.getObject();
+                               if(!object.isPersistent()) continue;
+                               if (predicate.equals(l0.ConsistsOf)) {
+                                       if (change.isClaim())
+                                               w.addComponentAddition(subject, object);
+                                       else 
+                                               w.addComponentRemoval(subject, object);
+                               } else if (predicate.equals(l0.IsLinkedTo)) {
+                                       w.addLinkChange(subject);
+                               } else /*if (graph.isSubrelationOf(predicate, l0.DependsOn))*/ {
+                                       //System.err.println("-modi " + NameUtils.getSafeName(graph, subject, true));
+                                       w.addComponentModification(subject);
+                               } 
+                       }
+                       return w.getResult();
+               }
+
+       };
+
+       private static int trackers = 0;
+       
+       private static ChangeListener listener;
+
+       public static void assertFinishedTracking() {
+           if(trackers != 0) throw new IllegalStateException("Trackers should be 0 (was " + trackers + ")");
+       }
+       
+       @Override
+       public synchronized void untrack(RequestProcessor processor, final Resource model) {
+
+           trackers--;
+           
+           if(trackers < 0) throw new IllegalStateException("Dependency tracking reference count is broken");
+           
+           if(trackers == 0) {
+               
+               if(listener == null) throw new IllegalStateException("Dependency tracking was not active");
+           
+               GraphChangeListenerSupport changeSupport = processor.getService(GraphChangeListenerSupport.class);
+               changeSupport.removeMetadataListener(listener);
+               listener = null;
+                       
+           }
+           
+       }
+
+       @Override
+       public synchronized void trackAndIndex(RequestProcessor processor, Resource model__) {
+
+           if(trackers == 0) {
+
+               if(listener != null) throw new IllegalStateException("Dependency tracking was active");
+
+               listener = new GenericChangeListener<DependencyChangesRequest, DependencyChanges>() {
+
+                   @Override
+                   public boolean preEventRequest() {
+                       return !Indexing.isDependenciesIndexingDisabled();
+                   }
+
+                   @Override
+                   public void onEvent(ReadGraph graph, MetadataI metadata, DependencyChanges event) throws DatabaseException {
+
+                       TimeLogger.log(DependenciesRelation.class, "trackAndIndex.onEvent: starting index update processing");
+
+                       if(DEBUG)
+                           System.err.println("Adding metadata " + event + " in revision " + graph.getService(ManagementSupport.class).getHeadRevisionId());
+
+                       WriteGraph w = (WriteGraph)graph;
+                       if(!event.isEmpty())
+                               w.addMetadata(event);
+
+                       final Session session = graph.getSession();
+                       final IndexedRelations indexer = session.getService(IndexedRelations.class);
+                       Layer0 L0 = Layer0.getInstance(graph);
+                       SerialisationSupport ss = graph.getService(SerialisationSupport.class);
+
+                       for(Map.Entry<Resource, Change[]>  modelEntry : event.get().entrySet()) {
+
+                           final Resource model = modelEntry.getKey();
+                           final Change[] changes = modelEntry.getValue();
+
+                           boolean linkChange = false;
+
+                           Collection<Object[]> _additions = Collections.emptyList();
+                           Collection<Object> _removals = Collections.emptyList();
+                           Collection<Object> _replacementKeys = Collections.emptyList();
+                           Collection<Object[]> _replacementObjects = Collections.emptyList();
+                           Collection<Pair<String, String>> _typeChanges = Collections.emptyList();
+
+                           if(DEBUG) System.out.println("MODEL: " + NameUtils.getSafeLabel(graph, model));
+                           //                final Change[] changes = event.get(model);
+                           if(DEBUG) System.out.println("  CHANGES: " + Arrays.toString(changes));
+                           if (changes != null) {
+                               _additions = new ArrayList<Object[]>();
+                               _removals = new ArrayList<Object>();
+                               _replacementKeys = new ArrayList<Object>();
+                               _replacementObjects = new ArrayList<Object[]>();
+                               _typeChanges = new HashSet<Pair<String, String>>();
+
+                               for (Change _entry : changes) {
+                                   if (_entry instanceof ComponentAddition) {
+                                       ComponentAddition entry = (ComponentAddition)_entry;
+                                       final String name = graph.getPossibleRelatedValue(entry.component, L0.HasName, Bindings.STRING);
+                                       final GUID id = graph.getPossibleRelatedValue(entry.component, L0.identifier, GUID.BINDING);
+                                       final String types = graph.syncRequest(new TypeString(L0, graph.getTypes(entry.component)));
+                                       if (name != null && types != null) {
+                                               if(!entry.isValid(graph)) continue;
+                                           Resource parent = graph.getPossibleObject(entry.component, L0.PartOf);
+                                           if (parent != null) {
+                                               _additions.add(new Object[] { ss.getRandomAccessId(parent), ss.getRandomAccessId(entry.component), name, types, id != null ? id.indexString() : "" });
+                                           } else {
+                                                   //System.err.println("resource " + entry.component + ": no parent for entry " + name + " " + types);
+                                           }
+                                       } else {
+                                           //System.err.println("resource " + entry.component + ": " + name + " " + types);
+                                       }
+                                   } else if(_entry instanceof ComponentModification) {
+                                       ComponentModification entry = (ComponentModification)_entry;
+                                       final String name = graph.getPossibleRelatedValue(entry.component, L0.HasName, Bindings.STRING);
+                                       final GUID id = graph.getPossibleRelatedValue(entry.component, L0.identifier, GUID.BINDING);
+                                       if(graph.isInstanceOf(entry.component, L0.Type)) {
+                                           SerialisationSupport support = session.getService(SerialisationSupport.class);
+                                           _typeChanges.add(new Pair<String, String>(name, String.valueOf(support.getRandomAccessId((Resource) entry.component))));
+                                       } else {
+                                           final String types = graph.syncRequest(new TypeString(L0, graph.getTypes(entry.component)));
+                                           if (name != null && types != null) {
+                                               Resource part = graph.getPossibleObject(entry.component, L0.PartOf);
+                                               if(part != null) {
+                                                   _replacementKeys.add(ss.getRandomAccessId(entry.component));
+                                                   _replacementObjects.add(new Object[] { ss.getRandomAccessId(part), 
+                                                           ss.getRandomAccessId(entry.component), name, types, id != null ? id.indexString() : "" });
+                                               }
+                                           }
+                                       }
+                                   } else if (_entry instanceof ComponentRemoval) {
+                                       ComponentRemoval entry = (ComponentRemoval)_entry;
+                                       if(!entry.isValid(graph)) continue;
+                                       _removals.add(ss.getRandomAccessId(((ComponentRemoval)_entry).component));
+                                   } else if (_entry instanceof LinkChange) {
+                                       linkChange = true;
+                                   }
+                               }
+                           }
+
+                           final boolean reset = linkChange || event.hasUnresolved;
+                           //System.err.println("dependencies(" + NameUtils.getSafeLabel(graph, model) + "): reset=" + reset + " linkChange=" + linkChange + " unresolved=" + event.hasUnresolved );
+
+                           if (reset || !_additions.isEmpty() || !_removals.isEmpty() || !_replacementKeys.isEmpty() || !_typeChanges.isEmpty()) {
+
+                               TimeLogger.log(DependenciesRelation.class, "trackAndIndex.onEvent: starting index update");
+
+                               final Collection<Object[]> additions = _additions;
+                               final Collection<Object> removals = _removals;
+                               final Collection<Object> replacementKeys = _replacementKeys;
+                               final Collection<Object[]> replacementObjects = _replacementObjects; 
+                               final boolean typeNameChanges = typeNameChanges(graph, indexer, model, _typeChanges);
+
+                            final UUID pending = Indexing.makeIndexPending();
+
+                            {
+                                {
+                                       try {
+                                           boolean didChange = false;
+                                           // Unresolved and linkChanges are not relevant any more
+                                           boolean doReset = typeNameChanges;
+
+                                           if (doReset) {
+
+                                            if(DEBUG) {
+                                                System.err.println("resetIndex " + reset + " " + typeNameChanges);
+                                            }
+
+                                               indexer.removeAll(null, graph, DependenciesRelation.this, resource, model);
+                                               didChange = true;
+
+                                           } else {
+
+                                               if (!replacementKeys.isEmpty() && (replacementKeys.size() == replacementObjects.size())) {
+                                                   if(DEBUG) {
+                                                       System.out.println(replacementKeys.size() + " index replacements: " + replacementKeys);
+                                                   }
+                                                   didChange |= indexer.replace(null, graph, DependenciesRelation.this, resource, model, Dependencies.FIELD_RESOURCE, replacementKeys, replacementObjects);
+                                               }
+                                               if (!removals.isEmpty()) {
+                                                   if(DEBUG) {
+                                                       System.out.println(removals.size() + " index removals: " + removals);
+                                                   }
+                                                   indexer.remove(null, graph, DependenciesRelation.this, resource, model, Dependencies.FIELD_RESOURCE, removals);
+                                                   didChange = true;
+                                               }
+                                               if (!additions.isEmpty()) {
+                                                   if(DEBUG) {
+                                                       for(Object[] os : additions) System.err.println("Adding to index " + model + ": " + Arrays.toString(os));
+                                                   }
+                                                   //System.out.println(additions.size() + " index insertions");
+                                                   indexer.insert(null, graph, DependenciesRelation.this, resource, model, additions);
+                                                   didChange = true;
+                                               }
+
+                                           }
+
+                                           if (didChange)
+                                               // TODO: because this data is ran with
+                                               // ThreadUtils.getBlockingWorkExecutor()
+                                               // fireListeners needs to use peekService,
+                                               // not getService since there is no
+                                               // guarantee that the session isn't being
+                                               // disposed while this method is executing.
+                                               fireListeners(graph, model);
+
+                                       } catch (Throwable t) {
+                                           // Just to know if something unexpected happens here.
+                                           Logger.defaultLogError("Dependencies index update failed for model "
+                                                   + model + " and relation " + resource + ".", t);
+                                           t.printStackTrace();
+
+                                           // NOTE: Last resort: failure to update index
+                                           // properly results in removal of the whole index.
+                                           // This is the only thing that can be done
+                                           // at this point to ensure that the index will
+                                           // return correct results in the future, through
+                                           // complete reinitialization. 
+                                           //indexer.removeAll(null, session, DependenciesRelation.this, resource, model);
+                                       } finally {
+                                           Indexing.releaseIndexPending(pending);
+                                           Indexing.clearCaches(model);
+                                       }
+                                }
+                            }
+
+                               TimeLogger.log(DependenciesRelation.class, "trackAndIndex.onEvent: index update done");
+                           }
+                       }
+
+                   }
+
+               };
+
+               GraphChangeListenerSupport changeSupport = processor.getService(GraphChangeListenerSupport.class);
+               changeSupport.addMetadataListener(listener);
+
+           }
+
+           trackers++;
+
+       }
+
+       private boolean typeNameChanges(ReadGraph graph, IndexedRelations indexer,
+                       Resource model, final Collection<Pair<String, String>> typeChanges)
+                       throws DatabaseException {
+               if (typeChanges.isEmpty())
+                       return false;
+
+               for (Pair<String, String> nr : typeChanges) {
+                       String query = Dependencies.FIELD_RESOURCE + ":[" + nr.second + " TO " + nr.second + "]";
+                       //System.out.println("query: " + query);
+                       List<Map<String, Object>> results = indexer.query(null, query, graph, resource, model, Integer.MAX_VALUE);
+                       if (results.size() != 1) {
+                               return true;
+                       } else {
+                               Map<String, Object> result = results.get(0);
+                               if (!ObjectUtils.objectEquals(result.get(Dependencies.FIELD_NAME), nr.first)) {
+                                       return true;
+                               }
+                       }
+//                     System.err.println("Type " + nr.first + " was unchanged.");
+               }
+               return false;
+       }
+
+       @Override
+       public void addListener(RequestProcessor processor, Resource model, Runnable observer) {
+               DependenciesListenerStore store = processor.getSession().getService(DependenciesListenerStore.class);
+               store.addListener(model, observer);
+       }
+
+       @Override
+       public void removeListener(RequestProcessor processor, Resource model, Runnable observer) {
+               DependenciesListenerStore store = processor.getSession().getService(DependenciesListenerStore.class);
+               store.removeListener(model, observer);
+       }
+
+       void fireListeners(RequestProcessor processor, Resource model) {
+               DependenciesListenerStore store = processor.getSession().peekService(DependenciesListenerStore.class);
+               if (store != null)
+                       store.fireListeners(model);
+       }
+
+       @Override
+       public void reset(RequestProcessor processor, Resource input) {
+               if (DEBUG) {
+                       System.out.println("DependenciesRelation.reset: " + input);
+                       new Exception("DependenciesRelation.reset(" + listener + ")").printStackTrace(System.out);
+               }
+               DependenciesListenerStore store = processor.getSession().getService(DependenciesListenerStore.class);
+               store.fireListeners(input);
+       }
+
+       public static void addSubtree(ReadGraph graph, Resource root) throws DatabaseException {
+
+               Resource indexRoot = graph.syncRequest(new IndexRoot(root));
+               addSubtree(graph, indexRoot, root);
+
+       }
+
+       public static void addSubtree(ReadGraph graph, Resource indexRoot, Resource subtreeRoot) throws DatabaseException {
+               
+               DependenciesRelation dr = new DependenciesRelation(graph, indexRoot);
+        SerialisationSupport ss = graph.getService(SerialisationSupport.class);
+
+        ArrayList<Entry> entries = dr.find(graph, subtreeRoot);
+        entries.add(new Entry(graph, subtreeRoot));
+
+               ArrayList<Object[]> result = new ArrayList<Object[]>(entries.size());
+               for (Entry entry : entries) {
+                       result.add(new Object[] { ss.getRandomAccessId(entry.parent), ss.getRandomAccessId(entry.resource), entry.name, entry.types, entry.id });
+               }
+
+               Layer0X L0X = Layer0X.getInstance(graph);
+        IndexedRelations indexer = graph.getService(IndexedRelations.class);
+        indexer.insert(null, graph, dr, L0X.DependenciesRelation, indexRoot, result);
+               
+       }
+       
+}