+package org.simantics.modeling.scl.issue;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+
+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.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.common.procedure.adapter.SyncListenerAdapter;
+import org.simantics.db.common.request.UniqueRead;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.layer0.util.Layer0Utils;
+import org.simantics.db.layer0.variable.Variable;
+import org.simantics.db.layer0.variable.Variables;
+import org.simantics.layer0.Layer0;
+import org.simantics.modeling.ModelingUtils;
+import org.simantics.scl.compiler.errors.CompilationError;
+import org.simantics.scl.compiler.errors.Locations;
+import org.simantics.scl.osgi.issues.SCLIssueProviderFactory;
+import org.simantics.scl.osgi.issues.SCLIssueProviderFactory.SCLIssueProvider;
+import org.simantics.scl.osgi.issues.SCLIssuesTableEntry;
+import org.simantics.scl.runtime.SCLContext;
+import org.simantics.scl.runtime.function.Function1;
+import org.simantics.structural.stubs.StructuralResource2;
+import org.simantics.ui.workbench.action.DefaultActions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SCLExpressionIssueProvider implements SCLIssueProvider {
+
+ public static class SCLExpressionIssueProviderFactory implements SCLIssueProviderFactory {
+
+ @Override
+ public SCLIssueProvider getSCLIssueProvider() {
+ return new SCLExpressionIssueProvider();
+ }
+
+ }
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(SCLExpressionIssueProvider.class);
+ private List<SCLIssuesTableEntry> currentIssues = new ArrayList<>();
+ private boolean disposed = false;
+
+ SCLExpressionIssueProvider() {
+ }
+
+ @Override
+ public void listenIssues(Runnable callback) {
+ Simantics.getSession().asyncRequest(new UniqueRead<List<SCLIssuesTableEntry>>() {
+
+ @Override
+ public List<SCLIssuesTableEntry> perform(ReadGraph graph) throws DatabaseException {
+ Layer0 L0 = Layer0.getInstance(graph);
+ Set<Resource> indexRoots = new TreeSet<Resource>();
+ 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);
+ }
+ }
+
+ StructuralResource2 STR = StructuralResource2.getInstance(graph);
+
+ List<SCLIssuesTableEntry> results = new ArrayList<>();
+
+ for (Resource ontology : indexRoots) {
+ List<Resource> 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<Variable, String> 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);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return results;
+ }
+ }, new SyncListenerAdapter<List<SCLIssuesTableEntry>>() {
+
+ @Override
+ public void execute(ReadGraph graph, List<SCLIssuesTableEntry> result) {
+ synchronized (currentIssues) {
+ currentIssues.clear();
+ currentIssues.addAll(result);
+ }
+ if (callback != null)
+ callback.run();
+ }
+
+ @Override
+ public void exception(ReadGraph graph, Throwable t) {
+ LOGGER.error("Could not get SCL issues", t);
+ }
+
+ @Override
+ public boolean isDisposed() {
+ return disposed;
+ }
+ });
+ }
+
+ @Override
+ public List<SCLIssuesTableEntry> getIssues() {
+ synchronized (currentIssues) {
+ List<SCLIssuesTableEntry> results = new ArrayList<>(currentIssues);
+ return results;
+ }
+ }
+
+ @Override
+ public void dispose() {
+ disposed = true;
+ }
+
+ private static void openResource(Shell shell, Resource resource) {
+ DefaultActions.performDefaultAction(shell, new StructuredSelection(resource));
+ }
+
+}