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