X-Git-Url: https://gerrit.simantics.org/r/gitweb?p=simantics%2Fplatform.git;a=blobdiff_plain;f=bundles%2Forg.simantics.issues.common%2Fsrc%2Forg%2Fsimantics%2Fissues%2Fcommon%2FIssueUtils.java;h=ecc6b20cc9c517b6e04a8e0691821466b4961478;hp=f1dcadc892b95cab535904f865a5c117f2e8cfdc;hb=0d9b90834ce56b292c00b1a39850ed842c3e4d42;hpb=28418dc0f3cc153ba631c201c900b99e45fa03d1 diff --git a/bundles/org.simantics.issues.common/src/org/simantics/issues/common/IssueUtils.java b/bundles/org.simantics.issues.common/src/org/simantics/issues/common/IssueUtils.java index f1dcadc89..ecc6b20cc 100644 --- a/bundles/org.simantics.issues.common/src/org/simantics/issues/common/IssueUtils.java +++ b/bundles/org.simantics.issues.common/src/org/simantics/issues/common/IssueUtils.java @@ -1,529 +1,529 @@ -/******************************************************************************* - * Copyright (c) 2007, 2011 Association for Decentralized Information Management - * in Industry THTH ry. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * VTT Technical Research Centre of Finland - initial API and implementation - *******************************************************************************/ -package org.simantics.issues.common; - -import gnu.trove.set.hash.THashSet; - -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.atomic.AtomicBoolean; - -import org.simantics.Simantics; -import org.simantics.databoard.Bindings; -import org.simantics.databoard.util.URIStringUtils; -import org.simantics.db.Disposable; -import org.simantics.db.Issue; -import org.simantics.db.ReadGraph; -import org.simantics.db.RequestProcessor; -import org.simantics.db.Resource; -import org.simantics.db.Session; -import org.simantics.db.VirtualGraph; -import org.simantics.db.WriteGraph; -import org.simantics.db.common.primitiverequest.Objects; -import org.simantics.db.common.procedure.adapter.DisposableSyncListener; -import org.simantics.db.common.procedure.adapter.TransientCacheListener; -import org.simantics.db.common.procedure.single.SingleSetSyncListener; -import org.simantics.db.common.request.ResourceRead3; -import org.simantics.db.common.request.WriteRequest; -import org.simantics.db.common.utils.ListUtils; -import org.simantics.db.exception.DatabaseException; -import org.simantics.db.layer0.request.ActiveModels; -import org.simantics.db.layer0.request.Model; -import org.simantics.db.layer0.request.PossibleModel; -import org.simantics.db.layer0.util.RemoverUtil; -import org.simantics.db.layer0.variable.Variable; -import org.simantics.db.service.VirtualGraphSupport; -import org.simantics.issues.Severity; -import org.simantics.issues.ontology.IssueResource; -import org.simantics.layer0.Layer0; -import org.simantics.operation.Layer0X; -import org.simantics.scl.runtime.function.FunctionImpl2; -import org.simantics.utils.datastructures.Pair; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * @author Tuukka Lehtonen - */ -public class IssueUtils { - private static final Logger LOGGER = LoggerFactory.getLogger(IssueUtils.class); - - public static Resource toSeverityResource(IssueResource ISSUE, Severity severity) { - switch (severity) { - case ERROR: return ISSUE.Severity_Error; - case FATAL: return ISSUE.Severity_Fatal; - case INFO: return ISSUE.Severity_Info; - case WARNING: return ISSUE.Severity_Warning; - case NOTE: return ISSUE.Severity_Note; - default: return null; - } - } - - public static Severity toSeverity(IssueResource ISSUE, Resource severity) { - if (severity == null) - return null; - if (severity.equals(ISSUE.Severity_Fatal)) - return Severity.FATAL; - if (severity.equals(ISSUE.Severity_Error)) - return Severity.ERROR; - if (severity.equals(ISSUE.Severity_Info)) - return Severity.INFO; - if (severity.equals(ISSUE.Severity_Warning)) - return Severity.WARNING; - if (severity.equals(ISSUE.Severity_Note)) - return Severity.NOTE; - return null; - } - - private static class IssueSourceDirtyListener extends FunctionImpl2, Boolean> { - - private final IssueSource is; - - public IssueSourceDirtyListener(IssueSource is) { - this.is = is; - } - - @Override - public Boolean apply(ReadGraph graph, final List resources) { - VirtualGraphSupport support = graph.getService(VirtualGraphSupport.class); - VirtualGraph vg = support.getWorkspacePersistent(IssueConstants.ISSUE_VG); - if (graph instanceof WriteGraph) { - try { - if(is.needUpdate(graph, resources)) { - graph.sync(new WriteRequest(vg) { - @Override - public void perform(WriteGraph graph) throws DatabaseException { - is.update(graph, resources); - } - }); - } - } catch (DatabaseException e) { - LOGGER.error("Updating issue source failed.", e); - } - } else { - Session session = Simantics.getSession(); - session.asyncRequest(new WriteRequest(vg) { - @Override - public void perform(WriteGraph graph) throws DatabaseException { - is.update(graph, resources); - } - }); - } - - return true; - } - - } - - private static class IssueSourceManagedIssuesListener extends SingleSetSyncListener { - - private final HashMap listeners = new HashMap<>(); - private final AtomicBoolean disposed; - private final Resource source; - private final Resource model; - - public IssueSourceManagedIssuesListener(AtomicBoolean disposed, Resource source, Resource model) { - this.disposed = disposed; - this.source = source; - this.model = model; - } - - class IssueValidityListener extends DisposableSyncListener { - - final private Resource issue; - - public IssueValidityListener(Resource issue) { - this.issue = issue; - } - - @Override - public void execute(ReadGraph graph, Boolean valid) throws DatabaseException { - if(!valid) { - VirtualGraphSupport support = graph.getService(VirtualGraphSupport.class); - VirtualGraph vg = support.getWorkspacePersistent(IssueConstants.ISSUE_VG); - graph.asyncRequest(new WriteRequest(vg) { - - @Override - public void perform(WriteGraph graph) throws DatabaseException { - Issue desc = graph.sync(new StandardIssueDescription(issue)); - if(desc == null) - return; - Resource context = (Resource)desc.getMainContext(); - new DependencyIssueSynchronizer2(context, source).perform(graph); - } - - }); - } - } - - @Override - public void exception(ReadGraph graph, Throwable throwable) throws DatabaseException { - LOGGER.error("IssueValidityListener received an exception.", throwable); - } - - } - - @Override - public void add(ReadGraph graph, final Resource issue) throws DatabaseException { - IssueValidityListener listener = new IssueValidityListener(issue); - - graph.asyncRequest(new ResourceRead3(issue, model, source) { - - @Override - public Boolean perform(ReadGraph graph) throws DatabaseException { - Issue desc = graph.sync(new StandardIssueDescription(resource)); - if(desc == null) - return false; - Resource context = (Resource)desc.getMainContext(); - return graph.syncRequest(new DependencyIssueValidator2(context, resource2, resource3), TransientCacheListener.instance()); - } - - }, listener); - - listeners.put(issue, listener); - } - - @Override - public void remove(ReadGraph graph, final Resource issue) throws DatabaseException { - IssueValidityListener listener = listeners.remove(issue); - if(listener != null) - listener.dispose(); - } - - @Override - public void exception(ReadGraph graph, Throwable t) { - LOGGER.error("IssueSourceManagedIssuesListener received an exception.", t); - } - - @Override - public boolean isDisposed() { - boolean disp = disposed.get(); - if (disp) { - // Ensure validity listeners are cleared eventually. - if (!listeners.isEmpty()) { - for (IssueValidityListener listener : listeners.values()) { - listener.dispose(); - } - listeners.clear(); - } - } - return disp; - } - - } - - private static class ActiveIssueSourceListener extends SingleSetSyncListener { - - private final AtomicBoolean disposed; - private Map> sources = new HashMap<>(); - - public ActiveIssueSourceListener(AtomicBoolean disposed) { - this.disposed = disposed; - } - - @Override - public void add(ReadGraph graph, final Resource source) throws DatabaseException { - IssueResource ISSUE = IssueResource.getInstance(graph); - boolean isListeningTracker = graph.isInstanceOf(source, ISSUE.Sources_ListeningDependencyTracker); - IssueSource is = graph.adapt(source, IssueSource.class); - final Resource model = isListeningTracker ? graph.syncRequest(new Model(source)) : null; - - IssueSourceDirtyListener listener = new IssueSourceDirtyListener(is); - is.addDirtyListener(listener); - sources.put(source, Pair.make(is, listener)); - - if (isListeningTracker) { - graph.asyncRequest( - new Objects(source, ISSUE.IssueSource_Manages), - new IssueSourceManagedIssuesListener(disposed, source, model)); - } - } - - @Override - public void remove(ReadGraph graph, final Resource source) throws DatabaseException { - Pair is = sources.remove(source); - if (is != null) - is.first.removeDirtyListener(is.second); - } - - @Override - public void exception(ReadGraph graph, Throwable t) { - LOGGER.error("ActiveIssueSourceListener received an exception.", t); - } - - @Override - public boolean isDisposed() { - return disposed.get(); - } - - } - - public static Disposable listenActiveProjectIssueSources(RequestProcessor processor, Resource project) throws DatabaseException { - final AtomicBoolean disposed = new AtomicBoolean(false); - processor.syncRequest( - new ActiveProjectIssueSources(project), - new ActiveIssueSourceListener(disposed)); - return new Disposable() { - @Override - public void dispose() { - disposed.set(true); - } - }; - } - - public static List getContextsForProperty(ReadGraph graph, Variable property) throws DatabaseException { - IssueResource ISSUE = IssueResource.getInstance(graph); - Variable issueVariable = property.getParent(graph); - Resource issueResource = issueVariable.getRepresents(graph); - Resource list = graph.getPossibleObject(issueResource, ISSUE.Issue_HasContexts); - if(list != null) - return ListUtils.toList(graph, list); - else - return Collections.emptyList(); - } - - public static void writeAdditionalContext(WriteGraph graph, Resource issue, List contexts) throws DatabaseException { - - if(contexts.isEmpty()) return; - - IssueResource IR = IssueResource.getInstance(graph); - - // The main context - graph.claim(issue, IR.Issue_HasContext, contexts.get(0)); - // A possible parent - Layer0 L0 = Layer0.getInstance(graph); - Resource parent = graph.getPossibleObject(contexts.get(0), L0.PartOf); - if(parent != null) { - graph.claim(issue, IR.Issue_HasContext, parent); - } - - } - public static void newUserIssue(WriteGraph graph, String label, Resource severity, List contexts) throws DatabaseException { - - Resource model = graph.sync(new PossibleModel(contexts.get(0))); - if(model == null) throw new DatabaseException("No model for main context"); - - newUserIssueForModel(graph, model, label, severity, contexts); - - } - - public static Resource newUserIssueForModel(WriteGraph graph) throws DatabaseException { - - Resource project = Simantics.getProjectResource(); - Collection activeModels = graph.syncRequest(new ActiveModels(project)); - if (activeModels.size() != 1) - return null; - - IssueResource ISSUE = IssueResource.getInstance(graph); - Resource issue = null; - for (Resource model : activeModels) { - issue = newUserIssueForModel(graph, model, "New User Issue", ISSUE.Severity_Note, Collections.emptyList()); - } - return issue; - - } - - - public static Resource newUserIssueForModel(WriteGraph graph, Resource model, String label, Resource severity, List contexts) throws DatabaseException { - IssueResource ISSUE = IssueResource.getInstance(graph); - return newUserIssueForModel(graph, model, ISSUE.Issue, label, severity, contexts); - } - - public static Resource newUserIssueForModel(WriteGraph graph, Resource model, Resource type, String label, Resource severity, List contexts) throws DatabaseException { - - Layer0 L0 = Layer0.getInstance(graph); - IssueResource ISSUE = IssueResource.getInstance(graph); - - Resource issue = graph.newResource(); - graph.claim(issue, ISSUE.UserIssue, ISSUE.UserIssue, issue); - graph.claim(issue, L0.InstanceOf, null, type); - graph.claim(issue, ISSUE.Issue_HasSeverity, null, severity); - graph.claim(issue, ISSUE.Issue_HasContexts, ListUtils.create(graph, L0.List, contexts)); - writeAdditionalContext(graph, issue, contexts); - graph.claimLiteral(issue, L0.HasName, UUID.randomUUID().toString(), Bindings.STRING); - graph.claimLiteral(issue, L0.HasLabel, label, Bindings.STRING); - DateFormat format = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss"); - Object time = format.format(new Date()); - graph.claimLiteral(issue, ISSUE.Issue_creationTime, time, Bindings.STRING); - graph.claim(model, L0.ConsistsOf, L0.PartOf, issue); - return issue; - - } - - /** - * Creates a new issue based on SimpleIssue structure. - */ - public static void newSimpleIssueForModel(WriteGraph graph, Resource model, Resource issueType, List contexts, SimpleIssue simpleIssue) throws DatabaseException { - - Layer0 L0 = Layer0.getInstance(graph); - IssueResource ISSUE = IssueResource.getInstance(graph); - - Resource issue = graph.newResource(); - graph.claim(issue, L0.InstanceOf, null, issueType); - graph.claim(issue, ISSUE.Issue_HasSeverity, null, toSeverityResource(ISSUE, simpleIssue.severity)); - graph.claim(issue, ISSUE.Issue_HasContexts, ListUtils.create(graph, L0.List, contexts)); - writeAdditionalContext(graph, issue, contexts); - graph.claimLiteral(issue, L0.HasName, UUID.randomUUID().toString(), Bindings.STRING); - graph.claimLiteral(issue, L0.HasLabel, simpleIssue.label, Bindings.STRING); - DateFormat format = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss"); - Object time = format.format(new Date()); - graph.claimLiteral(issue, ISSUE.Issue_creationTime, time, Bindings.STRING); - graph.claim(model, L0.ConsistsOf, L0.PartOf, issue); - - } - - /** - * Returns the set of all issues with given type. - */ - public static Set getSimpleIssues(ReadGraph graph, List contexts, Resource issueType) - throws DatabaseException { - Layer0 L0 = Layer0.getInstance(graph); - IssueResource ISSUE = IssueResource.getInstance(graph); - - THashSet currentIssues = null; - for(Resource issue : graph.getObjects(contexts.get(0), ISSUE.Issue_HasContext_Inverse)) { - if(!graph.isInstanceOf(issue, issueType)) - continue; - List curContexts = - ListUtils.toList(graph, graph.getSingleObject(issue, ISSUE.Issue_HasContexts)); - if(!contexts.equals(curContexts)) - continue; - if(currentIssues == null) - currentIssues = new THashSet(); - currentIssues.add(new SimpleIssue( - (String)graph.getRelatedValue(issue, L0.HasLabel), - toSeverity(ISSUE, graph.getSingleObject(issue, ISSUE.Issue_HasSeverity)), - issue)); - } - if(currentIssues == null) - return Collections.emptySet(); - else - return currentIssues; - } - - /** - * Creates and removes issues so that after the operation, - * the context has exactly the given issues with the given type. - */ - public static void setSimpleIssues(WriteGraph graph, Resource model, List contexts, Resource issueType, SimpleIssue ... issues) throws DatabaseException { - Set currentIssues = getSimpleIssues(graph, contexts, issueType); - for(SimpleIssue newIssue : issues) { - if(currentIssues.contains(newIssue)) - currentIssues.remove(newIssue); - else - newSimpleIssueForModel(graph, model, issueType, contexts, newIssue); - } - for(SimpleIssue oldIssue : currentIssues) - RemoverUtil.remove(graph, oldIssue.issueResource); - } - - /** - * Creates and removes issues so that after the operation, - * the context has exactly the given issues with the given type. - */ - public static void setSimpleIssues(WriteGraph graph, List contexts, Resource issueType, SimpleIssue ... issues) throws DatabaseException { - Resource model = graph.sync(new PossibleModel(contexts.get(0))); - if(model == null) throw new DatabaseException("No model for main context"); - - setSimpleIssues(graph, model, contexts, issueType, issues); - } - - /** - * Creates and removes issues so that after the operation, - * the context has exactly the given issues with the given type. - * Because this method is called in read transaction, it - * makes a write transaction if necessary and continues the - * operation there. - */ - public static void setSimpleIssuesAsync(ReadGraph graph, - final List contexts, - final Resource issueType, - final SimpleIssue ... issues) throws DatabaseException { - Resource model = graph.sync(new PossibleModel(contexts.get(0))); - if(model == null) throw new DatabaseException("No model for main context"); - - setSimpleIssuesAsync(graph, model, contexts, issueType, issues); - } - - /** - * Creates and removes issues so that after the operation, - * the context has exactly the given issues with the given type. - * Because this method is called in read transaction, it - * makes a write transaction if necessary and continues the - * operation there. - */ - public static void setSimpleIssuesAsync(ReadGraph graph, - final Resource model, - final List contexts, - final Resource issueType, - final SimpleIssue ... issues) throws DatabaseException { - Set oldIssues = getSimpleIssues(graph, contexts, issueType); - - boolean needsUpdating = false; - if(issues.length != oldIssues.size()) - needsUpdating = true; - else - for(SimpleIssue newIssue : issues) { - if(!oldIssues.contains(newIssue)) { - needsUpdating = true; - break; - } - } - if(needsUpdating) { - VirtualGraphSupport support = graph.getService(VirtualGraphSupport.class); - VirtualGraph vg = support.getWorkspacePersistent(IssueConstants.ISSUE_VG); - Simantics.getSession().asyncRequest(new WriteRequest(vg) { - - @Override - public void perform(WriteGraph graph) throws DatabaseException { - setSimpleIssues(graph, model, contexts, issueType, issues); - } - }); - } - } - - public static Resource addIssueSource(WriteGraph g, Resource model, Resource sourceType, String name) throws DatabaseException { - Layer0 L0 = Layer0.getInstance(g); - Layer0X L0X = Layer0X.getInstance(g); - - Resource source = g.newResource(); - g.claim(source, L0.InstanceOf, null, sourceType); - g.addLiteral(source, L0.HasName, L0.NameOf, L0.String, name, Bindings.STRING); - g.claim(source, L0X.IsActivatedBy, L0X.Activates, model); - g.claim(source, L0.PartOf, L0.ConsistsOf, model); - return source; - } - - public static String pathString(String uri, int startIndex) { - StringBuilder sb = new StringBuilder(uri.length() - startIndex + 1); - sb.append(URIStringUtils.NAMESPACE_PATH_SEPARATOR); - while (true) { - int nextSlash = uri.indexOf(URIStringUtils.NAMESPACE_PATH_SEPARATOR, startIndex); - if (nextSlash == -1) { - sb.append(URIStringUtils.unescape(uri.substring(startIndex, uri.length()))); - break; - } - sb.append(URIStringUtils.unescape(uri.substring(startIndex, nextSlash))).append(URIStringUtils.NAMESPACE_PATH_SEPARATOR); - startIndex = nextSlash + 1; - } - return sb.toString(); - } - -} +/******************************************************************************* + * Copyright (c) 2007, 2011 Association for Decentralized Information Management + * in Industry THTH ry. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * VTT Technical Research Centre of Finland - initial API and implementation + *******************************************************************************/ +package org.simantics.issues.common; + +import gnu.trove.set.hash.THashSet; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.simantics.Simantics; +import org.simantics.databoard.Bindings; +import org.simantics.databoard.util.URIStringUtils; +import org.simantics.db.Disposable; +import org.simantics.db.Issue; +import org.simantics.db.ReadGraph; +import org.simantics.db.RequestProcessor; +import org.simantics.db.Resource; +import org.simantics.db.Session; +import org.simantics.db.VirtualGraph; +import org.simantics.db.WriteGraph; +import org.simantics.db.common.primitiverequest.Objects; +import org.simantics.db.common.procedure.adapter.DisposableSyncListener; +import org.simantics.db.common.procedure.adapter.TransientCacheListener; +import org.simantics.db.common.procedure.single.SingleSetSyncListener; +import org.simantics.db.common.request.ResourceRead3; +import org.simantics.db.common.request.WriteRequest; +import org.simantics.db.common.utils.ListUtils; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.layer0.request.ActiveModels; +import org.simantics.db.layer0.request.Model; +import org.simantics.db.layer0.request.PossibleModel; +import org.simantics.db.layer0.util.RemoverUtil; +import org.simantics.db.layer0.variable.Variable; +import org.simantics.db.service.VirtualGraphSupport; +import org.simantics.issues.Severity; +import org.simantics.issues.ontology.IssueResource; +import org.simantics.layer0.Layer0; +import org.simantics.operation.Layer0X; +import org.simantics.scl.runtime.function.FunctionImpl2; +import org.simantics.utils.datastructures.Pair; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Tuukka Lehtonen + */ +public class IssueUtils { + private static final Logger LOGGER = LoggerFactory.getLogger(IssueUtils.class); + + public static Resource toSeverityResource(IssueResource ISSUE, Severity severity) { + switch (severity) { + case ERROR: return ISSUE.Severity_Error; + case FATAL: return ISSUE.Severity_Fatal; + case INFO: return ISSUE.Severity_Info; + case WARNING: return ISSUE.Severity_Warning; + case NOTE: return ISSUE.Severity_Note; + default: return null; + } + } + + public static Severity toSeverity(IssueResource ISSUE, Resource severity) { + if (severity == null) + return null; + if (severity.equals(ISSUE.Severity_Fatal)) + return Severity.FATAL; + if (severity.equals(ISSUE.Severity_Error)) + return Severity.ERROR; + if (severity.equals(ISSUE.Severity_Info)) + return Severity.INFO; + if (severity.equals(ISSUE.Severity_Warning)) + return Severity.WARNING; + if (severity.equals(ISSUE.Severity_Note)) + return Severity.NOTE; + return null; + } + + private static class IssueSourceDirtyListener extends FunctionImpl2, Boolean> { + + private final IssueSource is; + + public IssueSourceDirtyListener(IssueSource is) { + this.is = is; + } + + @Override + public Boolean apply(ReadGraph graph, final List resources) { + VirtualGraphSupport support = graph.getService(VirtualGraphSupport.class); + VirtualGraph vg = support.getWorkspacePersistent(IssueConstants.ISSUE_VG); + if (graph instanceof WriteGraph) { + try { + if(is.needUpdate(graph, resources)) { + graph.sync(new WriteRequest(vg) { + @Override + public void perform(WriteGraph graph) throws DatabaseException { + is.update(graph, resources); + } + }); + } + } catch (DatabaseException e) { + LOGGER.error("Updating issue source failed.", e); + } + } else { + Session session = Simantics.getSession(); + session.asyncRequest(new WriteRequest(vg) { + @Override + public void perform(WriteGraph graph) throws DatabaseException { + is.update(graph, resources); + } + }); + } + + return true; + } + + } + + private static class IssueSourceManagedIssuesListener extends SingleSetSyncListener { + + private final HashMap listeners = new HashMap<>(); + private final AtomicBoolean disposed; + private final Resource source; + private final Resource model; + + public IssueSourceManagedIssuesListener(AtomicBoolean disposed, Resource source, Resource model) { + this.disposed = disposed; + this.source = source; + this.model = model; + } + + class IssueValidityListener extends DisposableSyncListener { + + final private Resource issue; + + public IssueValidityListener(Resource issue) { + this.issue = issue; + } + + @Override + public void execute(ReadGraph graph, Boolean valid) throws DatabaseException { + if(!valid) { + VirtualGraphSupport support = graph.getService(VirtualGraphSupport.class); + VirtualGraph vg = support.getWorkspacePersistent(IssueConstants.ISSUE_VG); + graph.asyncRequest(new WriteRequest(vg) { + + @Override + public void perform(WriteGraph graph) throws DatabaseException { + Issue desc = graph.sync(new StandardIssueDescription(issue)); + if(desc == null) + return; + Resource context = (Resource)desc.getMainContext(); + new DependencyIssueSynchronizer2(context, source).perform(graph); + } + + }); + } + } + + @Override + public void exception(ReadGraph graph, Throwable throwable) throws DatabaseException { + LOGGER.error("IssueValidityListener received an exception.", throwable); + } + + } + + @Override + public void add(ReadGraph graph, final Resource issue) throws DatabaseException { + IssueValidityListener listener = new IssueValidityListener(issue); + + graph.syncRequest(new ResourceRead3(issue, model, source) { + + @Override + public Boolean perform(ReadGraph graph) throws DatabaseException { + Issue desc = graph.sync(new StandardIssueDescription(resource)); + if(desc == null) + return false; + Resource context = (Resource)desc.getMainContext(); + return graph.syncRequest(new DependencyIssueValidator2(context, resource2, resource3), TransientCacheListener.instance()); + } + + }, listener); + + listeners.put(issue, listener); + } + + @Override + public void remove(ReadGraph graph, final Resource issue) throws DatabaseException { + IssueValidityListener listener = listeners.remove(issue); + if(listener != null) + listener.dispose(); + } + + @Override + public void exception(ReadGraph graph, Throwable t) { + LOGGER.error("IssueSourceManagedIssuesListener received an exception.", t); + } + + @Override + public boolean isDisposed() { + boolean disp = disposed.get(); + if (disp) { + // Ensure validity listeners are cleared eventually. + if (!listeners.isEmpty()) { + for (IssueValidityListener listener : listeners.values()) { + listener.dispose(); + } + listeners.clear(); + } + } + return disp; + } + + } + + private static class ActiveIssueSourceListener extends SingleSetSyncListener { + + private final AtomicBoolean disposed; + private Map> sources = new HashMap<>(); + + public ActiveIssueSourceListener(AtomicBoolean disposed) { + this.disposed = disposed; + } + + @Override + public void add(ReadGraph graph, final Resource source) throws DatabaseException { + IssueResource ISSUE = IssueResource.getInstance(graph); + boolean isListeningTracker = graph.isInstanceOf(source, ISSUE.Sources_ListeningDependencyTracker); + IssueSource is = graph.adapt(source, IssueSource.class); + final Resource model = isListeningTracker ? graph.syncRequest(new Model(source)) : null; + + IssueSourceDirtyListener listener = new IssueSourceDirtyListener(is); + is.addDirtyListener(listener); + sources.put(source, Pair.make(is, listener)); + + if (isListeningTracker) { + graph.syncRequest( + new Objects(source, ISSUE.IssueSource_Manages), + new IssueSourceManagedIssuesListener(disposed, source, model)); + } + } + + @Override + public void remove(ReadGraph graph, final Resource source) throws DatabaseException { + Pair is = sources.remove(source); + if (is != null) + is.first.removeDirtyListener(is.second); + } + + @Override + public void exception(ReadGraph graph, Throwable t) { + LOGGER.error("ActiveIssueSourceListener received an exception.", t); + } + + @Override + public boolean isDisposed() { + return disposed.get(); + } + + } + + public static Disposable listenActiveProjectIssueSources(RequestProcessor processor, Resource project) throws DatabaseException { + final AtomicBoolean disposed = new AtomicBoolean(false); + processor.syncRequest( + new ActiveProjectIssueSources(project, IssueResource.getInstance(processor).ContinuousIssueSource), + new ActiveIssueSourceListener(disposed)); + return new Disposable() { + @Override + public void dispose() { + disposed.set(true); + } + }; + } + + public static List getContextsForProperty(ReadGraph graph, Variable property) throws DatabaseException { + IssueResource ISSUE = IssueResource.getInstance(graph); + Variable issueVariable = property.getParent(graph); + Resource issueResource = issueVariable.getRepresents(graph); + Resource list = graph.getPossibleObject(issueResource, ISSUE.Issue_HasContexts); + if(list != null) + return ListUtils.toList(graph, list); + else + return Collections.emptyList(); + } + + public static void writeAdditionalContext(WriteGraph graph, Resource issue, List contexts) throws DatabaseException { + + if(contexts.isEmpty()) return; + + IssueResource IR = IssueResource.getInstance(graph); + + // The main context + graph.claim(issue, IR.Issue_HasContext, contexts.get(0)); + // A possible parent + Layer0 L0 = Layer0.getInstance(graph); + Resource parent = graph.getPossibleObject(contexts.get(0), L0.PartOf); + if(parent != null) { + graph.claim(issue, IR.Issue_HasContext, parent); + } + + } + public static void newUserIssue(WriteGraph graph, String label, Resource severity, List contexts) throws DatabaseException { + + Resource model = graph.sync(new PossibleModel(contexts.get(0))); + if(model == null) throw new DatabaseException("No model for main context"); + + newUserIssueForModel(graph, model, label, severity, contexts); + + } + + public static Resource newUserIssueForModel(WriteGraph graph) throws DatabaseException { + + Resource project = Simantics.getProjectResource(); + Collection activeModels = graph.syncRequest(new ActiveModels(project)); + if (activeModels.size() != 1) + return null; + + IssueResource ISSUE = IssueResource.getInstance(graph); + Resource issue = null; + for (Resource model : activeModels) { + issue = newUserIssueForModel(graph, model, "New User Issue", ISSUE.Severity_Note, Collections.emptyList()); + } + return issue; + + } + + + public static Resource newUserIssueForModel(WriteGraph graph, Resource model, String label, Resource severity, List contexts) throws DatabaseException { + IssueResource ISSUE = IssueResource.getInstance(graph); + return newUserIssueForModel(graph, model, ISSUE.Issue, label, severity, contexts); + } + + public static Resource newUserIssueForModel(WriteGraph graph, Resource model, Resource type, String label, Resource severity, List contexts) throws DatabaseException { + + Layer0 L0 = Layer0.getInstance(graph); + IssueResource ISSUE = IssueResource.getInstance(graph); + + Resource issue = graph.newResource(); + graph.claim(issue, ISSUE.UserIssue, ISSUE.UserIssue, issue); + graph.claim(issue, L0.InstanceOf, null, type); + graph.claim(issue, ISSUE.Issue_HasSeverity, null, severity); + graph.claim(issue, ISSUE.Issue_HasContexts, ListUtils.create(graph, L0.List, contexts)); + writeAdditionalContext(graph, issue, contexts); + graph.claimLiteral(issue, L0.HasName, UUID.randomUUID().toString(), Bindings.STRING); + graph.claimLiteral(issue, L0.HasLabel, label, Bindings.STRING); + DateFormat format = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss"); + Object time = format.format(new Date()); + graph.claimLiteral(issue, ISSUE.Issue_creationTime, time, Bindings.STRING); + graph.claim(model, L0.ConsistsOf, L0.PartOf, issue); + return issue; + + } + + /** + * Creates a new issue based on SimpleIssue structure. + */ + public static void newSimpleIssueForModel(WriteGraph graph, Resource model, Resource issueType, List contexts, SimpleIssue simpleIssue) throws DatabaseException { + + Layer0 L0 = Layer0.getInstance(graph); + IssueResource ISSUE = IssueResource.getInstance(graph); + + Resource issue = graph.newResource(); + graph.claim(issue, L0.InstanceOf, null, issueType); + graph.claim(issue, ISSUE.Issue_HasSeverity, null, toSeverityResource(ISSUE, simpleIssue.severity)); + graph.claim(issue, ISSUE.Issue_HasContexts, ListUtils.create(graph, L0.List, contexts)); + writeAdditionalContext(graph, issue, contexts); + graph.claimLiteral(issue, L0.HasName, UUID.randomUUID().toString(), Bindings.STRING); + graph.claimLiteral(issue, L0.HasLabel, simpleIssue.label, Bindings.STRING); + DateFormat format = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss"); + Object time = format.format(new Date()); + graph.claimLiteral(issue, ISSUE.Issue_creationTime, time, Bindings.STRING); + graph.claim(model, L0.ConsistsOf, L0.PartOf, issue); + + } + + /** + * Returns the set of all issues with given type. + */ + public static Set getSimpleIssues(ReadGraph graph, List contexts, Resource issueType) + throws DatabaseException { + Layer0 L0 = Layer0.getInstance(graph); + IssueResource ISSUE = IssueResource.getInstance(graph); + + THashSet currentIssues = null; + for(Resource issue : graph.getObjects(contexts.get(0), ISSUE.Issue_HasContext_Inverse)) { + if(!graph.isInstanceOf(issue, issueType)) + continue; + List curContexts = + ListUtils.toList(graph, graph.getSingleObject(issue, ISSUE.Issue_HasContexts)); + if(!contexts.equals(curContexts)) + continue; + if(currentIssues == null) + currentIssues = new THashSet(); + currentIssues.add(new SimpleIssue( + (String)graph.getRelatedValue(issue, L0.HasLabel), + toSeverity(ISSUE, graph.getSingleObject(issue, ISSUE.Issue_HasSeverity)), + issue)); + } + if(currentIssues == null) + return Collections.emptySet(); + else + return currentIssues; + } + + /** + * Creates and removes issues so that after the operation, + * the context has exactly the given issues with the given type. + */ + public static void setSimpleIssues(WriteGraph graph, Resource model, List contexts, Resource issueType, SimpleIssue ... issues) throws DatabaseException { + Set currentIssues = getSimpleIssues(graph, contexts, issueType); + for(SimpleIssue newIssue : issues) { + if(currentIssues.contains(newIssue)) + currentIssues.remove(newIssue); + else + newSimpleIssueForModel(graph, model, issueType, contexts, newIssue); + } + for(SimpleIssue oldIssue : currentIssues) + RemoverUtil.remove(graph, oldIssue.issueResource); + } + + /** + * Creates and removes issues so that after the operation, + * the context has exactly the given issues with the given type. + */ + public static void setSimpleIssues(WriteGraph graph, List contexts, Resource issueType, SimpleIssue ... issues) throws DatabaseException { + Resource model = graph.sync(new PossibleModel(contexts.get(0))); + if(model == null) throw new DatabaseException("No model for main context"); + + setSimpleIssues(graph, model, contexts, issueType, issues); + } + + /** + * Creates and removes issues so that after the operation, + * the context has exactly the given issues with the given type. + * Because this method is called in read transaction, it + * makes a write transaction if necessary and continues the + * operation there. + */ + public static void setSimpleIssuesAsync(ReadGraph graph, + final List contexts, + final Resource issueType, + final SimpleIssue ... issues) throws DatabaseException { + Resource model = graph.sync(new PossibleModel(contexts.get(0))); + if(model == null) throw new DatabaseException("No model for main context"); + + setSimpleIssuesAsync(graph, model, contexts, issueType, issues); + } + + /** + * Creates and removes issues so that after the operation, + * the context has exactly the given issues with the given type. + * Because this method is called in read transaction, it + * makes a write transaction if necessary and continues the + * operation there. + */ + public static void setSimpleIssuesAsync(ReadGraph graph, + final Resource model, + final List contexts, + final Resource issueType, + final SimpleIssue ... issues) throws DatabaseException { + Set oldIssues = getSimpleIssues(graph, contexts, issueType); + + boolean needsUpdating = false; + if(issues.length != oldIssues.size()) + needsUpdating = true; + else + for(SimpleIssue newIssue : issues) { + if(!oldIssues.contains(newIssue)) { + needsUpdating = true; + break; + } + } + if(needsUpdating) { + VirtualGraphSupport support = graph.getService(VirtualGraphSupport.class); + VirtualGraph vg = support.getWorkspacePersistent(IssueConstants.ISSUE_VG); + Simantics.getSession().asyncRequest(new WriteRequest(vg) { + + @Override + public void perform(WriteGraph graph) throws DatabaseException { + setSimpleIssues(graph, model, contexts, issueType, issues); + } + }); + } + } + + public static Resource addIssueSource(WriteGraph g, Resource model, Resource sourceType, String name) throws DatabaseException { + Layer0 L0 = Layer0.getInstance(g); + Layer0X L0X = Layer0X.getInstance(g); + + Resource source = g.newResource(); + g.claim(source, L0.InstanceOf, null, sourceType); + g.addLiteral(source, L0.HasName, L0.NameOf, L0.String, name, Bindings.STRING); + g.claim(source, L0X.IsActivatedBy, L0X.Activates, model); + g.claim(source, L0.PartOf, L0.ConsistsOf, model); + return source; + } + + public static String pathString(String uri, int startIndex) { + StringBuilder sb = new StringBuilder(uri.length() - startIndex + 1); + sb.append(URIStringUtils.NAMESPACE_PATH_SEPARATOR); + while (true) { + int nextSlash = uri.indexOf(URIStringUtils.NAMESPACE_PATH_SEPARATOR, startIndex); + if (nextSlash == -1) { + sb.append(URIStringUtils.unescape(uri.substring(startIndex, uri.length()))); + break; + } + sb.append(URIStringUtils.unescape(uri.substring(startIndex, nextSlash))).append(URIStringUtils.NAMESPACE_PATH_SEPARATOR); + startIndex = nextSlash + 1; + } + return sb.toString(); + } + +}