]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.modeling/src/org/simantics/modeling/utils/BatchValidations.java
Allow batch issue validation to remove issues with disconnected context
[simantics/platform.git] / bundles / org.simantics.modeling / src / org / simantics / modeling / utils / BatchValidations.java
1 package org.simantics.modeling.utils;
2
3 import java.util.ArrayList;
4 import java.util.Collection;
5 import java.util.Collections;
6 import java.util.Map;
7 import java.util.Set;
8
9 import org.eclipse.core.runtime.ILog;
10 import org.eclipse.core.runtime.IProgressMonitor;
11 import org.eclipse.core.runtime.IStatus;
12 import org.eclipse.core.runtime.Platform;
13 import org.eclipse.core.runtime.Status;
14 import org.eclipse.core.runtime.SubMonitor;
15 import org.simantics.Simantics;
16 import org.simantics.db.Issue;
17 import org.simantics.db.ReadGraph;
18 import org.simantics.db.Resource;
19 import org.simantics.db.Session;
20 import org.simantics.db.Statement;
21 import org.simantics.db.VirtualGraph;
22 import org.simantics.db.WriteGraph;
23 import org.simantics.db.common.request.WriteRequest;
24 import org.simantics.db.exception.DatabaseException;
25 import org.simantics.db.layer0.util.ModelTransferableGraphSourceRequest;
26 import org.simantics.db.layer0.util.RemoverUtil;
27 import org.simantics.db.service.VirtualGraphSupport;
28 import org.simantics.issues.common.AllBatchIssueSources;
29 import org.simantics.issues.common.BatchIssueDescriptions;
30 import org.simantics.issues.common.BatchIssueSource;
31 import org.simantics.issues.common.BatchIssueValidationContext;
32 import org.simantics.issues.common.ComposedValidation;
33 import org.simantics.issues.common.IssueByList;
34 import org.simantics.issues.common.IssueConstants;
35 import org.simantics.issues.ontology.IssueResource;
36 import org.simantics.issues.preferences.IssuePreferenceUtil;
37 import org.simantics.layer0.Layer0;
38 import org.simantics.modeling.internal.Plugin;
39 import org.simantics.modeling.requests.CollectionRequest;
40 import org.simantics.modeling.requests.CollectionResult;
41 import org.simantics.modeling.requests.Node;
42 import org.simantics.utils.page.PageDesc;
43
44 import gnu.trove.set.hash.THashSet;
45
46 public class BatchValidations {
47
48         private static final boolean PERF = false;
49
50         public static Collection<Resource> fillConfig(IProgressMonitor monitor, Collection<Resource> models) throws DatabaseException {
51                 final CollectionResult result = Simantics.getSession().syncRequest(new CollectionRequest(monitor, PageDesc.DEFAULT, models.toArray(Resource.NONE)));
52                 if (result == null)
53                         return Collections.emptyList();
54                 return toComposites(result.breadthFirstFlatten());
55         }
56
57         private static Collection<Resource> toComposites(Collection<Node> nodes) {
58                 Collection<Resource> result = new ArrayList<Resource>(nodes.size());
59                 for (Node n : nodes) {
60                         Resource composite = n.getDefiningResources().resources[0];
61                         Resource diagram = n.getDiagramResource();
62                         if (composite != null && diagram != null)
63                                 result.add(composite);
64                 }
65                 return result;
66         }
67
68         public static Map<Resource, Set<Issue>> validate(IProgressMonitor monitor, BatchIssueSource source, BatchIssueValidationContext context) throws DatabaseException {
69                 Session session = Simantics.getSession();
70                 return session.syncRequest(new ComposedValidation(monitor, source, context));
71         }
72
73         /**
74          * @param monitor
75          * @param source
76          * @param issues
77          * @return
78          * @throws DatabaseException
79          */
80         public static int store(final IProgressMonitor monitor,
81                         final Resource source, final Map<Resource, Set<Issue>> issues)
82                         throws DatabaseException {
83                 return store(monitor, source, issues, Integer.MAX_VALUE);
84         }
85
86         /**
87          * @param monitor
88          * @param source
89          * @param issues
90          * @param maxIssuesToWrite
91          * @return number of issues written (added)
92          * @throws DatabaseException
93          */
94         public static int store(final IProgressMonitor monitor,
95                         final Resource source, final Map<Resource, Set<Issue>> issues,
96                         final int maxIssuesToWrite) throws DatabaseException {
97
98                 if (issues.isEmpty() || maxIssuesToWrite <= 0)
99                         return 0;
100
101                 Session session = Simantics.getSession();
102                 VirtualGraphSupport support = session.getService(VirtualGraphSupport.class);
103                 VirtualGraph vg = support.getWorkspacePersistent(IssueConstants.ISSUE_VG);
104                 final int[] writtenIssues = { 0 };
105
106                 session.syncRequest(new WriteRequest(vg) {
107
108                         @Override
109                         public void perform(WriteGraph graph) throws DatabaseException {
110
111                                 for(Map.Entry<Resource, Set<Issue>> entry : issues.entrySet()) {
112
113                                         if (monitor.isCanceled())
114                                                 return;
115
116                                         Resource context = entry.getKey();
117
118                                         Set<Issue> current = entry.getValue();
119                                         Set<Issue> existing = graph.sync(new BatchIssueDescriptions(source, context));
120
121                                         if(!existing.equals(current)) {
122
123                                                 Set<Issue> added = new THashSet<Issue>(current);
124                                                 Set<Issue> removed = new THashSet<Issue>(existing);
125                                                 added.removeAll(existing);
126                                                 removed.removeAll(current);
127
128                                                 for(Issue add : added) {
129                                                         add.write(graph, source);
130                                                         // Stop if write limit is reached.
131                                                         if (++writtenIssues[0] >= maxIssuesToWrite)
132                                                                 return;
133                                                 }
134                                                 for(Issue remove : removed) {
135                                                         Resource issue = graph.sync(new IssueByList(source, remove));
136                                                         if (issue == null)
137                                                                 // FIXME: when can this happen and what should be done in this case?
138                                                                 continue;
139                                                         graph.deny(issue, Layer0.getInstance(graph).PartOf);
140                                                         graph.deny(source, IssueResource.getInstance(graph).IssueSource_Manages, issue);
141                                                         RemoverUtil.remove(graph, issue);
142                                                 }
143
144                                         }
145
146                                 }
147
148                         }
149                 });
150
151                 return writtenIssues[0];
152
153         }
154
155         public static void runAll(IProgressMonitor monitor, Resource model) throws DatabaseException {
156                 runAll(monitor, model, model);
157         }
158
159         public static void runAll(IProgressMonitor monitor, Resource model, Resource resource) throws DatabaseException {
160                 final Session session = Simantics.getSession();
161                 final Collection<BatchIssueSource> validations = session.sync( new AllBatchIssueSources(model) );
162                 SubMonitor progress = SubMonitor.convert(monitor, "Validate Model", 100*validations.size());
163                 BatchIssueValidationContext context = new BatchIssueValidationContext();
164                 context.domain = ModelTransferableGraphSourceRequest.getDomainOnly(session, monitor, resource);
165                 context.contexts = fillConfig(progress, Collections.singletonList(resource));
166                 int maxWrittenIssues = IssuePreferenceUtil.getPreferences().maxBatchIssuesToWrite;
167                 int totalIssueCount = 0;
168                 int writtenIssueCount = 0;
169                 if (!context.contexts.isEmpty()) {
170                         long t0 = System.nanoTime();
171                         for(BatchIssueSource bis : validations) {
172                                 if (monitor.isCanceled())
173                                         return;
174                                 long startTime = System.nanoTime();
175                                 if (PERF)
176                                         System.out.println("validate " + bis);
177                                 Map<Resource, Set<Issue>> is = validate(progress.newChild(90, SubMonitor.SUPPRESS_NONE), bis, context);
178                                 int issueCount = count(is);
179                                 long validationTime = System.nanoTime();
180                                 if (PERF)
181                                         System.out.println("store " + issueCount + " issues");
182                                 int wroteIssues = store(progress.newChild(10, SubMonitor.SUPPRESS_NONE), bis.getResource(), is, Math.max(0, maxWrittenIssues - writtenIssueCount));
183                                 totalIssueCount += issueCount;
184                                 writtenIssueCount += wroteIssues;
185                                 long writeTime = System.nanoTime();
186                                 if (PERF) {
187                                         System.out.println("validation time: " + ((validationTime-startTime)*1e-9) + " s");
188                                         System.out.println("issue store time: " + ((writeTime-validationTime)*1e-9) + " s");
189                                 }
190                         }
191                         long tf = System.nanoTime();
192                         if (PERF) {
193                                 System.out.println("total validation time: " + ((tf-t0)*1e-9) + " s");
194                         }
195                 }
196                 if (totalIssueCount > maxWrittenIssues) {
197                         ILog log = Platform.getLog(Platform.getBundle(Plugin.PLUGIN_ID));
198                         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 + "."));
199                 }
200         }
201
202         /**
203          * @param map
204          * @return
205          */
206         @SuppressWarnings("rawtypes")
207         private static int count(Map map) {
208                 int result = 0;
209                 for (Object obj : map.values()) {
210                         if (obj instanceof Set<?>) {
211                                 Set<?> set = (Set<?>) obj;
212                                 result += set.size();
213                         }
214                 }
215                 return result;
216         }
217
218         /**
219          * Checks if the specified <code>resourceToCheckForLinks</code> is linked to
220          * anything else besides itself and <code>excludeLinksTo</code>.
221          * 
222          * <p>
223          * This is used to if an issue context is still valid. We consider any issue
224          * context that is not attached to something else besides its issue context to
225          * be an invalid issue. Assertions and L0.InstanceOf do not count as external
226          * links.
227          * 
228          * @param graph database access handle
229          * @param resourceToCheckForLinks the resource to check for "external" links
230          * @param excludeLinksTo exclude links to this resource from evaluation
231          * @return <code>true</code> if there are links, <code>false</code> otherwise
232          * @throws DatabaseException 
233          */
234         public static boolean isLinkedToOtherThan(ReadGraph graph, Resource resourceToCheckForLinks,
235                         Resource excludeLinksTo)
236                                         throws DatabaseException
237         {
238                 Layer0 L0 = Layer0.getInstance(graph);
239                 for (Statement stm : graph.getStatements(resourceToCheckForLinks, L0.IsWeaklyRelatedTo)) {
240                         if (stm.isAsserted(resourceToCheckForLinks))
241                                 continue;
242                         if (stm.getPredicate().equals(L0.InstanceOf))
243                                 continue;
244                         Resource o = stm.getObject();
245                         if (o.equals(excludeLinksTo) || o.equals(resourceToCheckForLinks))
246                                 continue;
247
248                         return true;
249                 }
250                 return false;
251         }
252
253 }