--- /dev/null
+package org.simantics.modeling.utils;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Collection;\r
+import java.util.Collections;\r
+import java.util.Map;\r
+import java.util.Set;\r
+\r
+import org.eclipse.core.runtime.ILog;\r
+import org.eclipse.core.runtime.IProgressMonitor;\r
+import org.eclipse.core.runtime.IStatus;\r
+import org.eclipse.core.runtime.Platform;\r
+import org.eclipse.core.runtime.Status;\r
+import org.eclipse.core.runtime.SubMonitor;\r
+import org.simantics.Simantics;\r
+import org.simantics.db.Issue;\r
+import org.simantics.db.Resource;\r
+import org.simantics.db.Session;\r
+import org.simantics.db.VirtualGraph;\r
+import org.simantics.db.WriteGraph;\r
+import org.simantics.db.common.request.WriteRequest;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.db.layer0.util.ModelTransferableGraphSourceRequest;\r
+import org.simantics.db.layer0.util.RemoverUtil;\r
+import org.simantics.db.service.VirtualGraphSupport;\r
+import org.simantics.issues.common.AllBatchIssueSources;\r
+import org.simantics.issues.common.BatchIssueDescriptions;\r
+import org.simantics.issues.common.BatchIssueSource;\r
+import org.simantics.issues.common.BatchIssueValidationContext;\r
+import org.simantics.issues.common.ComposedValidation;\r
+import org.simantics.issues.common.IssueByList;\r
+import org.simantics.issues.common.IssueConstants;\r
+import org.simantics.issues.ontology.IssueResource;\r
+import org.simantics.issues.preferences.IssuePreferenceUtil;\r
+import org.simantics.layer0.Layer0;\r
+import org.simantics.modeling.internal.Plugin;\r
+import org.simantics.modeling.requests.CollectionRequest;\r
+import org.simantics.modeling.requests.CollectionResult;\r
+import org.simantics.modeling.requests.Node;\r
+import org.simantics.utils.page.PageDesc;\r
+\r
+import gnu.trove.set.hash.THashSet;\r
+\r
+public class BatchValidations {\r
+\r
+ private static final boolean PERF = false;\r
+\r
+ public static Collection<Resource> fillConfig(IProgressMonitor monitor, Collection<Resource> models) throws DatabaseException {\r
+ final CollectionResult result = Simantics.getSession().syncRequest(new CollectionRequest(monitor, PageDesc.DEFAULT, models.toArray(Resource.NONE)));\r
+ if (result == null)\r
+ return Collections.emptyList();\r
+ return toComposites(result.breadthFirstFlatten());\r
+ }\r
+\r
+ private static Collection<Resource> toComposites(Collection<Node> nodes) {\r
+ Collection<Resource> result = new ArrayList<Resource>(nodes.size());\r
+ for (Node n : nodes) {\r
+ Resource composite = n.getDefiningResources().resources[0];\r
+ Resource diagram = n.getDiagramResource();\r
+ if (composite != null && diagram != null)\r
+ result.add(composite);\r
+ }\r
+ return result;\r
+ }\r
+\r
+ public static Map<Resource, Set<Issue>> validate(IProgressMonitor monitor, BatchIssueSource source, BatchIssueValidationContext context) throws DatabaseException {\r
+ Session session = Simantics.getSession();\r
+ return session.syncRequest(new ComposedValidation(monitor, source, context));\r
+ }\r
+\r
+ /**\r
+ * @param monitor\r
+ * @param source\r
+ * @param issues\r
+ * @return\r
+ * @throws DatabaseException\r
+ */\r
+ public static int store(final IProgressMonitor monitor,\r
+ final Resource source, final Map<Resource, Set<Issue>> issues)\r
+ throws DatabaseException {\r
+ return store(monitor, source, issues, Integer.MAX_VALUE);\r
+ }\r
+\r
+ /**\r
+ * @param monitor\r
+ * @param source\r
+ * @param issues\r
+ * @param maxIssuesToWrite\r
+ * @return number of issues written (added)\r
+ * @throws DatabaseException\r
+ */\r
+ public static int store(final IProgressMonitor monitor,\r
+ final Resource source, final Map<Resource, Set<Issue>> issues,\r
+ final int maxIssuesToWrite) throws DatabaseException {\r
+\r
+ if (issues.isEmpty() || maxIssuesToWrite <= 0)\r
+ return 0;\r
+\r
+ Session session = Simantics.getSession();\r
+ VirtualGraphSupport support = session.getService(VirtualGraphSupport.class);\r
+ VirtualGraph vg = support.getWorkspacePersistent(IssueConstants.ISSUE_VG);\r
+ final int[] writtenIssues = { 0 };\r
+\r
+ session.syncRequest(new WriteRequest(vg) {\r
+\r
+ @Override\r
+ public void perform(WriteGraph graph) throws DatabaseException {\r
+\r
+ for(Map.Entry<Resource, Set<Issue>> entry : issues.entrySet()) {\r
+\r
+ if (monitor.isCanceled())\r
+ return;\r
+\r
+ Resource context = entry.getKey();\r
+\r
+ Set<Issue> current = entry.getValue();\r
+ Set<Issue> existing = graph.sync(new BatchIssueDescriptions(source, context));\r
+\r
+ if(!existing.equals(current)) {\r
+\r
+ Set<Issue> added = new THashSet<Issue>(current);\r
+ Set<Issue> removed = new THashSet<Issue>(existing);\r
+ added.removeAll(existing);\r
+ removed.removeAll(current);\r
+\r
+ for(Issue add : added) {\r
+ add.write(graph, source);\r
+ // Stop if write limit is reached.\r
+ if (++writtenIssues[0] >= maxIssuesToWrite)\r
+ return;\r
+ }\r
+ for(Issue remove : removed) {\r
+ Resource issue = graph.sync(new IssueByList(source, remove));\r
+ if (issue == null)\r
+ // FIXME: when can this happen and what should be done in this case?\r
+ continue;\r
+ graph.deny(issue, Layer0.getInstance(graph).PartOf);\r
+ graph.deny(source, IssueResource.getInstance(graph).IssueSource_Manages, issue);\r
+ RemoverUtil.remove(graph, issue);\r
+ }\r
+\r
+ }\r
+\r
+ }\r
+\r
+ }\r
+ });\r
+\r
+ return writtenIssues[0];\r
+\r
+ }\r
+\r
+ public static void runAll(IProgressMonitor monitor, Resource model) throws DatabaseException {\r
+ runAll(monitor, model, model);\r
+ }\r
+\r
+ public static void runAll(IProgressMonitor monitor, Resource model, Resource resource) throws DatabaseException {\r
+ final Session session = Simantics.getSession();\r
+ final Collection<BatchIssueSource> validations = session.sync( new AllBatchIssueSources(model) );\r
+ SubMonitor progress = SubMonitor.convert(monitor, "Validate Model", 100*validations.size());\r
+ BatchIssueValidationContext context = new BatchIssueValidationContext();\r
+ context.domain = ModelTransferableGraphSourceRequest.getDomainOnly(session, monitor, resource);\r
+ context.contexts = fillConfig(progress, Collections.singletonList(resource));\r
+ int maxWrittenIssues = IssuePreferenceUtil.getPreferences().maxBatchIssuesToWrite;\r
+ int totalIssueCount = 0;\r
+ int writtenIssueCount = 0;\r
+ if (!context.contexts.isEmpty()) {\r
+ long t0 = System.nanoTime();\r
+ for(BatchIssueSource bis : validations) {\r
+ if (monitor.isCanceled())\r
+ return;\r
+ long startTime = System.nanoTime();\r
+ if (PERF)\r
+ System.out.println("validate " + bis);\r
+ Map<Resource, Set<Issue>> is = validate(progress.newChild(90, SubMonitor.SUPPRESS_NONE), bis, context);\r
+ int issueCount = count(is);\r
+ long validationTime = System.nanoTime();\r
+ if (PERF)\r
+ System.out.println("store " + issueCount + " issues");\r
+ int wroteIssues = store(progress.newChild(10, SubMonitor.SUPPRESS_NONE), bis.getResource(), is, Math.max(0, maxWrittenIssues - writtenIssueCount));\r
+ totalIssueCount += issueCount;\r
+ writtenIssueCount += wroteIssues;\r
+ long writeTime = System.nanoTime();\r
+ if (PERF) {\r
+ System.out.println("validation time: " + ((validationTime-startTime)*1e-9) + " s");\r
+ System.out.println("issue store time: " + ((writeTime-validationTime)*1e-9) + " s");\r
+ }\r
+ }\r
+ long tf = System.nanoTime();\r
+ if (PERF) {\r
+ System.out.println("total validation time: " + ((tf-t0)*1e-9) + " s");\r
+ }\r
+ }\r
+ if (totalIssueCount > maxWrittenIssues) {\r
+ ILog log = Platform.getLog(Platform.getBundle(Plugin.PLUGIN_ID));\r
+ log.log(new Status(IStatus.WARNING, Plugin.PLUGIN_ID, "Batch issue validation produced " + totalIssueCount + " issues which is more than it was allowed to write into the database. The write limit was " + maxWrittenIssues + "."));\r
+ }\r
+ }\r
+\r
+ /**\r
+ * @param map\r
+ * @return\r
+ */\r
+ @SuppressWarnings("rawtypes")\r
+ private static int count(Map map) {\r
+ int result = 0;\r
+ for (Object obj : map.values()) {\r
+ if (obj instanceof Set<?>) {\r
+ Set<?> set = (Set<?>) obj;\r
+ result += set.size();\r
+ }\r
+ }\r
+ return result;\r
+ }\r
+\r
+}\r