--- /dev/null
+package org.simantics.issues.common;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.simantics.db.Issue;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.common.QueryMemoryWatcher;
+import org.simantics.db.common.utils.Logger;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.layer0.util.ModelTransferableGraphSourceRequest.DomainOnlyProcessor;
+import org.simantics.db.service.CollectionSupport;
+import org.simantics.db.service.QueryControl;
+import org.simantics.issues.ontology.IssueResource;
+import org.simantics.layer0.Layer0;
+import org.simantics.scl.db.SCLFunctions;
+import org.simantics.scl.runtime.function.Function1;
+
+import gnu.trove.set.hash.THashSet;
+
+/**
+ * @author Antti Villberg
+ */
+public class ConstraintIssueSource implements BatchIssueSource {
+
+ private final Resource resource;
+
+ public ConstraintIssueSource(Resource resource) {
+ this.resource = resource;
+ }
+
+ @Override
+ public Map<Resource, Set<Issue>> run(IProgressMonitor monitor, ReadGraph graph, BatchIssueValidationContext context) throws DatabaseException {
+
+ Layer0 L0 = Layer0.getInstance(graph);
+ Set<Issue> emptySet = Collections.emptySet();
+ CollectionSupport cs = graph.getService(CollectionSupport.class);
+ Map<Resource,Set<Issue>> result = cs.createMap(Set.class);
+ monitor.setTaskName("Constraint analysis");
+
+ DomainOnlyProcessor domain = context.domain;
+
+ int entityCount = domain.internals.size();
+
+ IssueResource ISSUE = IssueResource.getInstance(graph);
+ Resource type = graph.getSingleType(resource, ISSUE.IssueSource);
+ List<Function1<Resource, List<Issue>>> validators = new ArrayList<>();
+ for(Resource constraint : graph.getObjects(type, ISSUE.IssueSource_HasConstraint)) {
+ Function1<Resource, List<Issue>> validator = graph.getRelatedValue2(constraint, L0.Constraint_Validator, constraint);
+ //Resource function = graph.getSingleObject(constraint, L0.Constraint_Validator);
+ validators.add(validator);
+ }
+
+ QueryControl qc = graph.getService(QueryControl.class);
+ qc.flush(graph);
+
+ // Allow this process to make 50k queries
+ QueryMemoryWatcher memory = new QueryMemoryWatcher(graph, 50000, 0.5, 300);
+
+ SCLFunctions.runWithGraph(graph, () -> {
+
+ int totalExaminedCount = 0;
+ int examinedCount = 1000;
+
+ for(Resource r : domain.internals) {
+
+ Set<Issue> set = emptySet;
+ if (examinedCount >= 1000) {
+ monitor.subTask(contextProgressMessage(totalExaminedCount, entityCount));
+ examinedCount = 0;
+ if(monitor.isCanceled()) return;
+ memory.maintain();
+ }
+ for(Function1<Resource, List<Issue>> validator : validators) {
+ try {
+ @SuppressWarnings("unchecked")
+ List<Issue> issues = validator.apply(r);//(List<Issue>)Functions.exec(graph, validator, graph, r);
+ if (issues != null && !issues.isEmpty()) {
+ if (set == emptySet)
+ set = new THashSet<Issue>();
+ set.addAll(issues);
+ }
+ } catch (Throwable t) {
+ Logger.defaultLogError(t);
+ }
+ }
+ ++totalExaminedCount;
+ ++examinedCount;
+ if(!set.isEmpty())
+ result.put(r, set);
+
+ }
+
+ });
+
+ return result;
+
+ }
+
+ private static String contextProgressMessage(int totalExaminedCount, int entityCount) {
+ StringBuilder sb = new StringBuilder(80)
+ .append("Validating resources").append(" ").append(100*totalExaminedCount / entityCount).append("% ready.");
+ return sb.toString();
+ }
+
+ @Override
+ public Resource getResource() {
+ return resource;
+ }
+
+}