]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.modeling/src/org/simantics/modeling/MigrateModel.java
Fixed multiple issues causing dangling references to discarded queries
[simantics/platform.git] / bundles / org.simantics.modeling / src / org / simantics / modeling / MigrateModel.java
1 /*******************************************************************************
2  * Copyright (c) 2014, 2015 Association for Decentralized Information Management
3  * in 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  *     Semantum Oy - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.modeling;
13
14 import java.util.ArrayList;
15 import java.util.Collection;
16 import java.util.Collections;
17 import java.util.List;
18 import java.util.Map;
19 import java.util.Set;
20
21 import org.simantics.databoard.Bindings;
22 import org.simantics.databoard.util.URIStringUtils;
23 import org.simantics.datatypes.literal.GUID;
24 import org.simantics.db.ReadGraph;
25 import org.simantics.db.Resource;
26 import org.simantics.db.Statement;
27 import org.simantics.db.WriteGraph;
28 import org.simantics.db.common.NamedResource;
29 import org.simantics.db.common.procedure.adapter.TransientCacheListener;
30 import org.simantics.db.common.utils.NameUtils;
31 import org.simantics.db.exception.DatabaseException;
32 import org.simantics.db.layer0.adapter.Instances;
33 import org.simantics.db.layer0.request.ChildrenByIdentifier;
34 import org.simantics.db.layer0.util.Layer0Utils;
35 import org.simantics.diagram.stubs.DiagramResource;
36 import org.simantics.layer0.Layer0;
37 import org.simantics.structural.stubs.StructuralResource2;
38 import org.simantics.structural2.modelingRules.AllowedConnectionTypes;
39 import org.simantics.utils.ObjectUtils;
40 import org.simantics.utils.datastructures.Triple;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43
44 import gnu.trove.set.hash.THashSet;
45
46 /**
47  * @author Antti Villberg
48  */
49 public class MigrateModel {
50
51         public static final Logger LOGGER = LoggerFactory.getLogger(MigrateModel.class);
52
53         public static class MigrationOperation {
54                 
55                 public NamedResource instanceToMigrate;
56                 public NamedResource targetType;
57                 public NamedResource targetSymbol;
58                 
59                 public MigrationOperation(NamedResource nr, NamedResource targetType, NamedResource targetSymbol) {
60                         this.instanceToMigrate = nr;
61                         this.targetType = targetType;
62                         this.targetSymbol = targetSymbol;
63                 }
64                 
65                 @Override
66                 public String toString() {
67                         return instanceToMigrate.getName();
68                 }
69                 
70                 public String getDescription(ReadGraph graph) throws DatabaseException {
71                         String sourceURI = graph.getPossibleURI(instanceToMigrate.getResource());
72                         if(sourceURI == null) sourceURI = NameUtils.getSafeName(graph, instanceToMigrate.getResource());
73                         sourceURI = sourceURI.replace("http://Projects/Development%20Project/", "");
74                         if(targetSymbol != null) {
75                                 String targetURI = graph.getURI(targetSymbol.getResource());
76                                 return URIStringUtils.unescape(sourceURI) + " into " + URIStringUtils.unescape(targetURI);
77                         } else {
78                                 String targetURI = graph.getURI(targetType.getResource());
79                                 return URIStringUtils.unescape(sourceURI) + " into " + URIStringUtils.unescape(targetURI);
80                         }
81                 }
82                 
83                 private Resource getPossibleReplacement(ReadGraph graph, Resource type, Resource predicate) throws DatabaseException {
84                         Layer0 L0 = Layer0.getInstance(graph);
85                         
86                         // Try to find a relation with the same GUID
87                         GUID guid = graph.getPossibleRelatedValue(predicate, L0.identifier, GUID.BINDING);
88                         Map<GUID, Resource> children = graph.syncRequest(new ChildrenByIdentifier(type), TransientCacheListener.instance());
89                         Resource replacement = children.get(guid);
90                         if (replacement != null)
91                                 return replacement;
92                         
93                         // Fall back to using relation name
94                         String name = graph.getPossibleRelatedValue(predicate, L0.HasName, Bindings.STRING);
95                         if(name == null) return null;
96                         Resource child = Layer0Utils.getPossibleChild(graph, type, name);
97                         if(child != null) return child;
98                         for(Resource r : graph.getObjects(type, L0.DomainOf)) {
99                                 String rName = graph.getPossibleRelatedValue(r, L0.HasName, Bindings.STRING);
100                                 if(name.equals(rName)) return r;
101                         }
102                         return null; 
103                 }
104                 
105                 public String replace(WriteGraph graph, Resource instanceToMigrate, Resource type) throws DatabaseException {
106
107                         Layer0 L0 = Layer0.getInstance(graph);
108                         StructuralResource2 STR = StructuralResource2.getInstance(graph);
109
110                         Collection<Resource> currentTypes = graph.getPrincipalTypes(instanceToMigrate);
111                         if(currentTypes.size() == 1 && currentTypes.contains(type)) return null;
112                         
113                         StringBuilder problems = new StringBuilder();
114                         
115                         // Validate operation
116                         for(Statement stm : graph.getStatements(instanceToMigrate, L0.IsWeaklyRelatedTo)) {
117
118                                 Resource predicate = stm.getPredicate();
119                                 if(stm.isAsserted(instanceToMigrate)) continue;
120                                 
121                                 if(graph.isInstanceOf(predicate, STR.Property)) {
122                                         Resource replacement = getPossibleReplacement(graph, type, predicate);
123                                         // OK for a property to disappear
124                                         if(replacement == null) continue;
125                                         if(!graph.isInstanceOf(replacement, STR.Property)) {
126                                                 String name = graph.getRelatedValue(predicate, L0.HasName, Bindings.STRING);
127                                                 problems.append(" " + name + " was a property in the source type\n");
128                                                 continue;
129                                         }
130                                         
131                                         String sourceValueType = graph.getPossibleRelatedValue(predicate, L0.RequiresValueType, Bindings.STRING);
132                                         String replacementValueType = graph.getPossibleRelatedValue(replacement, L0.RequiresValueType, Bindings.STRING);
133                                         if(!ObjectUtils.objectEquals(sourceValueType, replacementValueType)) {
134                                                 String name = graph.getRelatedValue(predicate, L0.HasName, Bindings.STRING);
135                                                 problems.append(" value types for property " + name + " differ (" + sourceValueType + " vs. " + replacementValueType + ")\n");
136                                                 continue;
137                                         }
138                                 }
139
140                                 if(graph.isInstanceOf(predicate, STR.ConnectionRelation)) {
141                                         Resource replacement = getPossibleReplacement(graph, type, predicate);
142                                         if(replacement == null) {
143                                                 String name = graph.getRelatedValue(predicate, L0.HasName, Bindings.STRING);
144                                                 problems.append(" used connection point " + name + " has been removed from target type\n");
145                                                 continue;
146                                         }
147                                         if(!graph.isInstanceOf(replacement, STR.ConnectionRelation)) {
148                                                 String name = graph.getRelatedValue(predicate, L0.HasName, Bindings.STRING);
149                                                 problems.append(" " + name + " was a connection point in the source type\n");
150                                                 continue;
151                                         }
152
153                                         Collection<Resource> sourceConnetionTypes = graph.syncRequest(new AllowedConnectionTypes(predicate));
154                                         Collection<Resource> replacementConnectionTypes = graph.syncRequest(new AllowedConnectionTypes(replacement));
155                                         
156                                         Set<Resource> sourceSet = new THashSet<>(sourceConnetionTypes);
157                                         Set<Resource> replacementSet = new THashSet<>(replacementConnectionTypes);
158                                         
159                                         if(!sourceSet.equals(replacementSet)) {
160                                                 String name = graph.getRelatedValue(predicate, L0.HasName, Bindings.STRING);
161                                                 problems.append(" allowed connection types for connection point " + name + " differ (" + NameUtils.getSafeName(graph, sourceSet) + " vs. " + NameUtils.getSafeName(graph, replacementSet) + ")\n");
162                                                 continue;
163                                         }
164
165                                 }
166                                 
167                         }
168                         
169                         if(problems.length() > 0) {
170                                 return problems.toString();
171                         }
172
173                         // Perform operation
174                         for(Statement stm : graph.getStatements(instanceToMigrate, L0.IsWeaklyRelatedTo)) {
175
176                                 Resource predicate = stm.getPredicate();
177                                 if(stm.isAsserted(instanceToMigrate)) continue;
178                                                 
179                                 if(L0.InstanceOf.equals(predicate)) {
180                                         graph.deny(stm);
181                                         graph.claim(instanceToMigrate, L0.InstanceOf, type);
182                                 }
183                                 
184                                 if(graph.isInstanceOf(predicate, STR.Property) || graph.isInstanceOf(predicate, STR.ConnectionRelation)) {
185                                         Resource replacement = getPossibleReplacement(graph, type, predicate);
186                                         graph.deny(stm);
187                                         if(replacement == null) continue;
188                                         
189                                         // Filter out incompatible literal types.
190                                         // TODO: Show warning in UI
191                                         Resource object = stm.getObject();
192                                         Resource replacementRange = graph.getPossibleObject(replacement, L0.HasRange);
193                                         if (replacementRange != null && !graph.isInstanceOf(object, replacementRange)) {
194                                                 String instanceName = graph.getPossibleRelatedValue(instanceToMigrate, L0.HasName);
195                                                 if (instanceName == null) instanceName = "<unknown>";
196                                                 
197                                                 String rangeName = graph.getPossibleRelatedValue(replacementRange, L0.HasName);
198                                                 if (rangeName == null) rangeName = "<unknown>";
199                                                 
200                                                 Resource literalType= graph.getPossibleType(object, L0.Value);
201                                                 String literalTypeName = literalType != null ? graph.getPossibleRelatedValue(literalType, L0.HasName) : null;
202                                                 if (literalTypeName == null) literalTypeName = "<unknown>";
203                                                 
204                                                 String replacementName = graph.getRelatedValue(replacement, L0.HasName);
205                                                 LOGGER.warn("{}: Ignored incompatible value of type {} for predicate {} with range {}", instanceName, literalTypeName, replacementName, rangeName);
206                                                 
207                                                 continue;
208                                         }
209                                         
210                                         graph.claim(stm.getSubject(), replacement, object);
211                                 }
212                                 
213                         }
214                         
215                         return null;
216
217                 }
218                 
219                 public String perform(WriteGraph graph) throws DatabaseException {
220                         
221                         Resource instance = instanceToMigrate.getResource();
222                         Resource type = targetType.getResource();
223                         Resource symbol = targetSymbol != null ? targetSymbol.getResource() : null;
224                         
225                         String result = replace(graph, instance, type);
226                         if(result != null) return result;
227                         
228                         ModelingResources MOD = ModelingResources.getInstance(graph);
229                         Resource element = graph.getPossibleObject(instance, MOD.ComponentToElement);
230                         if(element == null) return null;
231                         
232                         Resource targetSymbol = symbol;
233                         if(targetSymbol == null) {
234                                 Layer0 L0 = Layer0.getInstance(graph);
235                                 DiagramResource DIA = DiagramResource.getInstance(graph);
236                                 Resource currentSymbol = graph.getPossibleType(element, DIA.Element);
237                                 if(currentSymbol != null) {
238                                         String currentSymbolName = graph.getRelatedValue(currentSymbol, L0.HasName, Bindings.STRING);
239                                         targetSymbol = Layer0Utils.getPossibleChild(graph, type, DIA.ElementClass, currentSymbolName);
240                                         if(targetSymbol == null)
241                                                 return "Did not find symbol '" + currentSymbolName + "' from target type.\n";
242                                 } else {
243                                         // No symbol - fine 
244                                         return null;
245                                 }
246
247                         }
248                         
249                         return replace(graph, element, targetSymbol);
250                         
251                 }
252                 
253         }
254
255         public int instanceCount = 0;
256         public Set<Resource> activeModels = new THashSet<>();
257         public List<Triple<String, NamedResource, Collection<MigrationOperation>>> instances = new ArrayList<>();
258         public List<MigrationOperation> sortedShownInstances = Collections.emptyList();
259
260         public static void changeComponentType(WriteGraph graph, Resource instance, Resource newComponentType) throws DatabaseException {
261             ModelingResources MOD = ModelingResources.getInstance(graph);
262             Resource newSymbol = graph.getSingleObject(newComponentType, MOD.ComponentTypeToSymbol);
263             new MigrationOperation(new NamedResource("", instance), new NamedResource("", newComponentType), new NamedResource("", newSymbol))
264             .perform(graph);
265         }
266
267         public static void changeAllComponentTypes(WriteGraph graph, Resource model, Resource oldComponentType, Resource newComponentType) throws DatabaseException {
268             ModelingResources MOD = ModelingResources.getInstance(graph);
269             NamedResource newComponentTypeN = new NamedResource("", newComponentType);
270         Resource newSymbol = graph.getSingleObject(newComponentType, MOD.ComponentTypeToSymbol);
271         NamedResource newSymbolN = new NamedResource("", newSymbol);
272         
273             Collection<Resource> instances = graph.adapt(oldComponentType, Instances.class).find(graph, model);
274             
275             for(Resource instance : instances) {
276                 new MigrationOperation(
277                         new NamedResource("", instance),
278                         newComponentTypeN,
279                         newSymbolN)
280                 .perform(graph);
281             }
282         }
283
284 }