From: jsimomaa Date: Tue, 31 Oct 2017 10:54:21 +0000 (+0200) Subject: Don't recompile all expressions if only one is modified X-Git-Tag: v1.31.0~73 X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=commitdiff_plain;h=refs%2Fchanges%2F69%2F1169%2F3;p=simantics%2Fplatform.git Don't recompile all expressions if only one is modified in SCLExpressionIssueProvider refs #7575 Change-Id: I5e27b164f79320ad6a54215f9aaef6dbe77f2fa5 --- diff --git a/bundles/org.simantics.modeling/src/org/simantics/modeling/scl/issue/SCLExpressionIssueProvider.java b/bundles/org.simantics.modeling/src/org/simantics/modeling/scl/issue/SCLExpressionIssueProvider.java index bbbd6b016..fbba1546f 100644 --- a/bundles/org.simantics.modeling/src/org/simantics/modeling/scl/issue/SCLExpressionIssueProvider.java +++ b/bundles/org.simantics.modeling/src/org/simantics/modeling/scl/issue/SCLExpressionIssueProvider.java @@ -1,17 +1,25 @@ package org.simantics.modeling.scl.issue; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Objects; import java.util.Set; import java.util.TreeSet; +import java.util.concurrent.ConcurrentHashMap; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.simantics.Simantics; +import org.simantics.db.Disposable; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; +import org.simantics.db.common.procedure.adapter.DisposableListener; +import org.simantics.db.common.procedure.adapter.DisposableSyncListener; import org.simantics.db.common.procedure.adapter.SyncListenerAdapter; +import org.simantics.db.common.request.TernaryRead; +import org.simantics.db.common.request.UnaryRead; import org.simantics.db.common.request.UniqueRead; import org.simantics.db.exception.DatabaseException; import org.simantics.db.layer0.util.Layer0Utils; @@ -43,123 +51,326 @@ public class SCLExpressionIssueProvider implements SCLIssueProvider { } private static final Logger LOGGER = LoggerFactory.getLogger(SCLExpressionIssueProvider.class); - private List currentIssues = new ArrayList<>(); private boolean disposed = false; + private ComponentSyncListenerAdapter listener; SCLExpressionIssueProvider() { } @Override public void listenIssues(Runnable callback) { - Simantics.getSession().asyncRequest(new UniqueRead>() { - - @Override - public List perform(ReadGraph graph) throws DatabaseException { - Layer0 L0 = Layer0.getInstance(graph); - Set indexRoots = new TreeSet(); - for(Resource ontology : Layer0Utils.listOntologies(graph)) { - if (graph.isInstanceOf(ontology, L0.SharedOntology)) { - indexRoots.add(ontology); - } + listener = new ComponentSyncListenerAdapter(callback); + Simantics.getSession().asyncRequest(new ComponentRequest(), listener); + } + + @Override + public List getIssues() { + return listener.getIssues(); + } + + @Override + public void dispose() { + listener.dispose(); + disposed = true; + } + + private static void openResource(Shell shell, Resource resource) { + DefaultActions.performDefaultAction(shell, new StructuredSelection(resource)); + } + + private static class ComponentRequest extends UniqueRead> { + + @Override + public Set perform(ReadGraph graph) throws DatabaseException { + Layer0 L0 = Layer0.getInstance(graph); + Set indexRoots = new TreeSet(); + for(Resource ontology : Layer0Utils.listOntologies(graph)) { + if (graph.isInstanceOf(ontology, L0.SharedOntology)) { + indexRoots.add(ontology); } + } - for(Resource child : graph.getObjects(Simantics.getProjectResource(), L0.ConsistsOf)) { - if (graph.isInstanceOf(child, L0.IndexRoot)) { - indexRoots.add(child); - } + for(Resource child : graph.getObjects(Simantics.getProjectResource(), L0.ConsistsOf)) { + if (graph.isInstanceOf(child, L0.IndexRoot)) { + indexRoots.add(child); } + } - StructuralResource2 STR = StructuralResource2.getInstance(graph); - - List results = new ArrayList<>(); - - for (Resource ontology : indexRoots) { - List components = ModelingUtils.searchByTypeShallow(graph, ontology, STR.Component); - for (Resource component : components) { - - for (Resource predicate : graph.getPredicates(component)) { - if (graph.isSubrelationOf(predicate, L0.HasProperty)) { - for (Resource object : graph.getObjects(component, predicate)) { - if (graph.isInstanceOf(object, L0.SCLValue)) { - Resource type = graph.getPossibleType(object, L0.SCLValue); - Variable typeVariable = Variables.getVariable(graph, type); - - Function1 func = typeVariable.getPossiblePropertyValue(graph, "validator"); - if (func == null) { - // No validator available - if (LOGGER.isTraceEnabled()) - LOGGER.trace("No validator available for " + typeVariable.getURI(graph)); - continue; - } - - Variable componentVariable = Variables.getVariable(graph, component); - Variable propertyVariable = componentVariable.getProperty(graph, predicate); - - SCLContext sclContext = SCLContext.getCurrent(); - Object oldGraph = sclContext.get("graph"); - try { - sclContext.put("graph", graph); - String validatorValue = func.apply(propertyVariable); - if (validatorValue != null && !validatorValue.isEmpty()) { - results.add(new SCLIssuesTableEntry(propertyVariable.getURI(graph), new CompilationError(Locations.NO_LOCATION, validatorValue.replace("\n", " "))) { - @Override - public void openLocation() { - openResource(Display.getCurrent().getActiveShell(), component); - } - }); - } - } catch (Throwable t) { - LOGGER.error("Failed to invoke type validator function " + func, t); - } finally { - sclContext.put("graph", oldGraph); - } - } - } - } + StructuralResource2 STR = StructuralResource2.getInstance(graph); + + Set allComponents = new HashSet<>(); + for (Resource ontology : indexRoots) { + List components = ModelingUtils.searchByTypeShallow(graph, ontology, STR.Component); + allComponents.addAll(components); + } + return allComponents; + } + } + + private static class SCLValueRequest extends UnaryRead> { + + public SCLValueRequest(Resource parameter) { + super(parameter); + } + + @Override + public Set perform(ReadGraph graph) throws DatabaseException { + Layer0 L0 = Layer0.getInstance(graph); + Set results = new HashSet<>(); + for (Resource predicate : graph.getPredicates(parameter)) { + if (graph.isSubrelationOf(predicate, L0.HasProperty)) { + for (Resource object : graph.getObjects(parameter, predicate)) { + if (graph.isInstanceOf(object, L0.SCLValue)) { + results.add(new ResourceHolder(parameter, predicate, object)); } } } - return results; } - }, new SyncListenerAdapter>() { + return results; + } + } + + private static class SCLExpressionValidationRequest extends TernaryRead { - @Override - public void execute(ReadGraph graph, List result) { - synchronized (currentIssues) { - currentIssues.clear(); - currentIssues.addAll(result); - } - if (callback != null) - callback.run(); + public SCLExpressionValidationRequest(Resource component, Resource predicate, Resource object) { + super(component, predicate, object); + } + + @Override + public SCLIssuesTableEntry perform(ReadGraph graph) throws DatabaseException { + Resource type = graph.getPossibleType(parameter3, Layer0.getInstance(graph).SCLValue); + if (type == null) { + return null; } + if (!graph.hasStatement(parameter)) + return null; + + Variable componentVariable = Variables.getVariable(graph, parameter); + Variable propertyVariable = componentVariable.getProperty(graph, parameter2); + + Variable typeVariable = Variables.getVariable(graph, type); - @Override - public void exception(ReadGraph graph, Throwable t) { - LOGGER.error("Could not get SCL issues", t); + Function1 func = typeVariable.getPossiblePropertyValue(graph, "validator"); + if (func == null) { + // No validator available + if (LOGGER.isTraceEnabled()) + LOGGER.trace("No validator available for " + typeVariable.getURI(graph)); + return null; } - @Override - public boolean isDisposed() { - return disposed; + SCLContext sclContext = SCLContext.getCurrent(); + Object oldGraph = sclContext.get("graph"); + try { + sclContext.put("graph", graph); + String validatorValue = func.apply(propertyVariable); + if (validatorValue != null && !validatorValue.isEmpty()) { + return new SCLIssuesTableEntry(propertyVariable.getURI(graph), new CompilationError(Locations.NO_LOCATION, validatorValue.replace("\n", " "))) { + @Override + public void openLocation() { + openResource(Display.getCurrent().getActiveShell(), parameter); + } + }; + } + } catch (Throwable t) { + LOGGER.error("Failed to invoke type validator function " + func, t); + } finally { + sclContext.put("graph", oldGraph); } - }); + return null; + } } + + private static class ComponentSyncListenerAdapter extends SyncListenerAdapter> implements Disposable { - @Override - public List getIssues() { - synchronized (currentIssues) { - List results = new ArrayList<>(currentIssues); - return results; + private ConcurrentHashMap currentlyListening = new ConcurrentHashMap<>(); + private boolean disposed; + private Runnable callback; + + public ComponentSyncListenerAdapter(Runnable callback) { + this.callback = callback; + } + + @Override + public void execute(ReadGraph graph, Set newComponents) { + if (currentlyListening.isEmpty() && newComponents.isEmpty()) { + // we can stop here as nothing will change + return; + } + + Set removedComponents = new HashSet<>(currentlyListening.keySet()); + removedComponents.removeAll(newComponents); + + Set addedComponents = new HashSet<>(newComponents); + addedComponents.removeAll(currentlyListening.keySet()); + + for (Resource removedComponent : removedComponents) { + // stop listening + DisposableSyncListener listener = currentlyListening.remove(removedComponent); + listener.dispose(); + } + + for (Resource addedComponent : addedComponents) { + SCLValueDisposableSyncListener listener = new SCLValueDisposableSyncListener(callback); + currentlyListening.put(addedComponent, listener); + graph.asyncRequest(new SCLValueRequest(addedComponent), listener); + } + } + + public List getIssues() { + List issues = new ArrayList<>(); + for (SCLValueDisposableSyncListener listener : currentlyListening.values()) { + List listenerIssues = listener.getIssues(); + if (listenerIssues != null && !listenerIssues.isEmpty()) + issues.addAll(listenerIssues); + } + return issues; + } + + @Override + public void dispose() { + currentlyListening.values().forEach(l -> l.dispose()); + currentlyListening.clear(); + this.disposed = true; + } + + @Override + public boolean isDisposed() { + return disposed; } } + + private static class SCLValueDisposableSyncListener extends DisposableSyncListener> { - @Override - public void dispose() { - disposed = true; + private Runnable callback; + private ConcurrentHashMap currentlyListeningSCLValues = new ConcurrentHashMap<>(); + + public SCLValueDisposableSyncListener(Runnable callback) { + this.callback = callback; + } + + @Override + public void execute(ReadGraph graph, Set newComponents) throws DatabaseException { + if (currentlyListeningSCLValues.isEmpty() && newComponents.isEmpty()) { + // we can stop here as nothing will change + return; + } + + + Set removedComponents = new HashSet<>(currentlyListeningSCLValues.keySet()); + removedComponents.removeAll(newComponents); + + Set addedComponents = new HashSet<>(newComponents); + addedComponents.removeAll(currentlyListeningSCLValues.keySet()); + + for (ResourceHolder removedComponent : removedComponents) { + // stop listening + DisposableListener listener = currentlyListeningSCLValues.remove(removedComponent); + listener.dispose(); + } + + for (ResourceHolder sclValue : addedComponents) { + SCLIssuesTableEntryDisposableListener listener = new SCLIssuesTableEntryDisposableListener(callback); + currentlyListeningSCLValues.put(sclValue, listener); + graph.syncRequest(new SCLExpressionValidationRequest(sclValue.component, sclValue.predicate, sclValue.object), listener); + } + if (callback != null) { + callback.run(); + } + } + + public List getIssues() { + if (currentlyListeningSCLValues.isEmpty()) + return null; + List issues = new ArrayList<>(); + for (SCLIssuesTableEntryDisposableListener listener : currentlyListeningSCLValues.values()) { + if (listener.getResult() != null) + issues.add(listener.getResult()); + } + return issues; + } + + @Override + public void exception(ReadGraph graph, Throwable throwable) throws DatabaseException { + LOGGER.error("Could not listen", throwable); + } + + @Override + public void dispose() { + currentlyListeningSCLValues.values().forEach(l -> l.dispose()); + currentlyListeningSCLValues.clear(); + super.dispose(); + } + } + + private static class SCLIssuesTableEntryDisposableListener extends DisposableListener { - private static void openResource(Shell shell, Resource resource) { - DefaultActions.performDefaultAction(shell, new StructuredSelection(resource)); + private SCLIssuesTableEntry result; + private Runnable callback; + + public SCLIssuesTableEntryDisposableListener(Runnable callback) { + this.callback = callback; + } + + @Override + public void execute(SCLIssuesTableEntry result) { + if (!Objects.equals(this.result, result)) { + this.result = result; + if (callback != null) { + callback.run(); + } + } + } + + @Override + public void exception(Throwable t) { + LOGGER.error("", t); + } + + public SCLIssuesTableEntry getResult() { + return result; + } } + + private static class ResourceHolder { + + Resource component; + Resource predicate; + Resource object; + + public ResourceHolder(Resource component, Resource predicate, Resource object) { + this.component = Objects.requireNonNull(component); + this.predicate = Objects.requireNonNull(predicate); + this.object = Objects.requireNonNull(object); + } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + component.hashCode(); + result = prime * result + object.hashCode(); + result = prime * result + predicate.hashCode(); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + ResourceHolder other = (ResourceHolder) obj; + if (!component.equals(other.component)) + return false; + if (!object.equals(other.object)) + return false; + if (!predicate.equals(other.predicate)) + return false; + return true; + } + + } }