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
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.db.layer0.genericrelation;
\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
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
66 public class DependenciesRelation extends UnsupportedRelation implements GenericRelationIndex {
\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
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
82 final Resource resource;
\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
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
100 Process(ReadGraph graph, final Resource resource) throws DatabaseException {
\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
106 names = dqs.compilePossibleRelatedValue(graph, L0.HasName, new AsyncContextProcedure<Entry, String>() {
\r
109 public void execute(AsyncReadGraph graph, Entry entry, String name) {
\r
114 public void exception(AsyncReadGraph graph, Throwable throwable) {
\r
115 Logger.defaultLogError(throwable);
\r
120 type = new AsyncContextProcedure<Entry, Resource>() {
\r
123 public void execute(AsyncReadGraph graph, Entry entry, Resource type) {
\r
124 entry.principalType = type;
\r
128 public void exception(AsyncReadGraph graph, Throwable throwable) {
\r
129 Logger.defaultLogError(throwable);
\r
134 structure = dqs.compileForEachObject(graph, L0.ConsistsOf, new AsyncContextMultiProcedure<Resource, Resource>() {
\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
142 dqs.forEachObjectCompiled(graph, child, child, structure);
\r
143 dqs.forPossibleRelatedValueCompiled(graph, child, entry, names);
\r
144 dqs.forPossibleDirectType(graph, child, entry, type);
\r
148 public void finished(AsyncReadGraph graph) {
\r
152 public void exception(AsyncReadGraph graph, Throwable throwable) {
\r
153 Logger.defaultLogError(throwable);
\r
158 graph.syncRequest(new ReadRequest() {
\r
161 public void run(ReadGraph graph) throws DatabaseException {
\r
162 dqs.forEachObjectCompiled(graph, resource, resource, structure);
\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
176 typeStrings.put(e.principalType, typeString);
\r
178 e.types = typeString;
\r
180 e.types = graph.syncRequest(new TypeString(L0, graph.getTypes(e.resource)));
\r
182 GUID id = graph.getPossibleRelatedValue(e.resource, L0.identifier, GUID.BINDING);
\r
184 e.id = id.indexString();
\r
189 //SessionGarbageCollection.gc(null, graph.getSession(), false, null);
\r
195 public ArrayList<Entry> find(ReadGraph graph, final Resource model) throws DatabaseException {
\r
196 return new Process(graph, model).result;
\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
206 public boolean isRealizable() {
\r
211 final public List<Object[]> realize(ReadGraph graph) throws DatabaseException {
\r
213 long time = System.nanoTime();
\r
215 SerialisationSupport ss = graph.getService(SerialisationSupport.class);
\r
217 Resource subject = ss.getResource(subjectId);
\r
219 Collection<Entry> entries = find(graph, subject);
\r
221 long time2 = System.nanoTime();
\r
224 System.out.println("Found " + entries.size() + " dependencies in " + 1e-6 * (time2 - time) + "ms for " + graph.getPossibleURI(subject) + ".");
\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
238 public Pair<String, String>[] getFields() {
\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
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
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
263 public static class DependencyChangesRequest extends UnaryRead<ChangeSet, DependencyChanges> {
\r
265 @SuppressWarnings("unused")
\r
266 final private static boolean LOG = false;
\r
268 public DependencyChangesRequest(ChangeSet parameter) {
\r
273 public DependencyChanges perform(ReadGraph graph) throws DatabaseException {
\r
275 DependencyChangesWriter w = new DependencyChangesWriter(graph);
\r
277 Resource changeInformation = graph.getPossibleResource("http://www.simantics.org/Modeling-1.2/changeInformation/Inverse");
\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
284 //System.err.println("+comp modi " + NameUtils.getSafeName(graph, renamedComponent, true));
\r
285 w.addComponentModification(modifiedComponent.getObject());
\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
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
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
310 return w.getResult();
\r
315 private static int trackers = 0;
\r
317 private static ChangeListener listener;
\r
319 public static void assertFinishedTracking() {
\r
320 if(trackers != 0) throw new IllegalStateException("Trackers should be 0 (was " + trackers + ")");
\r
324 public synchronized void untrack(RequestProcessor processor, final Resource model) {
\r
328 if(trackers < 0) throw new IllegalStateException("Dependency tracking reference count is broken");
\r
330 if(trackers == 0) {
\r
332 if(listener == null) throw new IllegalStateException("Dependency tracking was not active");
\r
334 GraphChangeListenerSupport changeSupport = processor.getService(GraphChangeListenerSupport.class);
\r
335 changeSupport.removeMetadataListener(listener);
\r
343 public synchronized void trackAndIndex(RequestProcessor processor, Resource model__) {
\r
345 if(trackers == 0) {
\r
347 if(listener != null) throw new IllegalStateException("Dependency tracking was active");
\r
349 listener = new GenericChangeListener<DependencyChangesRequest, DependencyChanges>() {
\r
352 public boolean preEventRequest() {
\r
353 return !Indexing.isDependenciesIndexingDisabled();
\r
357 public void onEvent(ReadGraph graph, MetadataI metadata, DependencyChanges event) throws DatabaseException {
\r
359 TimeLogger.log(DependenciesRelation.class, "trackAndIndex.onEvent: starting index update processing");
\r
362 System.err.println("Adding metadata " + event + " in revision " + graph.getService(ManagementSupport.class).getHeadRevisionId());
\r
364 WriteGraph w = (WriteGraph)graph;
\r
365 if(!event.isEmpty())
\r
366 w.addMetadata(event);
\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
373 for(Map.Entry<Resource, Change[]> modelEntry : event.get().entrySet()) {
\r
375 final Resource model = modelEntry.getKey();
\r
376 final Change[] changes = modelEntry.getValue();
\r
378 boolean linkChange = false;
\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
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
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
408 System.err.println("resource " + entry.component + ": no parent for entry " + name + " " + types);
\r
411 System.err.println("resource " + entry.component + ": " + name + " " + types);
\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
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
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
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
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
444 if (reset || !_additions.isEmpty() || !_removals.isEmpty() || !_replacementKeys.isEmpty() || !_typeChanges.isEmpty()) {
\r
446 TimeLogger.log(DependenciesRelation.class, "trackAndIndex.onEvent: starting index update");
\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
454 final UUID pending = Indexing.makeIndexPending();
\r
459 boolean didChange = false;
\r
460 // Unresolved and linkChanges are not relevant any more
\r
461 boolean doReset = typeNameChanges;
\r
466 System.err.println("resetIndex " + reset + " " + typeNameChanges);
\r
469 indexer.removeAll(null, graph, DependenciesRelation.this, resource, model);
\r
474 if (!replacementKeys.isEmpty() && (replacementKeys.size() == replacementObjects.size())) {
\r
476 System.out.println(replacementKeys.size() + " index replacements: " + replacementKeys);
\r
478 didChange |= indexer.replace(null, graph, DependenciesRelation.this, resource, model, Dependencies.FIELD_RESOURCE, replacementKeys, replacementObjects);
\r
480 if (!removals.isEmpty()) {
\r
482 System.out.println(removals.size() + " index removals: " + removals);
\r
484 indexer.remove(null, graph, DependenciesRelation.this, resource, model, Dependencies.FIELD_RESOURCE, removals);
\r
487 if (!additions.isEmpty()) {
\r
489 for(Object[] os : additions) System.err.println("Adding to index " + model + ": " + Arrays.toString(os));
\r
491 //System.out.println(additions.size() + " index insertions");
\r
492 indexer.insert(null, graph, DependenciesRelation.this, resource, model, additions);
\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
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
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
521 Indexing.releaseIndexPending(pending);
\r
522 Indexing.clearCaches(model);
\r
527 TimeLogger.log(DependenciesRelation.class, "trackAndIndex.onEvent: index update done");
\r
535 GraphChangeListenerSupport changeSupport = processor.getService(GraphChangeListenerSupport.class);
\r
536 changeSupport.addMetadataListener(listener);
\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
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
557 Map<String, Object> result = results.get(0);
\r
558 if (!ObjectUtils.objectEquals(result.get(Dependencies.FIELD_NAME), nr.first)) {
\r
562 // System.err.println("Type " + nr.first + " was unchanged.");
\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
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
579 void fireListeners(RequestProcessor processor, Resource model) {
\r
580 DependenciesListenerStore store = processor.getSession().peekService(DependenciesListenerStore.class);
\r
582 store.fireListeners(model);
\r
586 public void reset(RequestProcessor processor, Resource input) {
\r
588 System.out.println("DependenciesRelation.reset: " + input);
\r
589 new Exception("DependenciesRelation.reset(" + listener + ")").printStackTrace(System.out);
\r
591 DependenciesListenerStore store = processor.getSession().getService(DependenciesListenerStore.class);
\r
592 store.fireListeners(input);
\r
595 public static void addSubtree(ReadGraph graph, Resource root) throws DatabaseException {
\r
597 Resource indexRoot = graph.syncRequest(new IndexRoot(root));
\r
598 addSubtree(graph, indexRoot, root);
\r
602 public static void addSubtree(ReadGraph graph, Resource indexRoot, Resource subtreeRoot) throws DatabaseException {
\r
604 DependenciesRelation dr = new DependenciesRelation(graph, indexRoot);
\r
605 SerialisationSupport ss = graph.getService(SerialisationSupport.class);
\r
607 ArrayList<Entry> entries = dr.find(graph, subtreeRoot);
\r
608 entries.add(new Entry(graph, subtreeRoot));
\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
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