1 package org.simantics.issues.common;
3 import java.util.ArrayList;
4 import java.util.HashSet;
7 import java.util.concurrent.ConcurrentHashMap;
8 import java.util.concurrent.ConcurrentMap;
9 import java.util.concurrent.CopyOnWriteArraySet;
11 import org.simantics.Simantics;
12 import org.simantics.db.MetadataI;
13 import org.simantics.db.ReadGraph;
14 import org.simantics.db.Resource;
15 import org.simantics.db.WriteGraph;
16 import org.simantics.db.common.Indexing;
17 import org.simantics.db.common.changeset.GenericChangeListener;
18 import org.simantics.db.common.procedure.adapter.TransientCacheListener;
19 import org.simantics.db.common.request.PossibleTypedParent;
20 import org.simantics.db.common.utils.Functions;
21 import org.simantics.db.common.utils.NameUtils;
22 import org.simantics.db.event.ChangeListener;
23 import org.simantics.db.exception.DatabaseException;
24 import org.simantics.db.layer0.genericrelation.DependenciesRelation.DependencyChangesRequest;
25 import org.simantics.db.layer0.genericrelation.DependencyChanges;
26 import org.simantics.db.service.GraphChangeListenerSupport;
27 import org.simantics.issues.ontology.IssueResource;
28 import org.simantics.layer0.Layer0;
29 import org.simantics.scl.runtime.function.Function2;
30 import org.simantics.simulation.ontology.SimulationResource;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
34 public class DependencyIssueSource2 implements IssueSource {
36 private static final Logger LOGGER = LoggerFactory.getLogger(DependencyIssueSource2.class);
37 public static final boolean DEBUG = false;
39 private Resource source;
40 private Resource model;
41 private Resource type;
42 private Set<Resource> searchTypes;
43 private Resource base;
44 private Resource extensionFunction;
46 private CopyOnWriteArraySet<Function2<ReadGraph, List<Resource>, Boolean>> listeners = new CopyOnWriteArraySet<>();
47 private ConcurrentMap<Function2<ReadGraph, List<Resource>, Boolean>, ChangeListener> listenerMap = new ConcurrentHashMap<>();
49 public DependencyIssueSource2(ReadGraph graph, Resource source) throws DatabaseException {
50 Layer0 L0 = Layer0.getInstance(graph);
51 IssueResource IR = IssueResource.getInstance(graph);
53 this.model = graph.syncRequest(new PossibleTypedParent(source, SimulationResource.getInstance(graph).Model));
54 this.extensionFunction = graph.getPossibleObject(source, IR.Sources_DependencyTracker_HasExtension);
55 this.type = graph.getSingleObject(source, IR.Sources_DependencyTracker_HasType);
56 HashSet<Resource> _searchTypes = new HashSet<>();
57 _searchTypes.addAll(graph.getObjects(source, IR.Sources_DependencyTracker_HasSearchType));
58 _searchTypes.add(type);
59 this.searchTypes = new HashSet<>(_searchTypes);
60 Resource baseFunction = graph.getSingleObject(source, IR.Sources_DependencyTracker_HasBaseFunction);
61 this.base = Functions.exec(graph, baseFunction, graph, graph.getSingleObject(source, L0.PartOf));
64 private List<Resource> resourcesToCheck(ReadGraph graph, DependencyChanges event) throws DatabaseException {
66 HashSet<Resource> depSet = new HashSet<>();
69 LOGGER.info("resourcesToCheck[" + NameUtils.getSafeName(graph, source) + "] - component changes for type " + NameUtils.getSafeName(graph, searchTypes));
72 depSet.addAll(IssueSourceUtils.getChangedDependencies(graph, model, base, searchTypes, event));
73 depSet.addAll(IssueSourceUtils.getChangedDependencies(graph, source, model, base, searchTypes));
75 List<Resource> deps = new ArrayList<>(depSet);
78 LOGGER.info("resourcesToCheck[" + NameUtils.getSafeName(graph, source) + "] " + deps);
79 for(Resource r : deps) {
80 LOGGER.info("dep " + NameUtils.getSafeName(graph, r));
84 if(extensionFunction != null) {
86 deps = Functions.exec(graph, extensionFunction, graph, deps);
87 } catch (Throwable t) {
93 for(Resource r : deps) {
94 LOGGER.info("dep extension " + NameUtils.getSafeName(graph, r));
98 ArrayList<Resource> result = new ArrayList<>();
99 for(Resource dep : deps) {
100 // TODO: still not complete - e.g. someone can replace the InstanceOf
101 Set<Resource> types = graph.getTypes(dep);
102 if(types.isEmpty() || types.contains(type)) result.add(dep);
108 class DependencyChangeListener extends GenericChangeListener<DependencyChangesRequest, DependencyChanges> {
110 private boolean isValid(ReadGraph graph, List<Resource> toCheck) throws DatabaseException {
111 for(Resource resource : toCheck) {
112 if(!graph.syncRequest(new DependencyIssueValidator2(resource, model, source), TransientCacheListener.<Boolean>instance())) {
120 public boolean preEventRequest() {
121 return !Indexing.isDependenciesIndexingDisabled();
125 public void onEvent(ReadGraph graph, MetadataI metadata, DependencyChanges event) throws DatabaseException {
127 List<Resource> toCheck = resourcesToCheck(graph, event);
128 if(!isValid(graph, toCheck)) {
129 for(Function2<ReadGraph, List<Resource>, Boolean> r : listeners) {
130 r.apply(graph, toCheck);
134 if(graph instanceof WriteGraph) {
135 IssueSourceUtils.update((WriteGraph)graph, source);
142 public void addDirtyListener(Function2<ReadGraph, List<Resource>, Boolean> runnable) {
143 boolean added = listeners.add(runnable);
145 GraphChangeListenerSupport changeSupport = Simantics.getSession().getService(GraphChangeListenerSupport.class);
146 ChangeListener metadataListener = new DependencyChangeListener();
147 listenerMap.put(runnable, metadataListener);
148 changeSupport.addMetadataListener(metadataListener);
153 public void removeDirtyListener(Function2<ReadGraph, List<Resource>, Boolean> runnable) {
154 boolean removed = listeners.remove(runnable);
155 ChangeListener metadataListener = listenerMap.remove(runnable);
156 if (removed && metadataListener != null) {
157 GraphChangeListenerSupport changeSupport = Simantics.getSession().getService(GraphChangeListenerSupport.class);
158 changeSupport.removeMetadataListener(metadataListener);
163 public void update(WriteGraph graph, List<Resource> resources) throws DatabaseException {
165 for(Resource resource : resources) {
166 if(!graph.syncRequest(new DependencyIssueValidator2(resource, model, source), TransientCacheListener.<Boolean>instance())) {
167 new DependencyIssueSynchronizer2(resource, source).perform(graph);
171 IssueSourceUtils.update(graph, source);
176 public boolean needUpdate(ReadGraph graph, List<Resource> resources) throws DatabaseException {
178 for(Resource resource : resources) {
179 if(!graph.syncRequest(new DependencyIssueValidator2(resource, model, source), TransientCacheListener.<Boolean>instance())) return true;