--- /dev/null
+/*******************************************************************************
+ * 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.modeling.utils;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+import org.simantics.databoard.Bindings;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.common.NamedResource;
+import org.simantics.db.common.request.PossibleIndexRoot;
+import org.simantics.db.common.request.PossibleObjectWithType;
+import org.simantics.db.common.utils.NameUtils;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.exception.VariableException;
+import org.simantics.db.layer0.QueryIndexUtils;
+import org.simantics.db.layer0.request.PossibleModel;
+import org.simantics.db.layer0.variable.RVI;
+import org.simantics.db.layer0.variable.Variable;
+import org.simantics.db.layer0.variable.Variables;
+import org.simantics.diagram.content.ConnectionUtil;
+import org.simantics.diagram.stubs.DiagramResource;
+import org.simantics.layer0.Layer0;
+import org.simantics.modeling.ModelingResources;
+import org.simantics.structural.stubs.StructuralResource2;
+import org.simantics.utils.ui.ErrorLogger;
+
+import gnu.trove.set.hash.THashSet;
+
+/**
+ * @author Tuukka Lehtonen
+ */
+final public class RemoverUtils {
+
+ public static ValidationResult validateFlagRemoval(ReadGraph graph, Resource flag) throws DatabaseException {
+ DiagramResource DIA = DiagramResource.getInstance(graph);
+
+ for (Resource connectionRelation : graph.getObjects(flag, DIA.IsLiftedAs)) {
+ ValidationResult result = validateConnectionRelationRemoval(graph, connectionRelation, null);
+ if (!result.inUse())
+ continue;
+ return result;
+ }
+ return new ValidationResult();
+ }
+
+ public static ValidationResult validateConnectionRelationRemoval(ReadGraph graph, Resource connectionRelation, Resource diagramConnectionRelation) throws DatabaseException {
+ Layer0 L0 = Layer0.getInstance(graph);
+ StructuralResource2 STR = StructuralResource2.getInstance(graph);
+
+ ValidationResult result = new ValidationResult();
+
+ // This is a connection point of a structural component type configuration.
+ String connectionRelationName = NameUtils.getSafeLabel(graph, connectionRelation);
+ result.connectionRelation = new NamedResource(connectionRelationName, connectionRelation);
+ if (diagramConnectionRelation != null) {
+ String diagramConnectionRelationName = NameUtils.getSafeLabel(graph, diagramConnectionRelation);
+ result.diagramConnectionRelation = new NamedResource(diagramConnectionRelationName, diagramConnectionRelation);
+ }
+
+ // 1. Get component type
+ Resource componentType = graph.sync( new PossibleObjectWithType(connectionRelation, L0.PartOf, STR.ComponentType) );
+ if (componentType == null)
+ return result;
+ String componentTypeName = graph.getPossibleRelatedValue(componentType, L0.HasName, Bindings.STRING);
+ if (componentTypeName == null)
+ return result;
+ result.componentType = new NamedResource(componentTypeName, componentType);
+
+ // 2. Find index roots that may contain references to this component type
+ Set<Resource> indexRoots = new THashSet<Resource>();
+ Resource indexRoot = graph.sync( new PossibleIndexRoot(componentType) );
+ if (indexRoot == null)
+ return result;
+ String indexRootName = graph.getPossibleRelatedValue(indexRoot, L0.HasName, Bindings.STRING);
+ if (indexRootName == null)
+ return result;
+ result.containingIndexRoot = new NamedResource(indexRootName, indexRoot);
+
+ Resource model = graph.sync( new PossibleModel(indexRoot) );
+ if (model == null) {
+ // Need to look for references in all models that use this shared library.
+ indexRoots.addAll( graph.getObjects(indexRoot, L0.IsLinkedTo_Inverse) );
+ } else {
+ // The component type is in a model, other models can't reference it.
+ indexRoots.add(model);
+ }
+
+ // 3. Check whether this connection point is in use in any
+ // instances of this component type within the containing model.
+ for (Resource root : indexRoots) {
+ validateConnectionRelationRemovalForIndexRoot(graph, root, connectionRelation, diagramConnectionRelation, result);
+ }
+
+ return result;
+ }
+
+ private static void validateConnectionRelationRemovalForIndexRoot(ReadGraph graph, Resource root, Resource connectionRelation, Resource diagramConnectionRelation, ValidationResult result) throws DatabaseException {
+ String rootURI = graph.getPossibleURI(root);
+ if (rootURI == null)
+ return;
+
+ Layer0 L0 = Layer0.getInstance(graph);
+ String rootName = graph.getPossibleRelatedValue(root, L0.HasName, Bindings.STRING);
+ if (rootName == null)
+ return;
+ NamedResource namedRoot = new NamedResource(rootName, root);
+
+ List<Resource> components = QueryIndexUtils.searchByTypeShallow(graph, root, result.componentType.getResource());
+ if (components.isEmpty())
+ return;
+
+ ModelingResources MOD = ModelingResources.getInstance(graph);
+ StructuralResource2 STR = StructuralResource2.getInstance(graph);
+
+ for (Resource component : components) {
+ String componentName = graph.getPossibleRelatedValue(component, L0.HasName, Bindings.STRING);
+ next_connection:
+ for (Resource connection : graph.getObjects(component, connectionRelation)) {
+ if (graph.isInstanceOf(connection, STR.Connection)) {
+
+ if (diagramConnectionRelation != null) {
+ // When diagram connection relation is defined, validate that
+ // exactly the specified diagram connection relation is being
+ // used and not some other relation from another symbol.
+ for (Resource element : graph.getObjects(component, MOD.ComponentToElement)) {
+ for (Resource diagramConnector : graph.getObjects(element, diagramConnectionRelation)) {
+ Resource diagramConnection = ConnectionUtil.tryGetConnection(graph, diagramConnector);
+ if (diagramConnection == null)
+ continue;
+ Resource correspondingConnection = graph.getPossibleObject(diagramConnection, MOD.DiagramConnectionToConnection);
+ Resource correspondingConnectionSpecial = graph.getPossibleObject(diagramConnection, MOD.DiagramConnectionToConnectionSpecial);
+ if (connection.equals(correspondingConnection) || connection.equals(correspondingConnectionSpecial)) {
+ addUse(graph, component, componentName, namedRoot, rootURI, result);
+ continue next_connection;
+ }
+ }
+ }
+ } else {
+ addUse(graph, component, componentName, namedRoot, rootURI, result);
+ }
+ }
+ }
+ }
+ }
+
+ private static boolean addUse(ReadGraph graph, Resource component, String componentName, NamedResource namedRoot, String rootURI, ValidationResult result) throws DatabaseException {
+ String uri = null;
+ try {
+ Variable variable = Variables.getVariable(graph, component);
+ if (!result.usedVariables.add(variable))
+ return false;
+
+ Use use = new Use();
+ use.resource = component;
+ use.name = componentName;
+ use.variable = variable;
+ use.root = namedRoot;
+ uri = use.rootUri = use.variable.getURI(graph).replace(rootURI, "");
+ use.context = Variables.getPossibleContext(graph, use.variable);
+ if (use.context != null) {
+ use.path = Variables.getPossibleRVI2(graph, variable);
+ }
+ result.uses.add( use );
+ return true;
+ } catch (VariableException e) {
+ // Might happen with corrupt data, don't panic, just ignore.
+ ErrorLogger.defaultLogWarning("Connection relation removal validation ignored invalid connection to component " + uri, e);
+ return false;
+ }
+ }
+
+ public static String formatError(ReadGraph graph, ValidationResult result) throws DatabaseException {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Cannot remove connection point '" + result.connectionRelation.getName() + "'\nfrom user component '" + result.componentType.getName() + "'.")
+ .append("\nIt is used in " + result.uses.size() + " instance(s):\n\n");
+
+ // See whether the uses contain references from other libraries/models
+ // than the containing index root.
+ boolean absoluteUris = false;
+ for (Use use : result.uses) {
+ if (!use.root.getResource().equals(result.containingIndexRoot.getResource())) {
+ absoluteUris = true;
+ break;
+ }
+ }
+
+ for (Use use : result.uses) {
+ if (use.context != null) {
+ if (absoluteUris) {
+ sb.append("/").append(use.root.getName()).append(use.rootUri);
+ } else {
+ sb.append(use.path.toPossibleString(graph, use.context));
+ }
+ } else {
+ if (absoluteUris)
+ sb.append("/").append(use.root.getName());
+ sb.append(use.rootUri);
+ }
+ sb.append("\n");
+ }
+ return sb.toString();
+ }
+
+ public static class Use {
+ public String name;
+ public Resource resource;
+ public Variable variable;
+ public Variable context;
+ public RVI path;
+ public NamedResource root;
+ public String rootUri;
+ }
+
+ public static class ValidationResult {
+ public NamedResource containingIndexRoot;
+ public NamedResource componentType;
+ public NamedResource connectionRelation;
+ public NamedResource diagramConnectionRelation;
+ public Set<Variable> usedVariables = new THashSet<Variable>();
+ public Collection<Use> uses = new ArrayList<Use>();
+
+ public boolean inUse() {
+ return !uses.isEmpty();
+ }
+ }
+
+}