From: jsimomaa Date: Thu, 26 Oct 2017 08:51:22 +0000 (+0300) Subject: SCL expressions to SCL Issues view X-Git-Tag: v1.31.0~84 X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=commitdiff_plain;h=refs%2Fchanges%2F43%2F1143%2F11;p=simantics%2Fplatform.git SCL expressions to SCL Issues view refs #7575 Change-Id: Ic962c2e77a02dec079e6e0b3d5225a62ad734cee --- diff --git a/bundles/org.simantics.document.server/.project b/bundles/org.simantics.document.server/.project index ea6fdd9e8..5372c4d3f 100644 --- a/bundles/org.simantics.document.server/.project +++ b/bundles/org.simantics.document.server/.project @@ -20,6 +20,11 @@ + + org.eclipse.pde.ds.core.builder + + + org.eclipse.pde.PluginNature diff --git a/bundles/org.simantics.document.server/build.properties b/bundles/org.simantics.document.server/build.properties index 132fd4143..1635b2b1f 100644 --- a/bundles/org.simantics.document.server/build.properties +++ b/bundles/org.simantics.document.server/build.properties @@ -1,4 +1,3 @@ -source.. = src/ output.. = bin/ bin.includes = META-INF/,\ .,\ @@ -6,4 +5,4 @@ bin.includes = META-INF/,\ webdefault.xml,\ scl/ src.includes = scl/ - +source.. = src/ diff --git a/bundles/org.simantics.layer0/graph/Layer0Values.pgraph b/bundles/org.simantics.layer0/graph/Layer0Values.pgraph index 18fd6d25c..c36cf60e5 100644 --- a/bundles/org.simantics.layer0/graph/Layer0Values.pgraph +++ b/bundles/org.simantics.layer0/graph/Layer0Values.pgraph @@ -16,7 +16,7 @@ L0.RequiresValueType L0.String -L0.HasDefaultLiteralType L0.Type @@ -24,12 +24,15 @@ L0.HasSubliteralPredicate -- L0.SCLValue.expression --> L0.String -- L0.SCLValue.environment --> L0.SCLValue.Environment -- L0.SCLValueType.validator ==> "Variable -> String" + + + + + + diff --git a/bundles/org.simantics.modeling/build.properties b/bundles/org.simantics.modeling/build.properties index e69c44b34..0937bf5e1 100644 --- a/bundles/org.simantics.modeling/build.properties +++ b/bundles/org.simantics.modeling/build.properties @@ -15,5 +15,6 @@ bin.includes = META-INF/,\ .,\ adapters.xml,\ plugin.xml,\ - scl/ + scl/,\ + OSGI-INF/ src.includes = scl/ 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 new file mode 100644 index 000000000..bbbd6b016 --- /dev/null +++ b/bundles/org.simantics.modeling/src/org/simantics/modeling/scl/issue/SCLExpressionIssueProvider.java @@ -0,0 +1,165 @@ +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 currentIssues = new ArrayList<>(); + private boolean disposed = false; + + 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); + } + } + + 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); + } + } + } + } + } + } + } + return results; + } + }, new SyncListenerAdapter>() { + + @Override + public void execute(ReadGraph graph, List 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 getIssues() { + synchronized (currentIssues) { + List 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)); + } + +} diff --git a/bundles/org.simantics.scl.osgi/META-INF/MANIFEST.MF b/bundles/org.simantics.scl.osgi/META-INF/MANIFEST.MF index a1033a4a4..971c55338 100644 --- a/bundles/org.simantics.scl.osgi/META-INF/MANIFEST.MF +++ b/bundles/org.simantics.scl.osgi/META-INF/MANIFEST.MF @@ -6,11 +6,13 @@ Bundle-Version: 1.0.4.qualifier Bundle-Activator: org.simantics.scl.osgi.internal.Activator Require-Bundle: org.eclipse.core.runtime, gnu.trove3;bundle-version="3.0.0", - org.simantics.scl.compiler;bundle-version="0.6.0";visibility:=reexport + org.simantics.scl.compiler;bundle-version="0.6.0";visibility:=reexport, + org.slf4j.api Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Export-Package: org.simantics.scl.osgi, - org.simantics.scl.osgi.internal + org.simantics.scl.osgi.internal, + org.simantics.scl.osgi.issues Service-Component: OSGI-INF/org.simantics.scl.osgi.internal.BundleModuleSourceRepository.xml, OSGI-INF/org.simantics.scl.osgi.internal.FileSystemModuleSourceRepository.xml, OSGI-INF/org.simantics.scl.osgi.internal.BundleTestScriptRepository.xml diff --git a/bundles/org.simantics.scl.osgi/src/org/simantics/scl/osgi/internal/Activator.java b/bundles/org.simantics.scl.osgi/src/org/simantics/scl/osgi/internal/Activator.java index d7a9e9e7f..b629e08d9 100644 --- a/bundles/org.simantics.scl.osgi/src/org/simantics/scl/osgi/internal/Activator.java +++ b/bundles/org.simantics.scl.osgi/src/org/simantics/scl/osgi/internal/Activator.java @@ -13,6 +13,8 @@ import org.simantics.scl.osgi.SCLOsgi; public class Activator implements BundleActivator { + public static final String PLUGIN_ID = "org.simantics.scl.osgi"; + private static BundleContext context; public static BundleContext getContext() { diff --git a/bundles/org.simantics.scl.osgi/src/org/simantics/scl/osgi/issues/SCLIssueFactoryProvider.java b/bundles/org.simantics.scl.osgi/src/org/simantics/scl/osgi/issues/SCLIssueFactoryProvider.java new file mode 100644 index 000000000..c06dbe1cf --- /dev/null +++ b/bundles/org.simantics.scl.osgi/src/org/simantics/scl/osgi/issues/SCLIssueFactoryProvider.java @@ -0,0 +1,35 @@ +package org.simantics.scl.osgi.issues; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceReference; +import org.simantics.scl.osgi.internal.Activator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public final class SCLIssueFactoryProvider { + + private static final Logger LOGGER = LoggerFactory.getLogger(SCLIssueFactoryProvider.class); + + public static List getSCLIssueProviderFactories() { + ServiceReference[] serviceReferences = new ServiceReference[0]; + try { + serviceReferences = Activator.getContext().getAllServiceReferences(SCLIssueProviderFactory.class.getName(), null); + } catch (InvalidSyntaxException e) { + LOGGER.error("Could not get service references for " + SCLIssueProviderFactory.class.getName(), e); + } + if (serviceReferences == null || serviceReferences.length == 0) + return Collections.emptyList(); + + List services = new ArrayList<>(serviceReferences.length); + for (ServiceReference reference : serviceReferences) { + SCLIssueProviderFactory service = (SCLIssueProviderFactory) Activator.getContext().getService(reference); + services.add(service); + } + return services; + } + +} diff --git a/bundles/org.simantics.scl.osgi/src/org/simantics/scl/osgi/issues/SCLIssueProviderFactory.java b/bundles/org.simantics.scl.osgi/src/org/simantics/scl/osgi/issues/SCLIssueProviderFactory.java new file mode 100644 index 000000000..293e8a126 --- /dev/null +++ b/bundles/org.simantics.scl.osgi/src/org/simantics/scl/osgi/issues/SCLIssueProviderFactory.java @@ -0,0 +1,18 @@ +package org.simantics.scl.osgi.issues; + +import java.util.List; + +public interface SCLIssueProviderFactory { + + SCLIssueProvider getSCLIssueProvider(); + + public static interface SCLIssueProvider { + + void listenIssues(Runnable callback); + + List getIssues(); + + void dispose(); + } + +} diff --git a/bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/issues/SCLIssuesTableEntry.java b/bundles/org.simantics.scl.osgi/src/org/simantics/scl/osgi/issues/SCLIssuesTableEntry.java similarity index 83% rename from bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/issues/SCLIssuesTableEntry.java rename to bundles/org.simantics.scl.osgi/src/org/simantics/scl/osgi/issues/SCLIssuesTableEntry.java index 2b49dd125..c9c21e8b3 100644 --- a/bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/issues/SCLIssuesTableEntry.java +++ b/bundles/org.simantics.scl.osgi/src/org/simantics/scl/osgi/issues/SCLIssuesTableEntry.java @@ -1,9 +1,9 @@ -package org.simantics.scl.ui.issues; +package org.simantics.scl.osgi.issues; import org.simantics.scl.compiler.errors.CompilationError; import org.simantics.scl.compiler.errors.Locations; -public class SCLIssuesTableEntry implements Comparable { +public abstract class SCLIssuesTableEntry implements Comparable { public final String moduleName; public final CompilationError error; @@ -12,6 +12,8 @@ public class SCLIssuesTableEntry implements Comparable { this.error = error; } + public abstract void openLocation(); + @Override public int compareTo(SCLIssuesTableEntry o) { if(this == o) diff --git a/bundles/org.simantics.scl.ui/META-INF/MANIFEST.MF b/bundles/org.simantics.scl.ui/META-INF/MANIFEST.MF index 061dad852..60603619a 100644 --- a/bundles/org.simantics.scl.ui/META-INF/MANIFEST.MF +++ b/bundles/org.simantics.scl.ui/META-INF/MANIFEST.MF @@ -14,10 +14,12 @@ Require-Bundle: org.eclipse.ui.editors;bundle-version="3.6.0", org.simantics.scl.osgi;bundle-version="1.0.0", org.simantics.scl.compiler;bundle-version="0.6.0", org.junit;bundle-version="4.12.0";resolution:=optional, - com.ibm.icu + com.ibm.icu, + org.slf4j.api Export-Package: org.simantics.scl.ui.console, org.simantics.scl.ui.editor, org.simantics.scl.ui.editor2, org.simantics.scl.ui.imports Bundle-Activator: org.simantics.scl.ui.Activator Bundle-ActivationPolicy: lazy +Service-Component: OSGI-INF/sclModuleIssueProvider.xml diff --git a/bundles/org.simantics.scl.ui/OSGI-INF/sclModuleIssueProvider.xml b/bundles/org.simantics.scl.ui/OSGI-INF/sclModuleIssueProvider.xml new file mode 100644 index 000000000..81cd28cf8 --- /dev/null +++ b/bundles/org.simantics.scl.ui/OSGI-INF/sclModuleIssueProvider.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/bundles/org.simantics.scl.ui/build.properties b/bundles/org.simantics.scl.ui/build.properties index 6c480f39f..4c8a5cea8 100644 --- a/bundles/org.simantics.scl.ui/build.properties +++ b/bundles/org.simantics.scl.ui/build.properties @@ -1,6 +1,7 @@ -source.. = src/ output.. = bin/ bin.includes = META-INF/,\ .,\ plugin.xml,\ - icons/ + icons/,\ + OSGI-INF/sclModuleIssueProvider.xml +source.. = src/ diff --git a/bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/Activator.java b/bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/Activator.java index 768682fa3..f68f2876e 100644 --- a/bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/Activator.java +++ b/bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/Activator.java @@ -12,6 +12,8 @@ import org.simantics.scl.ui.imports.internal.ManualImportModulesAction; public class Activator extends AbstractUIPlugin { + public static final String PLUGIN_ID = "org.simantics.scl.ui"; + private static Activator INSTANCE; @Override diff --git a/bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/issues/SCLIssuesContentProvider.java b/bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/issues/SCLIssuesContentProvider.java index 4772ad5ba..4e386e685 100644 --- a/bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/issues/SCLIssuesContentProvider.java +++ b/bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/issues/SCLIssuesContentProvider.java @@ -1,90 +1,40 @@ package org.simantics.scl.ui.issues; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; +import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Collectors; import org.eclipse.jface.viewers.IStructuredContentProvider; import org.eclipse.jface.viewers.Viewer; import org.eclipse.swt.widgets.Control; -import org.simantics.scl.compiler.errors.CompilationError; -import org.simantics.scl.compiler.errors.DoesNotExist; -import org.simantics.scl.compiler.errors.Failable; -import org.simantics.scl.compiler.errors.Failure; -import org.simantics.scl.compiler.module.Module; -import org.simantics.scl.compiler.module.repository.ModuleRepository; -import org.simantics.scl.compiler.module.repository.UpdateListener; - -import gnu.trove.map.hash.THashMap; -import gnu.trove.procedure.TObjectObjectProcedure; -import gnu.trove.procedure.TObjectProcedure; +import org.simantics.scl.osgi.issues.SCLIssueFactoryProvider; +import org.simantics.scl.osgi.issues.SCLIssueProviderFactory.SCLIssueProvider; +import org.simantics.scl.osgi.issues.SCLIssuesTableEntry; public class SCLIssuesContentProvider implements IStructuredContentProvider { public static final int MAX_ISSUE_COUNT = 1000; - - Viewer viewer; - ModuleRepository repository; + + private Viewer viewer; boolean disposed = false; - AtomicBoolean refreshInProgress = new AtomicBoolean(false); - - THashMap currentFailures = new THashMap(); - THashMap updateListeners = new THashMap(); - + private AtomicBoolean refreshInProgress = new AtomicBoolean(false); + private List issueProviders = new ArrayList<>(); + @Override public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { this.viewer = viewer; - this.repository = (ModuleRepository)newInput; - if(newInput != null) - listenIssues(); - } - - private UpdateListener getUpdateListener(String moduleName) { - UpdateListener listener; - synchronized(updateListeners) { - listener = updateListeners.get(moduleName); - if(listener == null) { - listener = new UpdateListener() { - @Override - public void notifyAboutUpdate() { - if(!disposed) - listenModule(moduleName); - } - }; - updateListeners.put(moduleName, listener); - } + if (!issueProviders.isEmpty()) { + issueProviders.forEach(p -> p.dispose()); + issueProviders.clear(); } - return listener; - } - - private void listenModule(String moduleName) { - if(repository == null) - return; - Failable result = repository.getModule(moduleName, getUpdateListener(moduleName)); - synchronized(currentFailures) { - if(result instanceof Failure) { - Failure failure = (Failure)result; - currentFailures.put(moduleName, failure.errors); - } - else if(result == DoesNotExist.INSTANCE) { - if(currentFailures.remove(moduleName) == null) - return; - } - else { - CompilationError[] warnings = result.getResult().getWarnings(); - if(warnings.length == 0) { - if(currentFailures.remove(moduleName) == null) - return; - } - else { - currentFailures.put(moduleName, warnings); - } - } + if(newInput != null) { + issueProviders = SCLIssueFactoryProvider.getSCLIssueProviderFactories().stream().map(f -> f.getSCLIssueProvider()).collect(Collectors.toList()); + issueProviders.forEach(p -> p.listenIssues(() -> refresh())); } - refresh(); } - + private void refresh() { if(!refreshInProgress.compareAndSet(false, true)) return; @@ -104,54 +54,18 @@ public class SCLIssuesContentProvider implements IStructuredContentProvider { }); } - private void listenIssues() { - new Thread() { - public void run() { - if(repository == null) - return; - repository.getSourceRepository().forAllModules(new TObjectProcedure() { - @Override - public boolean execute(String moduleName) { - listenModule(moduleName); - return true; - } - }); - } - }.start(); - } - - @Override public void dispose() { if(this.disposed) return; + issueProviders.forEach(p -> p.dispose()); + issueProviders.clear(); this.disposed = true; - if(repository != null) - synchronized(updateListeners) { - updateListeners.forEachEntry(new TObjectObjectProcedure() { - @Override - public boolean execute(String moduleName, UpdateListener listener) { - listener.stopListening(); - return true; - } - }); - updateListeners.clear(); - } } @Override public Object[] getElements(Object inputElement) { - ArrayList result = new ArrayList(); - synchronized(currentFailures) { - String[] moduleNames = currentFailures.keySet().toArray(new String[currentFailures.size()]); - Arrays.sort(moduleNames); - for(String moduleName : moduleNames) { - CompilationError[] errors = currentFailures.get(moduleName); - for(CompilationError error : errors) - result.add(new SCLIssuesTableEntry(moduleName, error)); - if(result.size() >= MAX_ISSUE_COUNT) - break; - } - } + ArrayList result = new ArrayList<>(); + issueProviders.forEach(p -> result.addAll(p.getIssues())); Collections.sort(result); return result.toArray(); } diff --git a/bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/issues/SCLIssuesView.java b/bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/issues/SCLIssuesView.java index 3e0cc3120..10e2288e1 100644 --- a/bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/issues/SCLIssuesView.java +++ b/bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/issues/SCLIssuesView.java @@ -26,8 +26,8 @@ import org.eclipse.ui.PartInitException; import org.eclipse.ui.part.ViewPart; import org.simantics.scl.compiler.errors.ErrorSeverity; import org.simantics.scl.osgi.SCLOsgi; +import org.simantics.scl.osgi.issues.SCLIssuesTableEntry; import org.simantics.scl.ui.Activator; -import org.simantics.scl.ui.editor2.OpenSCLDefinition; public class SCLIssuesView extends ViewPart { @@ -55,7 +55,8 @@ public class SCLIssuesView extends ViewPart { IAction action = new Action("Refresh") { @Override public void run() { - SCLOsgi.MODULE_REPOSITORY.getSourceRepository().checkUpdates(); + tableViewer.setInput(SCLOsgi.MODULE_REPOSITORY); +// issuesContentProvider.refresh() } }; action.setImageDescriptor(imageRegistry.getDescriptor("arrow_refresh")); @@ -144,7 +145,7 @@ public class SCLIssuesView extends ViewPart { public void doubleClick(DoubleClickEvent event) { IStructuredSelection selection = (IStructuredSelection)event.getSelection(); SCLIssuesTableEntry entry = (SCLIssuesTableEntry)selection.getFirstElement(); - OpenSCLDefinition.openDefinition(entry.moduleName, entry.error.location); + entry.openLocation(); } }); } diff --git a/bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/issues/SCLModuleIssueProvider.java b/bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/issues/SCLModuleIssueProvider.java new file mode 100644 index 000000000..20a89d41b --- /dev/null +++ b/bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/issues/SCLModuleIssueProvider.java @@ -0,0 +1,147 @@ +package org.simantics.scl.ui.issues; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.simantics.scl.compiler.errors.CompilationError; +import org.simantics.scl.compiler.errors.DoesNotExist; +import org.simantics.scl.compiler.errors.Failable; +import org.simantics.scl.compiler.errors.Failure; +import org.simantics.scl.compiler.module.Module; +import org.simantics.scl.compiler.module.repository.ModuleRepository; +import org.simantics.scl.compiler.module.repository.UpdateListener; +import org.simantics.scl.osgi.SCLOsgi; +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.ui.editor2.OpenSCLDefinition; + +import gnu.trove.map.hash.THashMap; +import gnu.trove.procedure.TObjectObjectProcedure; +import gnu.trove.procedure.TObjectProcedure; + +public class SCLModuleIssueProvider implements SCLIssueProvider { + + public static class SCLModuleIssueProviderFactory implements SCLIssueProviderFactory { + + @Override + public SCLIssueProvider getSCLIssueProvider() { + return new SCLModuleIssueProvider(); + } + + } + + ModuleRepository repository = SCLOsgi.MODULE_REPOSITORY; + + THashMap currentFailures = new THashMap<>(); + THashMap updateListeners = new THashMap<>(); + + private boolean disposed; + + SCLModuleIssueProvider() { + } + + private UpdateListener getUpdateListener(String moduleName, Runnable callback) { + UpdateListener listener; + synchronized(updateListeners) { + listener = updateListeners.get(moduleName); + if(listener == null) { + listener = new UpdateListener() { + @Override + public void notifyAboutUpdate() { + if(!disposed) + listenModule(moduleName, callback); + } + }; + updateListeners.put(moduleName, listener); + } + } + return listener; + } + + private void listenModule(String moduleName, Runnable callback) { + if(repository == null) + return; + Failable result = repository.getModule(moduleName, getUpdateListener(moduleName, callback)); + synchronized(currentFailures) { + if(result instanceof Failure) { + Failure failure = (Failure)result; + currentFailures.put(moduleName, failure.errors); + } + else if(result == DoesNotExist.INSTANCE) { + if(currentFailures.remove(moduleName) == null) + return; + } + else { + CompilationError[] warnings = result.getResult().getWarnings(); + if(warnings.length == 0) { + if(currentFailures.remove(moduleName) == null) + return; + } + else { + currentFailures.put(moduleName, warnings); + } + } + } + if (callback != null) + callback.run(); + } + + public void listenIssues(Runnable callback) { + new Thread() { + public void run() { + if(repository == null) + return; + repository.getSourceRepository().forAllModules(new TObjectProcedure() { + @Override + public boolean execute(String moduleName) { + listenModule(moduleName, callback); + return true; + } + }); + } + }.start(); + } + + @Override + public List getIssues() { + ArrayList result = new ArrayList<>(); + synchronized(currentFailures) { + String[] moduleNames = currentFailures.keySet().toArray(new String[currentFailures.size()]); + Arrays.sort(moduleNames); + for(String moduleName : moduleNames) { + CompilationError[] errors = currentFailures.get(moduleName); + for(CompilationError error : errors) { + result.add(new SCLIssuesTableEntry(moduleName, error) { + @Override + public void openLocation() { + OpenSCLDefinition.openDefinition(moduleName, error.location); + } + }); + } + } + } + return result; + } + + @Override + public void dispose() { + if (disposed) + return; + disposed = true; + if(repository != null) { + synchronized(updateListeners) { + updateListeners.forEachEntry(new TObjectObjectProcedure() { + @Override + public boolean execute(String moduleName, UpdateListener listener) { + listener.stopListening(); + return true; + } + }); + updateListeners.clear(); + } + } + } + +}