package org.simantics.modeling.utils; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Map; import java.util.Set; import org.eclipse.core.runtime.ILog; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubMonitor; 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.ModelTransferableGraphSourceRequest; import org.simantics.db.layer0.util.RemoverUtil; import org.simantics.db.service.VirtualGraphSupport; import org.simantics.issues.common.AllBatchIssueSources; import org.simantics.issues.common.BatchIssueDescriptions; import org.simantics.issues.common.BatchIssueSource; import org.simantics.issues.common.BatchIssueValidationContext; import org.simantics.issues.common.ComposedValidation; import org.simantics.issues.common.IssueByList; import org.simantics.issues.common.IssueConstants; import org.simantics.issues.ontology.IssueResource; import org.simantics.issues.preferences.IssuePreferenceUtil; import org.simantics.layer0.Layer0; import org.simantics.modeling.internal.Plugin; import org.simantics.modeling.requests.CollectionRequest; import org.simantics.modeling.requests.CollectionResult; import org.simantics.modeling.requests.Node; import org.simantics.utils.page.PageDesc; import gnu.trove.set.hash.THashSet; public class BatchValidations { private static final boolean PERF = false; public static Collection fillConfig(IProgressMonitor monitor, Collection models) throws DatabaseException { final CollectionResult result = Simantics.getSession().syncRequest(new CollectionRequest(monitor, PageDesc.DEFAULT, models.toArray(Resource.NONE))); if (result == null) return Collections.emptyList(); return toComposites(result.breadthFirstFlatten()); } private static Collection toComposites(Collection nodes) { Collection result = new ArrayList(nodes.size()); for (Node n : nodes) { Resource composite = n.getDefiningResources().resources[0]; Resource diagram = n.getDiagramResource(); if (composite != null && diagram != null) result.add(composite); } return result; } public static Map> 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> 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> 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> entry : issues.entrySet()) { if (monitor.isCanceled()) return; Resource context = entry.getKey(); Set current = entry.getValue(); Set existing = graph.sync(new BatchIssueDescriptions(source, context)); if(!existing.equals(current)) { Set added = new THashSet(current); Set removed = new THashSet(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]; } public static void runAll(IProgressMonitor monitor, Resource model) throws DatabaseException { runAll(monitor, model, model); } public static void runAll(IProgressMonitor monitor, Resource model, Resource resource) throws DatabaseException { final Session session = Simantics.getSession(); final Collection validations = session.sync( new AllBatchIssueSources(model) ); SubMonitor progress = SubMonitor.convert(monitor, "Validate Model", 100*validations.size()); BatchIssueValidationContext context = new BatchIssueValidationContext(); context.domain = ModelTransferableGraphSourceRequest.getDomainOnly(session, monitor, resource); context.contexts = fillConfig(progress, Collections.singletonList(resource)); int maxWrittenIssues = IssuePreferenceUtil.getPreferences().maxBatchIssuesToWrite; int totalIssueCount = 0; int writtenIssueCount = 0; if (!context.contexts.isEmpty()) { long t0 = System.nanoTime(); for(BatchIssueSource bis : validations) { if (monitor.isCanceled()) return; long startTime = System.nanoTime(); if (PERF) System.out.println("validate " + bis); Map> is = validate(progress.newChild(90, SubMonitor.SUPPRESS_NONE), bis, context); int issueCount = count(is); long validationTime = System.nanoTime(); if (PERF) System.out.println("store " + issueCount + " issues"); int wroteIssues = store(progress.newChild(10, SubMonitor.SUPPRESS_NONE), bis.getResource(), is, Math.max(0, maxWrittenIssues - writtenIssueCount)); totalIssueCount += issueCount; writtenIssueCount += wroteIssues; long writeTime = System.nanoTime(); if (PERF) { System.out.println("validation time: " + ((validationTime-startTime)*1e-9) + " s"); System.out.println("issue store time: " + ((writeTime-validationTime)*1e-9) + " s"); } } long tf = System.nanoTime(); if (PERF) { System.out.println("total validation time: " + ((tf-t0)*1e-9) + " s"); } } if (totalIssueCount > maxWrittenIssues) { ILog log = Platform.getLog(Platform.getBundle(Plugin.PLUGIN_ID)); 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 + ".")); } } /** * @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 resourceToCheckForLinks is linked to * anything else besides itself and excludeLinksTo. * *

* 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 true if there are links, false 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; } }