+package org.simantics.issues.common;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.simantics.Simantics;
+import org.simantics.db.Issue;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.Session;
+import org.simantics.db.Statement;
+import org.simantics.db.VirtualGraph;
+import org.simantics.db.WriteGraph;
+import org.simantics.db.common.request.WriteRequest;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.layer0.util.RemoverUtil;
+import org.simantics.db.service.VirtualGraphSupport;
+import org.simantics.issues.ontology.IssueResource;
+import org.simantics.layer0.Layer0;
+
+import gnu.trove.set.hash.THashSet;
+
+public class BatchValidations {
+
+ private static final boolean PERF = false;
+
+ public static Map<Resource, Set<Issue>> validate(IProgressMonitor monitor, BatchIssueSource source, BatchIssueValidationContext context) throws DatabaseException {
+ Session session = Simantics.getSession();
+ return session.syncRequest(new ComposedValidation(monitor, source, context));
+ }
+
+ /**
+ * @param monitor
+ * @param source
+ * @param issues
+ * @return
+ * @throws DatabaseException
+ */
+ public static int store(final IProgressMonitor monitor,
+ final Resource source, final Map<Resource, Set<Issue>> issues)
+ throws DatabaseException {
+ return store(monitor, source, issues, Integer.MAX_VALUE);
+ }
+
+ /**
+ * @param monitor
+ * @param source
+ * @param issues
+ * @param maxIssuesToWrite
+ * @return number of issues written (added)
+ * @throws DatabaseException
+ */
+ public static int store(final IProgressMonitor monitor,
+ final Resource source, final Map<Resource, Set<Issue>> issues,
+ final int maxIssuesToWrite) throws DatabaseException {
+
+ if (issues.isEmpty() || maxIssuesToWrite <= 0)
+ return 0;
+
+ Session session = Simantics.getSession();
+ VirtualGraphSupport support = session.getService(VirtualGraphSupport.class);
+ VirtualGraph vg = support.getWorkspacePersistent(IssueConstants.ISSUE_VG);
+ final int[] writtenIssues = { 0 };
+
+ session.syncRequest(new WriteRequest(vg) {
+
+ @Override
+ public void perform(WriteGraph graph) throws DatabaseException {
+
+ for(Map.Entry<Resource, Set<Issue>> entry : issues.entrySet()) {
+
+ if (monitor.isCanceled())
+ return;
+
+ Resource context = entry.getKey();
+
+ Set<Issue> current = entry.getValue();
+ Set<Issue> existing = graph.sync(new BatchIssueDescriptions(source, context));
+
+ if(!existing.equals(current)) {
+
+ Set<Issue> added = new THashSet<Issue>(current);
+ Set<Issue> removed = new THashSet<Issue>(existing);
+ added.removeAll(existing);
+ removed.removeAll(current);
+
+ for(Issue add : added) {
+ add.write(graph, source);
+ // Stop if write limit is reached.
+ if (++writtenIssues[0] >= maxIssuesToWrite)
+ return;
+ }
+ for(Issue remove : removed) {
+ Resource issue = graph.sync(new IssueByList(source, remove));
+ if (issue == null)
+ // FIXME: when can this happen and what should be done in this case?
+ continue;
+ graph.deny(issue, Layer0.getInstance(graph).PartOf);
+ graph.deny(source, IssueResource.getInstance(graph).IssueSource_Manages, issue);
+ RemoverUtil.remove(graph, issue);
+ }
+
+ }
+
+ }
+
+ }
+ });
+
+ return writtenIssues[0];
+
+ }
+
+ /**
+ * @param map
+ * @return
+ */
+ @SuppressWarnings("rawtypes")
+ private static int count(Map map) {
+ int result = 0;
+ for (Object obj : map.values()) {
+ if (obj instanceof Set<?>) {
+ Set<?> set = (Set<?>) obj;
+ result += set.size();
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Checks if the specified <code>resourceToCheckForLinks</code> is linked to
+ * anything else besides itself and <code>excludeLinksTo</code>.
+ *
+ * <p>
+ * This is used to if an issue context is still valid. We consider any issue
+ * context that is not attached to something else besides its issue context to
+ * be an invalid issue. Assertions and L0.InstanceOf do not count as external
+ * links.
+ *
+ * @param graph database access handle
+ * @param resourceToCheckForLinks the resource to check for "external" links
+ * @param excludeLinksTo exclude links to this resource from evaluation
+ * @return <code>true</code> if there are links, <code>false</code> otherwise
+ * @throws DatabaseException
+ */
+ public static boolean isLinkedToOtherThan(ReadGraph graph, Resource resourceToCheckForLinks,
+ Resource excludeLinksTo)
+ throws DatabaseException
+ {
+ Layer0 L0 = Layer0.getInstance(graph);
+ for (Statement stm : graph.getStatements(resourceToCheckForLinks, L0.IsWeaklyRelatedTo)) {
+ if (stm.isAsserted(resourceToCheckForLinks))
+ continue;
+ if (stm.getPredicate().equals(L0.InstanceOf))
+ continue;
+ Resource o = stm.getObject();
+ if (o.equals(excludeLinksTo) || o.equals(resourceToCheckForLinks))
+ continue;
+
+ return true;
+ }
+ return false;
+ }
+
+}