/******************************************************************************* * Copyright (c) 2012 Association for Decentralized Information Management in * Industry THTH ry. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.annotation.ui; import java.util.HashMap; import java.util.Map; import java.util.UUID; import java.util.function.Consumer; import org.eclipse.jface.dialogs.IDialogSettings; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.window.Window; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.PlatformUI; import org.simantics.Simantics; import org.simantics.annotation.ontology.AnnotationResource; import org.simantics.annotation.ui.internal.SaveAnnotationDialog; import org.simantics.databoard.Bindings; import org.simantics.databoard.util.URIStringUtils; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.WriteGraph; import org.simantics.db.common.CommentMetadata; import org.simantics.db.common.request.PossibleIndexRoot; import org.simantics.db.common.request.ReadRequest; import org.simantics.db.common.request.WriteRequest; import org.simantics.db.common.utils.Logger; import org.simantics.db.common.utils.NameUtils; import org.simantics.db.exception.DatabaseException; import org.simantics.db.layer0.adapter.Instances; import org.simantics.db.layer0.request.PossibleModel; import org.simantics.db.layer0.request.VariableRead; import org.simantics.db.layer0.util.Layer0Utils; import org.simantics.db.layer0.variable.Variable; import org.simantics.db.layer0.variable.Variables; import org.simantics.layer0.Layer0; import org.simantics.selectionview.SelectionViewResources; import org.simantics.structural.stubs.StructuralResource2; import org.simantics.ui.workbench.dialogs.ResourceSelectionDialog3; import org.simantics.utils.datastructures.Pair; /** * @author Teemu Mätäsniemi * @author Antti Villberg * @author Tuukka Lehtonen */ public class AnnotationUtils { /** * @param graph * @param parent * @return (r1, r2) pair where r1 is the created annotation property * relation and and r2 is the created annotation type * @throws DatabaseException */ public static Pair newAnnotationType(WriteGraph graph, final Resource parent) throws DatabaseException { graph.markUndoPoint(); Layer0 L0 = Layer0.getInstance(graph); AnnotationResource ANNO = AnnotationResource.getInstance(graph); SelectionViewResources SEL = SelectionViewResources.getInstance(graph); StructuralResource2 STR = StructuralResource2.getInstance(graph); Resource indexRoot = graph.sync(new PossibleIndexRoot(parent)); // Get supertype for annotation type Resource propertySubrelation = graph.getPossibleObject(indexRoot, ANNO.HasAnnotationPropertySubrelation); if (propertySubrelation == null) propertySubrelation = L0.HasProperty; Resource supertype = graph.getPossibleObject(indexRoot, ANNO.HasAnnotationTypeSupertype); if (supertype == null) supertype = ANNO.Annotation; Resource property = graph.newResource(); String name = NameUtils.findFreshName(graph, "newAnnotationProperty", parent, L0.ConsistsOf); graph.addLiteral(property, L0.HasName, L0.NameOf, L0.String, name, Bindings.STRING); graph.claim(property, L0.SubrelationOf, propertySubrelation); Resource type = graph.newResource(); graph.claim(type, L0.Inherits, null, supertype); graph.addLiteral(type, L0.HasName, L0.NameOf, L0.String, UUID.randomUUID().toString(), Bindings.STRING); graph.claim(type, STR.ComponentType_HasDefaultPropertyRelationType, SEL.GenericParameterType); graph.claim(property, L0.HasRange, type); graph.claim(type, L0.ConsistsOf, property); graph.claim(parent, L0.ConsistsOf, type); CommentMetadata cm = graph.getMetadata(CommentMetadata.class); graph.addMetadata(cm.add("Added new annotationType " + type + " with property relation " + property + " ")); return Pair.make(property, type); } public static void newAnnotation(ReadGraph graph, final Resource parent, Resource model) throws DatabaseException { final Map> map = new HashMap>(); findAnnotationTypes(graph, model, map); findAnnotations(graph, model, map); queryUserSelectedAnnotationType(map, selected -> { Simantics.getSession().async(new WriteRequest() { @Override public void perform(WriteGraph g) throws DatabaseException { newAnnotation(g, parent, selected); } }); }); } private static boolean isEntryAnnotation(ReadGraph graph, Resource selected) throws DatabaseException { Layer0 L0 = Layer0.getInstance(graph); AnnotationResource ANNO = AnnotationResource.getInstance(graph); if(graph.isInstanceOf(selected, ANNO.Annotation)) { Resource type = graph.getSingleType(selected, ANNO.Annotation); return !graph.hasStatement(type, L0.HasRange_Inverse); } else if (graph.isInstanceOf(selected, ANNO.AnnotationType)) { return !graph.hasStatement(selected, L0.HasRange_Inverse); } else { throw new DatabaseException("Incompatible resource " + selected); } } public static void newAnnotation(ReadGraph graph, final Variable position, Resource model) throws DatabaseException { final Map> map = new HashMap>(); findAnnotationTypes(graph, model, map); findAnnotations(graph, model, map); queryUserSelectedAnnotationType(map, selected -> { Simantics.getSession().async(new WriteRequest() { @Override public void perform(WriteGraph g) throws DatabaseException { g.markUndoPoint(); if(isEntryAnnotation(g, selected)) { newAnnotation(g, position.getRepresents(g), selected); Layer0Utils.addCommentMetadata(g, "Attached new annotation to " + g.getRelatedValue2(position.getRepresents(g), Layer0.getInstance(g).HasName, Bindings.STRING)); } else { newAnnotation(g, position.getParent(g).getRepresents(g), selected); Layer0Utils.addCommentMetadata(g, "Attached new annotation to " + g.getRelatedValue2(position.getParent(g).getRepresents(g), Layer0.getInstance(g).HasName, Bindings.STRING)); } } }); }); } /** * Creates a new annotation instance for the specified parent after the user * selects the annotation type from the list of all annotation types in the * specified model. The annotation types are resolved from the model * dependency index. * * @param parent * @param model * @throws DatabaseException */ public static void newAnnotation(final Resource parent, final Resource model) throws DatabaseException { if (model == null) return; Simantics.getSession().syncRequest(new ReadRequest() { @Override public void run(ReadGraph graph) throws DatabaseException { newAnnotation(graph, parent, model); } }); } public static void newAnnotation(final Resource parent) throws DatabaseException { Simantics.getSession().syncRequest(new ReadRequest() { @Override public void run(ReadGraph graph) throws DatabaseException { Resource model = graph.sync(new PossibleModel(parent)); if(model == null) return; newAnnotation(graph, parent, model); } }); } public static void newAnnotation(final Variable position) throws DatabaseException { Simantics.getSession().syncRequest(new ReadRequest() { @Override public void run(ReadGraph graph) throws DatabaseException { Resource model = Variables.getModel(graph, position); if(model == null) return; newAnnotation(graph, position, model); } }); } public static void newAnnotationInstance(final Resource parent, final Resource model) throws DatabaseException { if (model == null) return; Simantics.getSession().syncRequest(new ReadRequest() { @Override public void run(ReadGraph graph) throws DatabaseException { final Map> map = new HashMap>(); findAnnotationTypes(graph, model, map); queryUserSelectedAnnotationType(map, selected -> { Simantics.getSession().async(new WriteRequest() { @Override public void perform(WriteGraph g) throws DatabaseException { g.markUndoPoint(); newAnnotationInstance(g, parent, selected); } }); }); } }); } /** * @param graph * @param model * @return * @throws DatabaseException */ protected static void findAnnotationTypes(ReadGraph graph, Resource model, Map> map) throws DatabaseException { Layer0 L0 = Layer0.getInstance(graph); AnnotationResource ANNO = AnnotationResource.getInstance(graph); Instances query = graph.adapt(ANNO.AnnotationType, Instances.class); String modelURI = graph.getURI(model); ImageDescriptor descriptor = graph.adapt(ANNO.Images_AnnotationType, ImageDescriptor.class); for(Resource _res : query.find(graph, model)) { // Don't allow instantiation of abstract annotation types. if (graph.hasStatement(_res, L0.Abstract)) continue; Resource res = graph.getPossibleObject(_res, L0.HasRange_Inverse); if(res == null) { // Entry type String name = graph.getPossibleRelatedValue(_res, L0.HasName, Bindings.STRING); if (name == null) continue; String label = graph.getPossibleRelatedValue2(_res, L0.HasLabel, Bindings.STRING); if (label != null && !name.equals(label)) { name = label + " (" + name + ")"; } Resource parent = graph.getPossibleObject(_res, L0.PartOf); if(parent == null) continue; String parentURI = graph.getURI(parent); if(parentURI.startsWith(modelURI)) { parentURI = parentURI.substring(modelURI.length()); if(parentURI.startsWith("/")) parentURI = parentURI.substring(1); } name = name + " - " + URIStringUtils.unescape(parentURI); map.put(_res, new Pair(name, descriptor)); } else { // Property type String name = graph.getPossibleRelatedValue(res, L0.HasName, Bindings.STRING); if (name == null) continue; String label = graph.getPossibleRelatedValue2(res, L0.HasLabel, Bindings.STRING); if (label != null && !name.equals(label)) { name = label + " (" + name + ")"; } Resource parent = graph.getPossibleObject(_res, L0.PartOf); if(parent == null) continue; String parentURI = graph.getURI(parent); if(parentURI.startsWith(modelURI)) { parentURI = parentURI.substring(modelURI.length()); if(parentURI.startsWith("/")) parentURI = parentURI.substring(1); } name = name + " - " + URIStringUtils.unescape(parentURI); map.put(_res, new Pair(name, descriptor)); } } } protected static void findAnnotations(ReadGraph graph, Resource model, Map> map) throws DatabaseException { Layer0 L0 = Layer0.getInstance(graph); AnnotationResource ANNO = AnnotationResource.getInstance(graph); Instances query = graph.adapt(ANNO.Annotation, Instances.class); String modelURI = graph.getURI(model); ImageDescriptor descriptor = graph.adapt(ANNO.Images_Annotation, ImageDescriptor.class); for(Resource _res : query.find(graph, model)) { // Don't allow instantiation of abstract annotation types. String name = graph.getPossibleRelatedValue(_res, L0.HasName, Bindings.STRING); if (name == null) continue; String label = graph.getPossibleRelatedValue2(_res, L0.HasLabel, Bindings.STRING); if (label != null && !name.equals(label)) { name = label + " (" + name + ")"; } Resource parent = graph.getPossibleObject(_res, L0.PartOf); if(parent == null) continue; String parentURI = graph.getPossibleURI(parent); if(parentURI == null) continue; if(parentURI.startsWith(modelURI)) { parentURI = parentURI.substring(modelURI.length()); if(parentURI.startsWith("/")) parentURI = parentURI.substring(1); } Resource type = graph.getPossibleType(_res, ANNO.Annotation); if(type != null) { Resource relation = graph.getPossibleObject(type, L0.HasRange_Inverse); if(relation != null) { String rName = graph.getPossibleRelatedValue(relation, L0.HasName, Bindings.STRING); if(rName != null) { name = name + " - " + rName; } } } name = name + " - " + URIStringUtils.unescape(parentURI); map.put(_res, new Pair(name, descriptor)); } } protected static boolean isAnnotation(Variable variable) { if (variable == null) return false; try { return Simantics.sync(new VariableRead(variable) { @Override public Boolean perform(ReadGraph graph) throws DatabaseException { AnnotationResource ANNO = AnnotationResource.getInstance(graph); Resource represents = variable.getPossibleRepresents(graph); if(represents == null) return false; return graph.isInstanceOf(represents, ANNO.Annotation); } }); } catch (DatabaseException e) { return false; } } protected static Map> findLibraries(Variable variable) { try { return Simantics.sync(new VariableRead>>(variable) { @Override public Map> perform(ReadGraph graph) throws DatabaseException { Map> result = new HashMap>(); Layer0 L0 = Layer0.getInstance(graph); AnnotationResource ANNO = AnnotationResource.getInstance(graph); Resource model = Variables.getModel(graph, variable); Instances query = graph.adapt(L0.Library, Instances.class); String modelURI = graph.getURI(model); int modelPos = modelURI.length(); ImageDescriptor descriptor = graph.adapt(ANNO.Images_Annotation, ImageDescriptor.class); for(Resource lib : query.find(graph, model)) { String path = graph.getURI(lib); if(!path.startsWith(modelURI)) continue; String suffix = URIStringUtils.unescape(path.substring(modelPos)); if(suffix.startsWith("/")) suffix = suffix.substring(1); result.put(lib, new Pair(suffix, descriptor)); } return result; } }); } catch (DatabaseException e) { Logger.defaultLogError(e); } return null; } public static Resource newAnnotation(WriteGraph graph, Resource container, Resource valueOrProperty) throws DatabaseException { graph.markUndoPoint(); Layer0 L0 = Layer0.getInstance(graph); AnnotationResource ANNO = AnnotationResource.getInstance(graph); if(graph.isInstanceOf(valueOrProperty, ANNO.Annotation)) { Resource type = graph.getPossibleType(valueOrProperty, ANNO.Annotation); if(type == null) return null; Resource property = graph.getPossibleObject(type, L0.HasRange_Inverse); if(property == null) { graph.claim(container, ANNO.Annotation_HasEntry, valueOrProperty); } else { graph.deny(container, property); graph.claim(container, property, valueOrProperty); } Layer0Utils.addCommentMetadata(graph, "Created new annotation value/property " + valueOrProperty + " to " + graph.getRelatedValue2(container, L0.HasName, Bindings.STRING)); return valueOrProperty; } else if (graph.isInstanceOf(valueOrProperty, ANNO.AnnotationType)) { Resource predicate = graph.getPossibleObject(valueOrProperty, L0.HasRange_Inverse); if(predicate != null) { Resource value = graph.newResource(); graph.claim(value, L0.InstanceOf, valueOrProperty); graph.deny(container, predicate); graph.claim(container, predicate, value); Layer0Utils.addCommentMetadata(graph, "Created new annotation type " + value + " to " + graph.getRelatedValue2(container, L0.HasName, Bindings.STRING)); return value; } else { Resource value = graph.newResource(); graph.claim(value, L0.InstanceOf, valueOrProperty); String name = NameUtils.findFreshEscapedName(graph, "Value", container, ANNO.Annotation_HasEntry); graph.addLiteral(value, L0.HasName, L0.NameOf, L0.String, name, Bindings.STRING); graph.claim(container, ANNO.Annotation_HasEntry, value); Layer0Utils.addCommentMetadata(graph, "Created new annotation entry " + value + " to " + graph.getRelatedValue2(container, L0.HasName, Bindings.STRING)); return value; } } else { Resource valueType = graph.getSingleObject(valueOrProperty, L0.HasRange); Resource value = graph.newResource(); graph.claim(value, L0.InstanceOf, valueType); graph.deny(container, valueOrProperty); graph.claim(container, valueOrProperty, value); return value; } } public static Resource newAnnotationInstance(WriteGraph graph, Resource container, Resource annotationProperty) throws DatabaseException { return newAnnotationInstance(graph, container, null, annotationProperty); } public static Resource newAnnotationInstance(WriteGraph graph, Resource container, String name, Resource annotationProperty) throws DatabaseException { return newAnnotationInstance(graph, container, name, annotationProperty, true); } public static Resource newAnnotationInstance(WriteGraph graph, Resource container, String name, Resource annotationProperty, boolean addCommentMetadata) throws DatabaseException { //graph.markUndoPoint(); Layer0 L0 = Layer0.getInstance(graph); AnnotationResource ANNO = AnnotationResource.getInstance(graph); if(graph.isInstanceOf(annotationProperty, ANNO.AnnotationType)) { Resource predicate = graph.getPossibleObject(annotationProperty, L0.HasRange_Inverse); String proposition = predicate != null ? (String)graph.getRelatedValue(predicate, L0.HasName, Bindings.STRING) : (String)graph.getRelatedValue(annotationProperty, L0.HasName, Bindings.STRING); Resource value = graph.newResource(); graph.claim(value, L0.InstanceOf, annotationProperty); if(name == null) name = NameUtils.findFreshName(graph, proposition + " value", container); graph.addLiteral(value, L0.HasName, L0.NameOf, L0.String, name, Bindings.STRING); graph.claim(container, L0.ConsistsOf, value); if (addCommentMetadata) { CommentMetadata cm = graph.getMetadata(CommentMetadata.class); graph.addMetadata(cm.add("Added new annotationValue named " + name + ", resource " + value)); } return value; } else { String propertyName = graph.getRelatedValue(annotationProperty, L0.HasName, Bindings.STRING); Resource valueType = graph.getSingleObject(annotationProperty, L0.HasRange); Resource value = graph.newResource(); graph.claim(value, L0.InstanceOf, valueType); if(name == null) name = NameUtils.findFreshName(graph, propertyName + " value", container); graph.addLiteral(value, L0.HasName, L0.NameOf, L0.String, name, Bindings.STRING); graph.claim(container, L0.ConsistsOf, value); if (addCommentMetadata) { CommentMetadata cm = graph.getMetadata(CommentMetadata.class); graph.addMetadata(cm.add("Added new annotationValue named " + name + ", resource " + value)); } return value; } } /** * @param map * @param selectionCallback */ public static void queryUserSelectedAnnotationType( final Map> map, final Consumer selectionCallback) { Display.getDefault().asyncExec(new Runnable() { @Override public void run() { Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(); ResourceSelectionDialog3 dialog = new ResourceSelectionDialog3(shell, map, "Select annotation type from list") { @Override protected IDialogSettings getBaseDialogSettings() { return Activator.getDefault().getDialogSettings(); } }; if (dialog.open() == Window.OK) { Object[] result = dialog.getResult(); if (result != null && result.length == 1) { final Resource res = (Resource)result[0]; selectionCallback.accept(res); } } } }); } public static void queryLibrary( final Map> map, final Consumer> selectionCallback) { Display.getDefault().asyncExec(new Runnable() { @Override public void run() { Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(); SaveAnnotationDialog page = new SaveAnnotationDialog(shell, map, "Select library"); if (page.open() == Window.OK) { Object[] result = page.getResult(); if (result != null && result.length == 1) { final Resource res = (Resource)result[0]; selectionCallback.accept(Pair.make(res, page.getName())); } } } }); } }