]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/genericrelation/DependenciesRelation.java
Added missing parts from SVN org.simantics.root project.
[simantics/platform.git] / bundles / org.simantics.db.layer0 / src / org / simantics / db / layer0 / genericrelation / DependenciesRelation.java
1 /*******************************************************************************\r
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
3  * in Industry THTH ry.\r
4  * All rights reserved. This program and the accompanying materials\r
5  * are made available under the terms of the Eclipse Public License v1.0\r
6  * which accompanies this distribution, and is available at\r
7  * http://www.eclipse.org/legal/epl-v10.html\r
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.db.layer0.genericrelation;\r
13 \r
14 import java.util.ArrayList;\r
15 import java.util.Arrays;\r
16 import java.util.Collection;\r
17 import java.util.Collections;\r
18 import java.util.HashSet;\r
19 import java.util.List;\r
20 import java.util.Map;\r
21 import java.util.UUID;\r
22 \r
23 import org.simantics.databoard.Bindings;\r
24 import org.simantics.databoard.util.ObjectUtils;\r
25 import org.simantics.datatypes.literal.GUID;\r
26 import org.simantics.db.AsyncReadGraph;\r
27 import org.simantics.db.ChangeSet;\r
28 import org.simantics.db.ChangeSet.StatementChange;\r
29 import org.simantics.db.MetadataI;\r
30 import org.simantics.db.ReadGraph;\r
31 import org.simantics.db.RequestProcessor;\r
32 import org.simantics.db.Resource;\r
33 import org.simantics.db.Session;\r
34 import org.simantics.db.Statement;\r
35 import org.simantics.db.WriteGraph;\r
36 import org.simantics.db.common.Indexing;\r
37 import org.simantics.db.common.changeset.GenericChangeListener;\r
38 import org.simantics.db.common.request.IndexRoot;\r
39 import org.simantics.db.common.request.ReadRequest;\r
40 import org.simantics.db.common.request.SuperTypeString;\r
41 import org.simantics.db.common.request.TypeString;\r
42 import org.simantics.db.common.request.UnaryRead;\r
43 import org.simantics.db.common.utils.Logger;\r
44 import org.simantics.db.common.utils.NameUtils;\r
45 import org.simantics.db.event.ChangeListener;\r
46 import org.simantics.db.exception.DatabaseException;\r
47 import org.simantics.db.layer0.adapter.GenericRelation;\r
48 import org.simantics.db.layer0.adapter.GenericRelationIndex;\r
49 import org.simantics.db.layer0.genericrelation.DependencyChanges.Change;\r
50 import org.simantics.db.layer0.genericrelation.DependencyChanges.ComponentAddition;\r
51 import org.simantics.db.layer0.genericrelation.DependencyChanges.ComponentModification;\r
52 import org.simantics.db.layer0.genericrelation.DependencyChanges.ComponentRemoval;\r
53 import org.simantics.db.layer0.genericrelation.DependencyChanges.LinkChange;\r
54 import org.simantics.db.procedure.AsyncContextMultiProcedure;\r
55 import org.simantics.db.procedure.AsyncContextProcedure;\r
56 import org.simantics.db.service.CollectionSupport;\r
57 import org.simantics.db.service.DirectQuerySupport;\r
58 import org.simantics.db.service.GraphChangeListenerSupport;\r
59 import org.simantics.db.service.ManagementSupport;\r
60 import org.simantics.db.service.SerialisationSupport;\r
61 import org.simantics.layer0.Layer0;\r
62 import org.simantics.operation.Layer0X;\r
63 import org.simantics.utils.datastructures.Pair;\r
64 import org.simantics.utils.logging.TimeLogger;\r
65 \r
66 public class DependenciesRelation extends UnsupportedRelation implements GenericRelationIndex {\r
67 \r
68         private static final boolean DEBUG = false;\r
69         static final boolean DEBUG_LISTENERS = false;\r
70         private static final boolean PROFILE = false;\r
71 \r
72         @SuppressWarnings("unchecked")\r
73         private final static Pair<String, String>[] fields = new Pair[] {\r
74                 Pair.make(Dependencies.FIELD_MODEL, "Long"),\r
75                 Pair.make(Dependencies.FIELD_PARENT, "Long"),\r
76                 Pair.make(Dependencies.FIELD_RESOURCE, "Long"),\r
77                 Pair.make(Dependencies.FIELD_NAME, "String"),\r
78                 Pair.make(Dependencies.FIELD_TYPES, "Text"),\r
79                 Pair.make(Dependencies.FIELD_GUID, "Text")\r
80         };\r
81 \r
82         final Resource resource;\r
83 \r
84         public DependenciesRelation(ReadGraph graph, Resource resource) {\r
85                 this.resource = resource;\r
86                 synchronized(this) {\r
87                         Session session = graph.getSession();\r
88                         DependenciesListenerStore store = session.peekService(DependenciesListenerStore.class);\r
89                         if(store == null) session.registerService(DependenciesListenerStore.class, new DependenciesListenerStore());\r
90                 }\r
91         }\r
92 \r
93         class Process {\r
94 \r
95                 final ArrayList<Entry> result = new ArrayList<Entry>();\r
96                 final AsyncContextMultiProcedure<Resource, Resource> structure;\r
97                 final AsyncContextProcedure<Entry, String> names;\r
98                 final AsyncContextProcedure<Entry, Resource> type;\r
99 \r
100                 Process(ReadGraph graph, final Resource resource) throws DatabaseException {\r
101 \r
102                         final Layer0 L0 = Layer0.getInstance(graph);\r
103                         final DirectQuerySupport dqs = graph.getService(DirectQuerySupport.class);\r
104                         final CollectionSupport cs = graph.getService(CollectionSupport.class);\r
105 \r
106                         names = dqs.compilePossibleRelatedValue(graph, L0.HasName, new AsyncContextProcedure<Entry, String>() {\r
107 \r
108                                 @Override\r
109                                 public void execute(AsyncReadGraph graph, Entry entry, String name) {\r
110                                         entry.name = name;\r
111                                 }\r
112 \r
113                                 @Override\r
114                                 public void exception(AsyncReadGraph graph, Throwable throwable) {\r
115                                         Logger.defaultLogError(throwable);\r
116                                 }\r
117 \r
118                         });\r
119 \r
120                         type = new AsyncContextProcedure<Entry, Resource>() {\r
121 \r
122                                 @Override\r
123                                 public void execute(AsyncReadGraph graph, Entry entry, Resource type) {\r
124                                         entry.principalType = type;\r
125                                 }\r
126 \r
127                                 @Override\r
128                                 public void exception(AsyncReadGraph graph, Throwable throwable) {\r
129                                         Logger.defaultLogError(throwable);\r
130                                 }\r
131 \r
132                         };\r
133 \r
134                         structure = dqs.compileForEachObject(graph, L0.ConsistsOf, new AsyncContextMultiProcedure<Resource, Resource>() {\r
135 \r
136                                 @Override\r
137                                 public void execute(AsyncReadGraph graph, Resource parent, Resource child) {\r
138                                         // WORKAROUND: don't browse virtual child resources\r
139                                         if(!child.isPersistent()) return;\r
140                                         Entry entry = new Entry(parent, child, "", "", "");\r
141                                         result.add(entry);\r
142                                         dqs.forEachObjectCompiled(graph, child, child, structure);\r
143                                         dqs.forPossibleRelatedValueCompiled(graph, child, entry, names);\r
144                                         dqs.forPossibleDirectType(graph, child, entry, type);\r
145                                 }\r
146 \r
147                                 @Override\r
148                                 public void finished(AsyncReadGraph graph) {\r
149                                 }\r
150 \r
151                                 @Override\r
152                                 public void exception(AsyncReadGraph graph, Throwable throwable) {\r
153                                         Logger.defaultLogError(throwable);\r
154                                 }\r
155 \r
156                         });\r
157 \r
158                         graph.syncRequest(new ReadRequest() {\r
159 \r
160                                 @Override\r
161                                 public void run(ReadGraph graph) throws DatabaseException {\r
162                                         dqs.forEachObjectCompiled(graph, resource, resource, structure);\r
163                                 }\r
164 \r
165                         });\r
166 \r
167             Map<Resource, String> typeStrings = cs.createMap(String.class);\r
168                         for(Entry e : result) {\r
169                                 if(e.principalType != null) {\r
170                                     String typeString = typeStrings.get(e.principalType);\r
171                                     if(typeString == null) {\r
172                                         typeString = graph.syncRequest(new SuperTypeString(e.principalType));\r
173                                         if (typeString.isEmpty()) {\r
174                                             Logger.defaultLogError(new DatabaseException("No name for type " + NameUtils.getURIOrSafeNameInternal(graph, e.resource) + " (" + e.resource + ")"));\r
175                                         }\r
176                                         typeStrings.put(e.principalType, typeString);\r
177                                     }\r
178                                     e.types = typeString;\r
179                                 } else {\r
180                                     e.types = graph.syncRequest(new TypeString(L0, graph.getTypes(e.resource)));\r
181                                 }\r
182                                 GUID id = graph.getPossibleRelatedValue(e.resource, L0.identifier, GUID.BINDING);\r
183                                 if(id != null)\r
184                                         e.id = id.indexString();\r
185                                 else \r
186                                         e.id = "";\r
187                         }\r
188 \r
189                         //SessionGarbageCollection.gc(null, graph.getSession(), false, null);\r
190                         \r
191                 }\r
192 \r
193         }\r
194 \r
195         public ArrayList<Entry> find(ReadGraph graph, final Resource model) throws DatabaseException {\r
196                 return new Process(graph, model).result;\r
197         }\r
198 \r
199         @Override\r
200         public GenericRelation select(String bindingPattern, Object[] constants) {\r
201                 checkSelectionArguments(bindingPattern, constants, new String[] { Dependencies.getBindingPattern() });\r
202                 final long subjectId = (Long)constants[0];\r
203                 return new UnsupportedRelation() {\r
204 \r
205                         @Override\r
206                         public boolean isRealizable() {\r
207                                 return true;\r
208                         }\r
209 \r
210                         @Override\r
211                         final public List<Object[]> realize(ReadGraph graph) throws DatabaseException {\r
212 \r
213                                 long time = System.nanoTime();\r
214 \r
215                 SerialisationSupport ss = graph.getService(SerialisationSupport.class);\r
216 \r
217                                 Resource subject = ss.getResource(subjectId); \r
218                                 \r
219                                 Collection<Entry> entries = find(graph, subject);\r
220 \r
221                                 long time2 = System.nanoTime();\r
222 \r
223                                 if (PROFILE)\r
224                                         System.out.println("Found " + entries.size() + " dependencies in " + 1e-6 * (time2 - time) + "ms for " + graph.getPossibleURI(subject) + ".");\r
225 \r
226                                 ArrayList<Object[]> result = new ArrayList<Object[]>();\r
227                                 for (Entry entry : entries) {\r
228                                         result.add(new Object[] { ss.getRandomAccessId(entry.parent), ss.getRandomAccessId(entry.resource), entry.name, entry.types, entry.id });\r
229                                 }\r
230                                 return result;\r
231 \r
232                         }\r
233 \r
234                 };\r
235         }\r
236 \r
237         @Override\r
238         public Pair<String, String>[] getFields() {\r
239                 return fields;\r
240         }\r
241 \r
242         @Override\r
243         public List<Map<String, Object>> query(RequestProcessor session, String search, String bindingPattern, Object[] constants, int maxResultCount) {\r
244                 if(!Dependencies.getBindingPattern().equals(bindingPattern)) throw new IllegalArgumentException("DependenciesRelation supports indexing only with 'bfffff'");\r
245                 IndexedRelations indexer = session.getService(IndexedRelations.class);\r
246                 return indexer.query(null, search, session, resource, (Resource)constants[0], maxResultCount);\r
247         }\r
248         \r
249         @Override\r
250         public List<Resource> queryResources(RequestProcessor session, String search, String bindingPattern, Object[] constants, int maxResultCount) {\r
251                 if(!Dependencies.getBindingPattern().equals(bindingPattern)) throw new IllegalArgumentException("DependenciesRelation supports indexing only with 'bfffff'");\r
252                 IndexedRelations indexer = session.getService(IndexedRelations.class);\r
253                 return indexer.queryResources(null, search, session, resource, (Resource)constants[0], maxResultCount);\r
254         }\r
255 \r
256         @Override\r
257         public List<Map<String, Object>> list(RequestProcessor session, String bindingPattern, Object[] constants, int maxResultCount) {\r
258                 if(!Dependencies.getBindingPattern().equals(bindingPattern)) throw new IllegalArgumentException("DependenciesRelation supports indexing only with 'bfffff'");\r
259                 IndexedRelations indexer = session.getService(IndexedRelations.class);\r
260                 return indexer.query(null, null, session, resource, (Resource)constants[0], maxResultCount);\r
261         }\r
262 \r
263         public static class DependencyChangesRequest extends UnaryRead<ChangeSet, DependencyChanges> {\r
264 \r
265                 @SuppressWarnings("unused")\r
266                 final private static boolean LOG = false;\r
267 \r
268                 public DependencyChangesRequest(ChangeSet parameter) {\r
269                         super(parameter);\r
270                 }\r
271 \r
272                 @Override\r
273                 public DependencyChanges perform(ReadGraph graph) throws DatabaseException {\r
274 \r
275                         DependencyChangesWriter w = new DependencyChangesWriter(graph);\r
276                         Layer0 l0 = w.l0;\r
277                         Resource changeInformation = graph.getPossibleResource("http://www.simantics.org/Modeling-1.2/changeInformation/Inverse");\r
278 \r
279                         for (Resource value : parameter.changedValues()) {\r
280                                 Statement modifiedComponent = graph.getPossibleStatement(value, l0.PropertyOf);\r
281                                 if (modifiedComponent == null\r
282                                                 || modifiedComponent.getPredicate().equals(changeInformation))\r
283                                         continue;\r
284                                 //System.err.println("+comp modi " + NameUtils.getSafeName(graph, renamedComponent, true));\r
285                                 w.addComponentModification(modifiedComponent.getObject());\r
286                         }\r
287                         for (Resource value : parameter.changedResources()) {\r
288                                 // No more info => need to check further\r
289                                 if(!graph.isImmutable(value))\r
290                                         w.addComponentModification(value);\r
291                         }\r
292                         for (StatementChange change : parameter.changedStatements()) {\r
293                                 //System.err.println("-stm " + NameUtils.getSafeName(graph, change.getSubject(), true) + " " + NameUtils.getSafeName(graph, change.getPredicate(), true) + " " + NameUtils.getSafeName(graph, change.getObject(), true));\r
294                                 Resource subject = change.getSubject();\r
295                                 Resource predicate = change.getPredicate();\r
296                                 Resource object = change.getObject();\r
297                                 if(!object.isPersistent()) continue;\r
298                                 if (predicate.equals(l0.ConsistsOf)) {\r
299                                         if (change.isClaim())\r
300                                                 w.addComponentAddition(subject, object);\r
301                                         else \r
302                                                 w.addComponentRemoval(subject, object);\r
303                                 } else if (predicate.equals(l0.IsLinkedTo)) {\r
304                                         w.addLinkChange(subject);\r
305                                 } else /*if (graph.isSubrelationOf(predicate, l0.DependsOn))*/ {\r
306                                         //System.err.println("-modi " + NameUtils.getSafeName(graph, subject, true));\r
307                                         w.addComponentModification(subject);\r
308                                 } \r
309                         }\r
310                         return w.getResult();\r
311                 }\r
312 \r
313         };\r
314 \r
315         private static int trackers = 0;\r
316         \r
317         private static ChangeListener listener;\r
318 \r
319         public static void assertFinishedTracking() {\r
320             if(trackers != 0) throw new IllegalStateException("Trackers should be 0 (was " + trackers + ")");\r
321         }\r
322         \r
323         @Override\r
324         public synchronized void untrack(RequestProcessor processor, final Resource model) {\r
325 \r
326             trackers--;\r
327             \r
328             if(trackers < 0) throw new IllegalStateException("Dependency tracking reference count is broken");\r
329             \r
330             if(trackers == 0) {\r
331                 \r
332                 if(listener == null) throw new IllegalStateException("Dependency tracking was not active");\r
333             \r
334                 GraphChangeListenerSupport changeSupport = processor.getService(GraphChangeListenerSupport.class);\r
335                 changeSupport.removeMetadataListener(listener);\r
336                 listener = null;\r
337                         \r
338             }\r
339             \r
340         }\r
341 \r
342         @Override\r
343         public synchronized void trackAndIndex(RequestProcessor processor, Resource model__) {\r
344 \r
345             if(trackers == 0) {\r
346 \r
347                 if(listener != null) throw new IllegalStateException("Dependency tracking was active");\r
348 \r
349                 listener = new GenericChangeListener<DependencyChangesRequest, DependencyChanges>() {\r
350 \r
351                     @Override\r
352                     public boolean preEventRequest() {\r
353                         return !Indexing.isDependenciesIndexingDisabled();\r
354                     }\r
355 \r
356                     @Override\r
357                     public void onEvent(ReadGraph graph, MetadataI metadata, DependencyChanges event) throws DatabaseException {\r
358 \r
359                         TimeLogger.log(DependenciesRelation.class, "trackAndIndex.onEvent: starting index update processing");\r
360 \r
361                         if(DEBUG)\r
362                             System.err.println("Adding metadata " + event + " in revision " + graph.getService(ManagementSupport.class).getHeadRevisionId());\r
363 \r
364                         WriteGraph w = (WriteGraph)graph;\r
365                         if(!event.isEmpty())\r
366                                 w.addMetadata(event);\r
367 \r
368                         final Session session = graph.getSession();\r
369                         final IndexedRelations indexer = session.getService(IndexedRelations.class);\r
370                         Layer0 L0 = Layer0.getInstance(graph);\r
371                         SerialisationSupport ss = graph.getService(SerialisationSupport.class);\r
372 \r
373                         for(Map.Entry<Resource, Change[]>  modelEntry : event.get().entrySet()) {\r
374 \r
375                             final Resource model = modelEntry.getKey();\r
376                             final Change[] changes = modelEntry.getValue();\r
377 \r
378                             boolean linkChange = false;\r
379 \r
380                             Collection<Object[]> _additions = Collections.emptyList();\r
381                             Collection<Object> _removals = Collections.emptyList();\r
382                             Collection<Object> _replacementKeys = Collections.emptyList();\r
383                             Collection<Object[]> _replacementObjects = Collections.emptyList();\r
384                             Collection<Pair<String, String>> _typeChanges = Collections.emptyList();\r
385 \r
386                             if(DEBUG) System.out.println("MODEL: " + NameUtils.getSafeLabel(graph, model));\r
387                             //                final Change[] changes = event.get(model);\r
388                             if(DEBUG) System.out.println("  CHANGES: " + Arrays.toString(changes));\r
389                             if (changes != null) {\r
390                                 _additions = new ArrayList<Object[]>();\r
391                                 _removals = new ArrayList<Object>();\r
392                                 _replacementKeys = new ArrayList<Object>();\r
393                                 _replacementObjects = new ArrayList<Object[]>();\r
394                                 _typeChanges = new HashSet<Pair<String, String>>();\r
395 \r
396                                 for (Change _entry : changes) {\r
397                                     if (_entry instanceof ComponentAddition) {\r
398                                         ComponentAddition entry = (ComponentAddition)_entry;\r
399                                         final String name = graph.getPossibleRelatedValue(entry.component, L0.HasName, Bindings.STRING);\r
400                                         final GUID id = graph.getPossibleRelatedValue(entry.component, L0.identifier, GUID.BINDING);\r
401                                         final String types = graph.syncRequest(new TypeString(L0, graph.getTypes(entry.component)));\r
402                                         if (name != null && types != null) {\r
403                                                 if(!entry.isValid(graph)) continue;\r
404                                             Resource parent = graph.getPossibleObject(entry.component, L0.PartOf);\r
405                                             if (parent != null) {\r
406                                                 _additions.add(new Object[] { ss.getRandomAccessId(parent), ss.getRandomAccessId(entry.component), name, types, id != null ? id.indexString() : "" });\r
407                                             } else {\r
408                                                     System.err.println("resource " + entry.component + ": no parent for entry " + name + " " + types);\r
409                                             }\r
410                                         } else {\r
411                                             System.err.println("resource " + entry.component + ": " + name + " " + types);\r
412                                         }\r
413                                     } else if(_entry instanceof ComponentModification) {\r
414                                         ComponentModification entry = (ComponentModification)_entry;\r
415                                         final String name = graph.getPossibleRelatedValue(entry.component, L0.HasName, Bindings.STRING);\r
416                                         final GUID id = graph.getPossibleRelatedValue(entry.component, L0.identifier, GUID.BINDING);\r
417                                         if(graph.isInstanceOf(entry.component, L0.Type)) {\r
418                                             SerialisationSupport support = session.getService(SerialisationSupport.class);\r
419                                             _typeChanges.add(new Pair<String, String>(name, String.valueOf(support.getRandomAccessId((Resource) entry.component))));\r
420                                         } else {\r
421                                             final String types = graph.syncRequest(new TypeString(L0, graph.getTypes(entry.component)));\r
422                                             if (name != null && types != null) {\r
423                                                 Resource part = graph.getPossibleObject(entry.component, L0.PartOf);\r
424                                                 if(part != null) {\r
425                                                     _replacementKeys.add(ss.getRandomAccessId(entry.component));\r
426                                                     _replacementObjects.add(new Object[] { ss.getRandomAccessId(part), \r
427                                                             ss.getRandomAccessId(entry.component), name, types, id != null ? id.indexString() : "" });\r
428                                                 }\r
429                                             }\r
430                                         }\r
431                                     } else if (_entry instanceof ComponentRemoval) {\r
432                                         ComponentRemoval entry = (ComponentRemoval)_entry;\r
433                                         if(!entry.isValid(graph)) continue;\r
434                                         _removals.add(ss.getRandomAccessId(((ComponentRemoval)_entry).component));\r
435                                     } else if (_entry instanceof LinkChange) {\r
436                                         linkChange = true;\r
437                                     }\r
438                                 }\r
439                             }\r
440 \r
441                             final boolean reset = linkChange || event.hasUnresolved;\r
442                             //System.err.println("dependencies(" + NameUtils.getSafeLabel(graph, model) + "): reset=" + reset + " linkChange=" + linkChange + " unresolved=" + event.hasUnresolved );\r
443 \r
444                             if (reset || !_additions.isEmpty() || !_removals.isEmpty() || !_replacementKeys.isEmpty() || !_typeChanges.isEmpty()) {\r
445 \r
446                                 TimeLogger.log(DependenciesRelation.class, "trackAndIndex.onEvent: starting index update");\r
447 \r
448                                 final Collection<Object[]> additions = _additions;\r
449                                 final Collection<Object> removals = _removals;\r
450                                 final Collection<Object> replacementKeys = _replacementKeys;\r
451                                 final Collection<Object[]> replacementObjects = _replacementObjects; \r
452                                 final boolean typeNameChanges = typeNameChanges(graph, indexer, model, _typeChanges);\r
453 \r
454                             final UUID pending = Indexing.makeIndexPending();\r
455 \r
456                             {\r
457                                 {\r
458                                         try {\r
459                                             boolean didChange = false;\r
460                                             // Unresolved and linkChanges are not relevant any more\r
461                                             boolean doReset = typeNameChanges;\r
462 \r
463                                             if (doReset) {\r
464 \r
465                                             if(DEBUG) {\r
466                                                 System.err.println("resetIndex " + reset + " " + typeNameChanges);\r
467                                             }\r
468 \r
469                                                 indexer.removeAll(null, graph, DependenciesRelation.this, resource, model);\r
470                                                 didChange = true;\r
471 \r
472                                             } else {\r
473 \r
474                                                 if (!replacementKeys.isEmpty() && (replacementKeys.size() == replacementObjects.size())) {\r
475                                                     if(DEBUG) {\r
476                                                         System.out.println(replacementKeys.size() + " index replacements: " + replacementKeys);\r
477                                                     }\r
478                                                     didChange |= indexer.replace(null, graph, DependenciesRelation.this, resource, model, Dependencies.FIELD_RESOURCE, replacementKeys, replacementObjects);\r
479                                                 }\r
480                                                 if (!removals.isEmpty()) {\r
481                                                     if(DEBUG) {\r
482                                                         System.out.println(removals.size() + " index removals: " + removals);\r
483                                                     }\r
484                                                     indexer.remove(null, graph, DependenciesRelation.this, resource, model, Dependencies.FIELD_RESOURCE, removals);\r
485                                                     didChange = true;\r
486                                                 }\r
487                                                 if (!additions.isEmpty()) {\r
488                                                     if(DEBUG) {\r
489                                                         for(Object[] os : additions) System.err.println("Adding to index " + model + ": " + Arrays.toString(os));\r
490                                                     }\r
491                                                     //System.out.println(additions.size() + " index insertions");\r
492                                                     indexer.insert(null, graph, DependenciesRelation.this, resource, model, additions);\r
493                                                     didChange = true;\r
494                                                 }\r
495 \r
496                                             }\r
497 \r
498                                             if (didChange)\r
499                                                 // TODO: because this data is ran with\r
500                                                 // ThreadUtils.getBlockingWorkExecutor()\r
501                                                 // fireListeners needs to use peekService,\r
502                                                 // not getService since there is no\r
503                                                 // guarantee that the session isn't being\r
504                                                 // disposed while this method is executing.\r
505                                                 fireListeners(graph, model);\r
506 \r
507                                         } catch (Throwable t) {\r
508                                             // Just to know if something unexpected happens here.\r
509                                             Logger.defaultLogError("Dependencies index update failed for model "\r
510                                                     + model + " and relation " + resource + ".", t);\r
511                                             t.printStackTrace();\r
512 \r
513                                             // NOTE: Last resort: failure to update index\r
514                                             // properly results in removal of the whole index.\r
515                                             // This is the only thing that can be done\r
516                                             // at this point to ensure that the index will\r
517                                             // return correct results in the future, through\r
518                                             // complete reinitialization. \r
519                                             //indexer.removeAll(null, session, DependenciesRelation.this, resource, model);\r
520                                         } finally {\r
521                                             Indexing.releaseIndexPending(pending);\r
522                                             Indexing.clearCaches(model);\r
523                                         }\r
524                                 }\r
525                             }\r
526 \r
527                                 TimeLogger.log(DependenciesRelation.class, "trackAndIndex.onEvent: index update done");\r
528                             }\r
529                         }\r
530 \r
531                     }\r
532 \r
533                 };\r
534 \r
535                 GraphChangeListenerSupport changeSupport = processor.getService(GraphChangeListenerSupport.class);\r
536                 changeSupport.addMetadataListener(listener);\r
537 \r
538             }\r
539 \r
540             trackers++;\r
541 \r
542         }\r
543 \r
544         private boolean typeNameChanges(ReadGraph graph, IndexedRelations indexer,\r
545                         Resource model, final Collection<Pair<String, String>> typeChanges)\r
546                         throws DatabaseException {\r
547                 if (typeChanges.isEmpty())\r
548                         return false;\r
549 \r
550                 for (Pair<String, String> nr : typeChanges) {\r
551                         String query = Dependencies.FIELD_RESOURCE + ":[" + nr.second + " TO " + nr.second + "]";\r
552                         //System.out.println("query: " + query);\r
553                         List<Map<String, Object>> results = indexer.query(null, query, graph, resource, model, Integer.MAX_VALUE);\r
554                         if (results.size() != 1) {\r
555                                 return true;\r
556                         } else {\r
557                                 Map<String, Object> result = results.get(0);\r
558                                 if (!ObjectUtils.objectEquals(result.get(Dependencies.FIELD_NAME), nr.first)) {\r
559                                         return true;\r
560                                 }\r
561                         }\r
562 //                      System.err.println("Type " + nr.first + " was unchanged.");\r
563                 }\r
564                 return false;\r
565         }\r
566 \r
567         @Override\r
568         public void addListener(RequestProcessor processor, Resource model, Runnable observer) {\r
569                 DependenciesListenerStore store = processor.getSession().getService(DependenciesListenerStore.class);\r
570                 store.addListener(model, observer);\r
571         }\r
572 \r
573         @Override\r
574         public void removeListener(RequestProcessor processor, Resource model, Runnable observer) {\r
575                 DependenciesListenerStore store = processor.getSession().getService(DependenciesListenerStore.class);\r
576                 store.removeListener(model, observer);\r
577         }\r
578 \r
579         void fireListeners(RequestProcessor processor, Resource model) {\r
580                 DependenciesListenerStore store = processor.getSession().peekService(DependenciesListenerStore.class);\r
581                 if (store != null)\r
582                         store.fireListeners(model);\r
583         }\r
584 \r
585         @Override\r
586         public void reset(RequestProcessor processor, Resource input) {\r
587                 if (DEBUG) {\r
588                         System.out.println("DependenciesRelation.reset: " + input);\r
589                         new Exception("DependenciesRelation.reset(" + listener + ")").printStackTrace(System.out);\r
590                 }\r
591                 DependenciesListenerStore store = processor.getSession().getService(DependenciesListenerStore.class);\r
592                 store.fireListeners(input);\r
593         }\r
594 \r
595         public static void addSubtree(ReadGraph graph, Resource root) throws DatabaseException {\r
596 \r
597                 Resource indexRoot = graph.syncRequest(new IndexRoot(root));\r
598                 addSubtree(graph, indexRoot, root);\r
599 \r
600         }\r
601 \r
602         public static void addSubtree(ReadGraph graph, Resource indexRoot, Resource subtreeRoot) throws DatabaseException {\r
603                 \r
604                 DependenciesRelation dr = new DependenciesRelation(graph, indexRoot);\r
605         SerialisationSupport ss = graph.getService(SerialisationSupport.class);\r
606 \r
607         ArrayList<Entry> entries = dr.find(graph, subtreeRoot);\r
608         entries.add(new Entry(graph, subtreeRoot));\r
609 \r
610                 ArrayList<Object[]> result = new ArrayList<Object[]>(entries.size());\r
611                 for (Entry entry : entries) {\r
612                         result.add(new Object[] { ss.getRandomAccessId(entry.parent), ss.getRandomAccessId(entry.resource), entry.name, entry.types, entry.id });\r
613                 }\r
614 \r
615                 Layer0X L0X = Layer0X.getInstance(graph);\r
616         IndexedRelations indexer = graph.getService(IndexedRelations.class);\r
617         indexer.insert(null, graph, dr, L0X.DependenciesRelation, indexRoot, result);\r
618                 \r
619         }\r
620         \r
621 }\r