1 /*******************************************************************************
2 * Copyright (c) 2012 Association for Decentralized Information Management in
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.annotation.ui;
14 import java.util.HashMap;
16 import java.util.UUID;
17 import java.util.function.Consumer;
19 import org.eclipse.jface.dialogs.IDialogSettings;
20 import org.eclipse.jface.resource.ImageDescriptor;
21 import org.eclipse.jface.window.Window;
22 import org.eclipse.swt.widgets.Display;
23 import org.eclipse.swt.widgets.Shell;
24 import org.eclipse.ui.PlatformUI;
25 import org.simantics.Simantics;
26 import org.simantics.annotation.ontology.AnnotationResource;
27 import org.simantics.annotation.ui.internal.SaveAnnotationDialog;
28 import org.simantics.databoard.Bindings;
29 import org.simantics.databoard.util.URIStringUtils;
30 import org.simantics.db.ReadGraph;
31 import org.simantics.db.Resource;
32 import org.simantics.db.WriteGraph;
33 import org.simantics.db.common.CommentMetadata;
34 import org.simantics.db.common.request.PossibleIndexRoot;
35 import org.simantics.db.common.request.ReadRequest;
36 import org.simantics.db.common.request.WriteRequest;
37 import org.simantics.db.common.utils.Logger;
38 import org.simantics.db.common.utils.NameUtils;
39 import org.simantics.db.exception.DatabaseException;
40 import org.simantics.db.layer0.adapter.Instances;
41 import org.simantics.db.layer0.request.PossibleModel;
42 import org.simantics.db.layer0.request.VariableRead;
43 import org.simantics.db.layer0.util.Layer0Utils;
44 import org.simantics.db.layer0.variable.Variable;
45 import org.simantics.db.layer0.variable.Variables;
46 import org.simantics.layer0.Layer0;
47 import org.simantics.selectionview.SelectionViewResources;
48 import org.simantics.structural.stubs.StructuralResource2;
49 import org.simantics.ui.workbench.dialogs.ResourceSelectionDialog3;
50 import org.simantics.utils.datastructures.Pair;
53 * @author Teemu Mätäsniemi
54 * @author Antti Villberg
55 * @author Tuukka Lehtonen
57 public class AnnotationUtils {
62 * @return (r1, r2) pair where r1 is the created annotation property
63 * relation and and r2 is the created annotation type
64 * @throws DatabaseException
66 public static Pair<Resource, Resource> newAnnotationType(WriteGraph graph, final Resource parent) throws DatabaseException {
67 graph.markUndoPoint();
68 Layer0 L0 = Layer0.getInstance(graph);
69 AnnotationResource ANNO = AnnotationResource.getInstance(graph);
70 SelectionViewResources SEL = SelectionViewResources.getInstance(graph);
71 StructuralResource2 STR = StructuralResource2.getInstance(graph);
73 Resource indexRoot = graph.sync(new PossibleIndexRoot(parent));
75 // Get supertype for annotation type
76 Resource propertySubrelation = graph.getPossibleObject(indexRoot, ANNO.HasAnnotationPropertySubrelation);
77 if (propertySubrelation == null)
78 propertySubrelation = L0.HasProperty;
79 Resource supertype = graph.getPossibleObject(indexRoot, ANNO.HasAnnotationTypeSupertype);
80 if (supertype == null)
81 supertype = ANNO.Annotation;
83 Resource property = graph.newResource();
84 String name = NameUtils.findFreshName(graph, "newAnnotationProperty", parent, L0.ConsistsOf);
85 graph.addLiteral(property, L0.HasName, L0.NameOf, L0.String, name, Bindings.STRING);
86 graph.claim(property, L0.SubrelationOf, propertySubrelation);
88 Resource type = graph.newResource();
89 graph.claim(type, L0.Inherits, null, supertype);
90 graph.addLiteral(type, L0.HasName, L0.NameOf, L0.String, UUID.randomUUID().toString(), Bindings.STRING);
91 graph.claim(type, STR.ComponentType_HasDefaultPropertyRelationType, SEL.GenericParameterType);
93 graph.claim(property, L0.HasRange, type);
95 graph.claim(type, L0.ConsistsOf, property);
96 graph.claim(parent, L0.ConsistsOf, type);
98 CommentMetadata cm = graph.getMetadata(CommentMetadata.class);
99 graph.addMetadata(cm.add("Added new annotationType " + type + " with property relation " + property + " "));
101 return Pair.make(property, type);
104 public static void newAnnotation(ReadGraph graph, Resource parent, Resource model) throws DatabaseException {
106 Map<Resource, Pair<String, ImageDescriptor>> map = new HashMap<>();
107 findAnnotationTypes(graph, model, map);
108 findAnnotations(graph, model, map);
109 queryUserSelectedAnnotationType(map, selected -> {
110 Simantics.getSession().async(new WriteRequest() {
112 public void perform(WriteGraph g) throws DatabaseException {
113 newAnnotation(g, parent, selected);
120 private static boolean isEntryAnnotation(ReadGraph graph, Resource selected) throws DatabaseException {
121 Layer0 L0 = Layer0.getInstance(graph);
122 AnnotationResource ANNO = AnnotationResource.getInstance(graph);
123 if(graph.isInstanceOf(selected, ANNO.Annotation)) {
124 Resource type = graph.getSingleType(selected, ANNO.Annotation);
125 return !graph.hasStatement(type, L0.HasRange_Inverse);
126 } else if (graph.isInstanceOf(selected, ANNO.AnnotationType)) {
127 return !graph.hasStatement(selected, L0.HasRange_Inverse);
129 throw new DatabaseException("Incompatible resource " + selected);
133 public static void newAnnotation(ReadGraph graph, Variable position, Resource model) throws DatabaseException {
135 Map<Resource, Pair<String, ImageDescriptor>> map = new HashMap<>();
136 findAnnotationTypes(graph, model, map);
137 findAnnotations(graph, model, map);
138 queryUserSelectedAnnotationType(map, selected -> {
139 Simantics.getSession().async(new WriteRequest() {
141 public void perform(WriteGraph g) throws DatabaseException {
143 if(isEntryAnnotation(g, selected)) {
144 newAnnotation(g, position.getRepresents(g), selected);
145 Layer0Utils.addCommentMetadata(g, "Attached new annotation to " + g.getRelatedValue2(position.getRepresents(g), Layer0.getInstance(g).HasName, Bindings.STRING));
147 newAnnotation(g, position.getParent(g).getRepresents(g), selected);
148 Layer0Utils.addCommentMetadata(g, "Attached new annotation to " + g.getRelatedValue2(position.getParent(g).getRepresents(g), Layer0.getInstance(g).HasName, Bindings.STRING));
157 * Creates a new annotation instance for the specified parent after the user
158 * selects the annotation type from the list of all annotation types in the
159 * specified model. The annotation types are resolved from the model
164 * @throws DatabaseException
166 public static void newAnnotation(Resource parent, Resource model) throws DatabaseException {
169 Simantics.getSession().syncRequest(new ReadRequest() {
171 public void run(ReadGraph graph) throws DatabaseException {
172 newAnnotation(graph, parent, model);
177 public static void newAnnotation(Resource parent) throws DatabaseException {
178 Simantics.getSession().syncRequest(new ReadRequest() {
180 public void run(ReadGraph graph) throws DatabaseException {
181 Resource model = graph.sync(new PossibleModel(parent));
182 if(model == null) return;
183 newAnnotation(graph, parent, model);
188 public static void newAnnotation(Variable position) throws DatabaseException {
189 Simantics.getSession().syncRequest(new ReadRequest() {
191 public void run(ReadGraph graph) throws DatabaseException {
192 Resource model = Variables.getModel(graph, position);
193 if(model == null) return;
194 newAnnotation(graph, position, model);
199 public static void newAnnotationInstance(Resource parent, Resource model) throws DatabaseException {
202 Simantics.getSession().syncRequest(new ReadRequest() {
204 public void run(ReadGraph graph) throws DatabaseException {
205 Map<Resource, Pair<String, ImageDescriptor>> map = new HashMap<>();
206 findAnnotationTypes(graph, model, map);
207 queryUserSelectedAnnotationType(map, selected -> {
208 Simantics.getSession().async(new WriteRequest() {
210 public void perform(WriteGraph g) throws DatabaseException {
212 newAnnotationInstance(g, parent, selected);
224 * @throws DatabaseException
226 protected static void findAnnotationTypes(ReadGraph graph, Resource model, Map<Resource, Pair<String, ImageDescriptor>> map) throws DatabaseException {
228 Layer0 L0 = Layer0.getInstance(graph);
229 AnnotationResource ANNO = AnnotationResource.getInstance(graph);
231 Instances query = graph.adapt(ANNO.AnnotationType, Instances.class);
233 String modelURI = graph.getURI(model);
235 ImageDescriptor descriptor = graph.adapt(ANNO.Images_AnnotationType, ImageDescriptor.class);
237 for(Resource _res : query.find(graph, model)) {
238 // Don't allow instantiation of abstract annotation types.
239 if (graph.hasStatement(_res, L0.Abstract))
242 // Don't allow instantiation of non-user-selectable annotation types
243 Boolean userSelectable = graph.getPossibleRelatedValue2(_res, ANNO.AnnotationType_systemAnnotation, Bindings.BOOLEAN);
244 if (Boolean.TRUE.equals(userSelectable))
247 Resource res = graph.getPossibleObject(_res, L0.HasRange_Inverse);
252 String name = graph.getPossibleRelatedValue(_res, L0.HasName, Bindings.STRING);
255 String label = graph.getPossibleRelatedValue2(_res, L0.HasLabel, Bindings.STRING);
257 if (label != null && !name.equals(label)) {
258 name = label + " (" + name + ")";
261 Resource parent = graph.getPossibleObject(_res, L0.PartOf);
262 if(parent == null) continue;
264 String parentURI = graph.getURI(parent);
265 if(parentURI.startsWith(modelURI)) {
266 parentURI = parentURI.substring(modelURI.length());
267 if(parentURI.startsWith("/")) parentURI = parentURI.substring(1);
270 name = name + " - " + URIStringUtils.unescape(parentURI);
272 map.put(_res, Pair.make(name, descriptor));
278 String name = graph.getPossibleRelatedValue(res, L0.HasName, Bindings.STRING);
281 String label = graph.getPossibleRelatedValue2(res, L0.HasLabel, Bindings.STRING);
283 if (label != null && !name.equals(label)) {
284 name = label + " (" + name + ")";
287 Resource parent = graph.getPossibleObject(_res, L0.PartOf);
288 if(parent == null) continue;
290 String parentURI = graph.getURI(parent);
291 if(parentURI.startsWith(modelURI)) {
292 parentURI = parentURI.substring(modelURI.length());
293 if(parentURI.startsWith("/")) parentURI = parentURI.substring(1);
296 name = name + " - " + URIStringUtils.unescape(parentURI);
298 map.put(_res, Pair.make(name, descriptor));
306 protected static void findAnnotations(ReadGraph graph, Resource model, Map<Resource, Pair<String, ImageDescriptor>> map) throws DatabaseException {
308 Layer0 L0 = Layer0.getInstance(graph);
309 AnnotationResource ANNO = AnnotationResource.getInstance(graph);
311 Instances query = graph.adapt(ANNO.Annotation, Instances.class);
313 String modelURI = graph.getURI(model);
315 ImageDescriptor descriptor = graph.adapt(ANNO.Images_Annotation, ImageDescriptor.class);
317 for(Resource _res : query.find(graph, model)) {
318 String name = graph.getPossibleRelatedValue(_res, L0.HasName, Bindings.STRING);
321 String label = graph.getPossibleRelatedValue2(_res, L0.HasLabel, Bindings.STRING);
322 if (label != null && !name.equals(label)) {
323 name = label + " (" + name + ")";
326 Resource parent = graph.getPossibleObject(_res, L0.PartOf);
327 if(parent == null) continue;
329 String parentURI = graph.getPossibleURI(parent);
330 if(parentURI == null) continue;
332 if(parentURI.startsWith(modelURI)) {
333 parentURI = parentURI.substring(modelURI.length());
334 if(parentURI.startsWith("/")) parentURI = parentURI.substring(1);
337 Resource type = graph.getPossibleType(_res, ANNO.Annotation);
339 // Don't list instances of non-user-selectable annotation types
340 Boolean userSelectable = graph.getPossibleRelatedValue2(type, ANNO.AnnotationType_systemAnnotation, Bindings.BOOLEAN);
341 if (Boolean.TRUE.equals(userSelectable))
344 Resource relation = graph.getPossibleObject(type, L0.HasRange_Inverse);
345 if(relation != null) {
346 String rName = graph.getPossibleRelatedValue(relation, L0.HasName, Bindings.STRING);
348 name = name + " - " + rName;
353 name = name + " - " + URIStringUtils.unescape(parentURI);
355 map.put(_res, Pair.make(name, descriptor));
361 protected static boolean isAnnotation(Variable variable) {
362 if (variable == null)
365 return Simantics.sync(new VariableRead<Boolean>(variable) {
368 public Boolean perform(ReadGraph graph) throws DatabaseException {
369 AnnotationResource ANNO = AnnotationResource.getInstance(graph);
370 Resource represents = variable.getPossibleRepresents(graph);
371 if(represents == null) return false;
372 return graph.isInstanceOf(represents, ANNO.Annotation);
376 } catch (DatabaseException e) {
381 protected static Map<Resource, Pair<String, ImageDescriptor>> findLibraries(Variable variable) {
385 return Simantics.sync(new VariableRead<Map<Resource, Pair<String, ImageDescriptor>>>(variable) {
388 public Map<Resource, Pair<String, ImageDescriptor>> perform(ReadGraph graph) throws DatabaseException {
390 Map<Resource, Pair<String, ImageDescriptor>> result = new HashMap<>();
392 Layer0 L0 = Layer0.getInstance(graph);
393 AnnotationResource ANNO = AnnotationResource.getInstance(graph);
395 Resource model = Variables.getModel(graph, variable);
396 Instances query = graph.adapt(L0.Library, Instances.class);
398 String modelURI = graph.getURI(model);
399 int modelPos = modelURI.length();
401 ImageDescriptor descriptor = graph.adapt(ANNO.Images_Annotation, ImageDescriptor.class);
403 for(Resource lib : query.find(graph, model)) {
405 String path = graph.getURI(lib);
406 if(!path.startsWith(modelURI)) continue;
407 String suffix = URIStringUtils.unescape(path.substring(modelPos));
408 if(suffix.startsWith("/")) suffix = suffix.substring(1);
409 result.put(lib, Pair.make(suffix, descriptor));
418 } catch (DatabaseException e) {
419 Logger.defaultLogError(e);
426 public static Resource newAnnotation(WriteGraph graph, Resource container, Resource valueOrProperty) throws DatabaseException {
427 graph.markUndoPoint();
428 Layer0 L0 = Layer0.getInstance(graph);
429 AnnotationResource ANNO = AnnotationResource.getInstance(graph);
431 if(graph.isInstanceOf(valueOrProperty, ANNO.Annotation)) {
433 Resource type = graph.getPossibleType(valueOrProperty, ANNO.Annotation);
434 if(type == null) return null;
435 Resource property = graph.getPossibleObject(type, L0.HasRange_Inverse);
436 if(property == null) {
437 graph.claim(container, ANNO.Annotation_HasEntry, valueOrProperty);
439 graph.deny(container, property);
440 graph.claim(container, property, valueOrProperty);
442 Layer0Utils.addCommentMetadata(graph, "Created new annotation value/property " + valueOrProperty + " to " + graph.getRelatedValue2(container, L0.HasName, Bindings.STRING));
443 return valueOrProperty;
445 } else if (graph.isInstanceOf(valueOrProperty, ANNO.AnnotationType)) {
447 Resource predicate = graph.getPossibleObject(valueOrProperty, L0.HasRange_Inverse);
448 if(predicate != null) {
449 Resource value = graph.newResource();
450 graph.claim(value, L0.InstanceOf, valueOrProperty);
451 graph.deny(container, predicate);
452 graph.claim(container, predicate, value);
453 Layer0Utils.addCommentMetadata(graph, "Created new annotation type " + value + " to " + graph.getRelatedValue2(container, L0.HasName, Bindings.STRING));
456 Resource value = graph.newResource();
457 graph.claim(value, L0.InstanceOf, valueOrProperty);
458 String name = NameUtils.findFreshEscapedName(graph, "Value", container, ANNO.Annotation_HasEntry);
459 graph.addLiteral(value, L0.HasName, L0.NameOf, L0.String, name, Bindings.STRING);
460 graph.claim(container, ANNO.Annotation_HasEntry, value);
461 Layer0Utils.addCommentMetadata(graph, "Created new annotation entry " + value + " to " + graph.getRelatedValue2(container, L0.HasName, Bindings.STRING));
467 Resource valueType = graph.getSingleObject(valueOrProperty, L0.HasRange);
468 Resource value = graph.newResource();
469 graph.claim(value, L0.InstanceOf, valueType);
470 graph.deny(container, valueOrProperty);
471 graph.claim(container, valueOrProperty, value);
478 public static Resource newAnnotationInstance(WriteGraph graph, Resource container, Resource annotationProperty) throws DatabaseException {
479 return newAnnotationInstance(graph, container, null, annotationProperty);
482 public static Resource newAnnotationInstance(WriteGraph graph, Resource container, String name, Resource annotationProperty) throws DatabaseException {
483 return newAnnotationInstance(graph, container, name, annotationProperty, true);
486 public static Resource newAnnotationInstance(WriteGraph graph, Resource container, String name, Resource annotationProperty, boolean addCommentMetadata) throws DatabaseException {
487 //graph.markUndoPoint();
488 Layer0 L0 = Layer0.getInstance(graph);
489 AnnotationResource ANNO = AnnotationResource.getInstance(graph);
491 if(graph.isInstanceOf(annotationProperty, ANNO.AnnotationType)) {
493 Resource predicate = graph.getPossibleObject(annotationProperty, L0.HasRange_Inverse);
495 String proposition = predicate != null ? (String)graph.getRelatedValue(predicate, L0.HasName, Bindings.STRING) :
496 (String)graph.getRelatedValue(annotationProperty, L0.HasName, Bindings.STRING);
498 Resource value = graph.newResource();
499 graph.claim(value, L0.InstanceOf, annotationProperty);
501 name = NameUtils.findFreshName(graph, proposition + " value", container);
502 graph.addLiteral(value, L0.HasName, L0.NameOf, L0.String, name, Bindings.STRING);
504 graph.claim(container, L0.ConsistsOf, value);
506 if (addCommentMetadata) {
507 CommentMetadata cm = graph.getMetadata(CommentMetadata.class);
508 graph.addMetadata(cm.add("Added new annotationValue named " + name + ", resource " + value));
515 String propertyName = graph.getRelatedValue(annotationProperty, L0.HasName, Bindings.STRING);
517 Resource valueType = graph.getSingleObject(annotationProperty, L0.HasRange);
518 Resource value = graph.newResource();
519 graph.claim(value, L0.InstanceOf, valueType);
521 name = NameUtils.findFreshName(graph, propertyName + " value", container);
522 graph.addLiteral(value, L0.HasName, L0.NameOf, L0.String, name, Bindings.STRING);
524 graph.claim(container, L0.ConsistsOf, value);
526 if (addCommentMetadata) {
527 CommentMetadata cm = graph.getMetadata(CommentMetadata.class);
528 graph.addMetadata(cm.add("Added new annotationValue named " + name + ", resource " + value));
539 * @param selectionCallback
541 public static void queryUserSelectedAnnotationType(
542 Map<Resource, Pair<String, ImageDescriptor>> map,
543 Consumer<Resource> selectionCallback)
545 Display.getDefault().asyncExec(() -> {
546 Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
547 ResourceSelectionDialog3<Resource> dialog = new ResourceSelectionDialog3<Resource>(shell, map, "Select annotation type from list") {
549 protected IDialogSettings getBaseDialogSettings() {
550 return Activator.getDefault().getDialogSettings();
553 if (dialog.open() == Window.OK) {
554 Object[] result = dialog.getResult();
555 if (result != null && result.length == 1) {
556 selectionCallback.accept((Resource) result[0]);
562 public static void queryLibrary(
563 Map<Resource, Pair<String, ImageDescriptor>> map,
564 Consumer<Pair<Resource,String>> selectionCallback)
566 Display.getDefault().asyncExec(() -> {
567 Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
568 SaveAnnotationDialog page = new SaveAnnotationDialog(shell, map, "Select library");
569 if (page.open() == Window.OK) {
570 Object[] result = page.getResult();
571 if (result != null && result.length == 1) {
572 selectionCallback.accept(Pair.make((Resource)result[0], page.getName()));