--- /dev/null
+package org.simantics.issues.common;\r
+\r
+import java.util.ArrayList;\r
+import java.util.HashSet;\r
+import java.util.List;\r
+import java.util.Set;\r
+import java.util.concurrent.ConcurrentHashMap;\r
+import java.util.concurrent.ConcurrentMap;\r
+import java.util.concurrent.CopyOnWriteArraySet;\r
+\r
+import org.simantics.Simantics;\r
+import org.simantics.db.MetadataI;\r
+import org.simantics.db.ReadGraph;\r
+import org.simantics.db.Resource;\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.procedure.adapter.TransientCacheListener;\r
+import org.simantics.db.common.request.PossibleTypedParent;\r
+import org.simantics.db.common.utils.Functions;\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.genericrelation.DependenciesRelation.DependencyChangesRequest;\r
+import org.simantics.db.layer0.genericrelation.DependencyChanges;\r
+import org.simantics.db.service.GraphChangeListenerSupport;\r
+import org.simantics.issues.ontology.IssueResource;\r
+import org.simantics.layer0.Layer0;\r
+import org.simantics.scl.runtime.function.Function2;\r
+import org.simantics.simulation.ontology.SimulationResource;\r
+\r
+public class DependencyIssueSource2 implements IssueSource {\r
+\r
+ public static final boolean DEBUG = false;\r
+ \r
+ private Resource source;\r
+ private Resource model;\r
+ private Resource type;\r
+ private Set<Resource> searchTypes;\r
+ private Resource base;\r
+ private Resource extensionFunction;\r
+\r
+ private CopyOnWriteArraySet<Function2<ReadGraph, List<Resource>, Boolean>> listeners = new CopyOnWriteArraySet<>();\r
+ private ConcurrentMap<Function2<ReadGraph, List<Resource>, Boolean>, ChangeListener> listenerMap = new ConcurrentHashMap<>();\r
+\r
+ public DependencyIssueSource2(ReadGraph graph, Resource source) throws DatabaseException {\r
+ Layer0 L0 = Layer0.getInstance(graph);\r
+ IssueResource IR = IssueResource.getInstance(graph);\r
+ this.source = source;\r
+ this.model = graph.syncRequest(new PossibleTypedParent(source, SimulationResource.getInstance(graph).Model));\r
+ this.extensionFunction = graph.getPossibleObject(source, IR.Sources_DependencyTracker_HasExtension); \r
+ this.type = graph.getSingleObject(source, IR.Sources_DependencyTracker_HasType);\r
+ HashSet<Resource> _searchTypes = new HashSet<>();\r
+ _searchTypes.addAll(graph.getObjects(source, IR.Sources_DependencyTracker_HasSearchType));\r
+ _searchTypes.add(type);\r
+ this.searchTypes = new HashSet<>(_searchTypes);\r
+ Resource baseFunction = graph.getSingleObject(source, IR.Sources_DependencyTracker_HasBaseFunction);\r
+ this.base = Functions.exec(graph, baseFunction, graph, graph.getSingleObject(source, L0.PartOf));\r
+ }\r
+\r
+ private List<Resource> resourcesToCheck(ReadGraph graph, DependencyChanges event) throws DatabaseException {\r
+\r
+ HashSet<Resource> depSet = new HashSet<>();\r
+\r
+ if(DEBUG) {\r
+ System.err.println("resourcesToCheck[" + NameUtils.getSafeName(graph, source) + "] - component changes for type " + NameUtils.getSafeName(graph, searchTypes));\r
+ }\r
+\r
+ depSet.addAll(IssueSourceUtils.getChangedDependencies(graph, model, base, searchTypes, event));\r
+ depSet.addAll(IssueSourceUtils.getChangedDependencies(graph, source, model, base, searchTypes));\r
+ \r
+ List<Resource> deps = new ArrayList<>(depSet);\r
+ \r
+ if(DEBUG) {\r
+ System.err.println("resourcesToCheck[" + NameUtils.getSafeName(graph, source) + "] " + deps);\r
+ for(Resource r : deps) {\r
+ System.err.println("dep " + NameUtils.getSafeName(graph, r));\r
+ }\r
+ }\r
+ \r
+ if(extensionFunction != null) {\r
+ try {\r
+ deps = Functions.exec(graph, extensionFunction, graph, deps);\r
+ } catch (Throwable t) {\r
+ t.printStackTrace();\r
+ }\r
+ }\r
+\r
+ if(DEBUG) {\r
+ for(Resource r : deps) {\r
+ System.err.println("dep extension " + NameUtils.getSafeName(graph, r));\r
+ }\r
+ }\r
+ \r
+ ArrayList<Resource> result = new ArrayList<>();\r
+ for(Resource dep : deps) {\r
+ // TODO: still not complete - e.g. someone can replace the InstanceOf\r
+ Set<Resource> types = graph.getTypes(dep);\r
+ if(types.isEmpty() || types.contains(type)) result.add(dep);\r
+ }\r
+ return result;\r
+ \r
+ }\r
+\r
+ class DependencyChangeListener extends GenericChangeListener<DependencyChangesRequest, DependencyChanges> {\r
+\r
+ private boolean isValid(ReadGraph graph, List<Resource> toCheck) throws DatabaseException {\r
+ for(Resource resource : toCheck) {\r
+ if(!graph.syncRequest(new DependencyIssueValidator2(resource, model, source), TransientCacheListener.<Boolean>instance())) {\r
+ return false;\r
+ }\r
+ }\r
+ return true;\r
+ }\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
+ List<Resource> toCheck = resourcesToCheck(graph, event);\r
+ if(!isValid(graph, toCheck)) {\r
+ for(Function2<ReadGraph, List<Resource>, Boolean> r : listeners) {\r
+ r.apply(graph, toCheck);\r
+ }\r
+ }\r
+\r
+ if(graph instanceof WriteGraph) {\r
+ IssueSourceUtils.update((WriteGraph)graph, source);\r
+ }\r
+ }\r
+\r
+ }\r
+\r
+ @Override\r
+ public void addDirtyListener(Function2<ReadGraph, List<Resource>, Boolean> runnable) {\r
+ boolean added = listeners.add(runnable);\r
+ if (added) {\r
+ GraphChangeListenerSupport changeSupport = Simantics.getSession().getService(GraphChangeListenerSupport.class);\r
+ ChangeListener metadataListener = new DependencyChangeListener();\r
+ listenerMap.put(runnable, metadataListener);\r
+ changeSupport.addMetadataListener(metadataListener);\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public void removeDirtyListener(Function2<ReadGraph, List<Resource>, Boolean> runnable) {\r
+ boolean removed = listeners.remove(runnable);\r
+ ChangeListener metadataListener = listenerMap.remove(runnable);\r
+ if (removed && metadataListener != null) {\r
+ GraphChangeListenerSupport changeSupport = Simantics.getSession().getService(GraphChangeListenerSupport.class);\r
+ changeSupport.removeMetadataListener(metadataListener);\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public void update(WriteGraph graph, List<Resource> resources) throws DatabaseException {\r
+\r
+ for(Resource resource : resources) {\r
+ if(!graph.syncRequest(new DependencyIssueValidator2(resource, model, source), TransientCacheListener.<Boolean>instance())) {\r
+ new DependencyIssueSynchronizer2(resource, source).perform(graph);\r
+ }\r
+ }\r
+\r
+ IssueSourceUtils.update(graph, source);\r
+\r
+ }\r
+\r
+ @Override\r
+ public boolean needUpdate(ReadGraph graph, List<Resource> resources) throws DatabaseException {\r
+\r
+ for(Resource resource : resources) {\r
+ if(!graph.syncRequest(new DependencyIssueValidator2(resource, model, source), TransientCacheListener.<Boolean>instance())) return true;\r
+ }\r
+\r
+ return false;\r
+\r
+ }\r
+\r
+}\r