]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.modeling/src/org/simantics/modeling/utils/RemoverUtils.java
Rename and move 'modeling.adapters.Removers' and make it API
[simantics/platform.git] / bundles / org.simantics.modeling / src / org / simantics / modeling / utils / RemoverUtils.java
1 /*******************************************************************************
2  * Copyright (c) 2012 Association for Decentralized Information Management in
3  * Industry THTH ry.
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
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.modeling.utils;
13
14 import java.util.ArrayList;
15 import java.util.Collection;
16 import java.util.List;
17 import java.util.Set;
18
19 import org.simantics.databoard.Bindings;
20 import org.simantics.db.ReadGraph;
21 import org.simantics.db.Resource;
22 import org.simantics.db.common.NamedResource;
23 import org.simantics.db.common.request.PossibleIndexRoot;
24 import org.simantics.db.common.request.PossibleObjectWithType;
25 import org.simantics.db.common.utils.NameUtils;
26 import org.simantics.db.exception.DatabaseException;
27 import org.simantics.db.exception.VariableException;
28 import org.simantics.db.layer0.QueryIndexUtils;
29 import org.simantics.db.layer0.request.PossibleModel;
30 import org.simantics.db.layer0.variable.RVI;
31 import org.simantics.db.layer0.variable.Variable;
32 import org.simantics.db.layer0.variable.Variables;
33 import org.simantics.diagram.content.ConnectionUtil;
34 import org.simantics.diagram.stubs.DiagramResource;
35 import org.simantics.layer0.Layer0;
36 import org.simantics.modeling.ModelingResources;
37 import org.simantics.structural.stubs.StructuralResource2;
38 import org.simantics.utils.ui.ErrorLogger;
39
40 import gnu.trove.set.hash.THashSet;
41
42 /**
43  * @author Tuukka Lehtonen
44  */
45 final public class RemoverUtils {
46
47     public static ValidationResult validateFlagRemoval(ReadGraph graph, Resource flag) throws DatabaseException {
48         DiagramResource DIA = DiagramResource.getInstance(graph);
49
50         for (Resource connectionRelation : graph.getObjects(flag, DIA.IsLiftedAs)) {
51             ValidationResult result = validateConnectionRelationRemoval(graph, connectionRelation, null);
52             if (!result.inUse())
53                 continue;
54             return result;
55         }
56         return new ValidationResult();
57     }
58
59     public static ValidationResult validateConnectionRelationRemoval(ReadGraph graph, Resource connectionRelation, Resource diagramConnectionRelation) throws DatabaseException {
60         Layer0 L0 = Layer0.getInstance(graph);
61         StructuralResource2 STR = StructuralResource2.getInstance(graph);
62
63         ValidationResult result = new ValidationResult();
64
65         // This is a connection point of a structural component type configuration.
66         String connectionRelationName = NameUtils.getSafeLabel(graph, connectionRelation);
67         result.connectionRelation = new NamedResource(connectionRelationName, connectionRelation);
68         if (diagramConnectionRelation != null) {
69             String diagramConnectionRelationName = NameUtils.getSafeLabel(graph, diagramConnectionRelation);
70             result.diagramConnectionRelation = new NamedResource(diagramConnectionRelationName, diagramConnectionRelation);
71         }
72
73         // 1. Get component type
74         Resource componentType = graph.sync( new PossibleObjectWithType(connectionRelation, L0.PartOf, STR.ComponentType) );
75         if (componentType == null)
76             return result;
77         String componentTypeName = graph.getPossibleRelatedValue(componentType, L0.HasName, Bindings.STRING);
78         if (componentTypeName == null)
79             return result;
80         result.componentType = new NamedResource(componentTypeName, componentType);
81
82         // 2. Find index roots that may contain references to this component type
83         Set<Resource> indexRoots = new THashSet<Resource>();
84         Resource indexRoot = graph.sync( new PossibleIndexRoot(componentType) );
85         if (indexRoot == null)
86             return result;
87         String indexRootName = graph.getPossibleRelatedValue(indexRoot, L0.HasName, Bindings.STRING);
88         if (indexRootName == null)
89             return result;
90         result.containingIndexRoot = new NamedResource(indexRootName, indexRoot);
91
92         Resource model = graph.sync( new PossibleModel(indexRoot) );
93         if (model == null) {
94             // Need to look for references in all models that use this shared library.
95             indexRoots.addAll( graph.getObjects(indexRoot, L0.IsLinkedTo_Inverse) );
96         } else {
97             // The component type is in a model, other models can't reference it.
98             indexRoots.add(model);
99         }
100
101         // 3. Check whether this connection point is in use in any
102         // instances of this component type within the containing model.
103         for (Resource root : indexRoots) {
104             validateConnectionRelationRemovalForIndexRoot(graph, root, connectionRelation, diagramConnectionRelation, result);
105         }
106
107         return result;
108     }
109
110     private static void validateConnectionRelationRemovalForIndexRoot(ReadGraph graph, Resource root, Resource connectionRelation, Resource diagramConnectionRelation, ValidationResult result) throws DatabaseException {
111         String rootURI = graph.getPossibleURI(root);
112         if (rootURI == null)
113             return;
114
115         Layer0 L0 = Layer0.getInstance(graph);
116         String rootName = graph.getPossibleRelatedValue(root, L0.HasName, Bindings.STRING);
117         if (rootName == null)
118             return;
119         NamedResource namedRoot = new NamedResource(rootName, root);
120
121         List<Resource> components = QueryIndexUtils.searchByTypeShallow(graph, root, result.componentType.getResource());
122         if (components.isEmpty())
123             return;
124
125         ModelingResources MOD = ModelingResources.getInstance(graph);
126         StructuralResource2 STR = StructuralResource2.getInstance(graph);
127
128         for (Resource component : components) {
129             String componentName = graph.getPossibleRelatedValue(component, L0.HasName, Bindings.STRING);
130             next_connection:
131                 for (Resource connection : graph.getObjects(component, connectionRelation)) {
132                     if (graph.isInstanceOf(connection, STR.Connection)) {
133
134                         if (diagramConnectionRelation != null) {
135                             // When diagram connection relation is defined, validate that
136                             // exactly the specified diagram connection relation is being
137                             // used and not some other relation from another symbol.
138                             for (Resource element : graph.getObjects(component, MOD.ComponentToElement)) {
139                                 for (Resource diagramConnector : graph.getObjects(element, diagramConnectionRelation)) {
140                                     Resource diagramConnection = ConnectionUtil.tryGetConnection(graph, diagramConnector);
141                                     if (diagramConnection == null)
142                                         continue;
143                                     Resource correspondingConnection = graph.getPossibleObject(diagramConnection, MOD.DiagramConnectionToConnection);
144                                     Resource correspondingConnectionSpecial = graph.getPossibleObject(diagramConnection, MOD.DiagramConnectionToConnectionSpecial);
145                                     if (connection.equals(correspondingConnection) || connection.equals(correspondingConnectionSpecial)) {
146                                         addUse(graph, component, componentName, namedRoot, rootURI, result);
147                                         continue next_connection;
148                                     }
149                                 }
150                             }
151                         } else {
152                             addUse(graph, component, componentName, namedRoot, rootURI, result);
153                         }
154                     }
155                 }
156         }
157     }
158
159     private static boolean addUse(ReadGraph graph, Resource component, String componentName, NamedResource namedRoot, String rootURI, ValidationResult result) throws DatabaseException {
160         String uri = null;
161         try {
162             Variable variable = Variables.getVariable(graph, component);
163             if (!result.usedVariables.add(variable))
164                 return false;
165
166             Use use = new Use();
167             use.resource = component;
168             use.name = componentName;
169             use.variable = variable;
170             use.root = namedRoot;
171             uri = use.rootUri = use.variable.getURI(graph).replace(rootURI, "");
172             use.context = Variables.getPossibleContext(graph, use.variable);
173             if (use.context != null) {
174                 use.path = Variables.getPossibleRVI2(graph, variable);
175             }
176             result.uses.add( use );
177             return true;
178         } catch (VariableException e) {
179             // Might happen with corrupt data, don't panic, just ignore.
180             ErrorLogger.defaultLogWarning("Connection relation removal validation ignored invalid connection to component " + uri, e);
181             return false;
182         }
183     }
184
185     public static String formatError(ReadGraph graph, ValidationResult result) throws DatabaseException {
186         StringBuilder sb = new StringBuilder();
187         sb.append("Cannot remove connection point '" + result.connectionRelation.getName() + "'\nfrom user component '" + result.componentType.getName() + "'.")
188         .append("\nIt is used in " + result.uses.size() + " instance(s):\n\n");
189
190         // See whether the uses contain references from other libraries/models
191         // than the containing index root.
192         boolean absoluteUris = false;
193         for (Use use : result.uses) {
194             if (!use.root.getResource().equals(result.containingIndexRoot.getResource())) {
195                 absoluteUris = true;
196                 break;
197             }
198         }
199
200         for (Use use : result.uses) {
201             if (use.context != null) {
202                 if (absoluteUris) {
203                     sb.append("/").append(use.root.getName()).append(use.rootUri);
204                 } else {
205                     sb.append(use.path.toPossibleString(graph, use.context));
206                 }
207             } else {
208                 if (absoluteUris)
209                     sb.append("/").append(use.root.getName());
210                 sb.append(use.rootUri);
211             }
212             sb.append("\n");
213         }
214         return sb.toString();
215     }
216
217     public static class Use {
218         public String name;
219         public Resource resource;
220         public Variable variable;
221         public Variable context;
222         public RVI path;
223         public NamedResource root;
224         public String rootUri;
225     }
226
227     public static class ValidationResult {
228         public NamedResource containingIndexRoot;
229         public NamedResource componentType;
230         public NamedResource connectionRelation;
231         public NamedResource diagramConnectionRelation;
232         public Set<Variable> usedVariables = new THashSet<Variable>();
233         public Collection<Use> uses = new ArrayList<Use>();
234
235         public boolean inUse() {
236             return !uses.isEmpty();
237         }
238     }
239
240 }