1 /*******************************************************************************
2 * Copyright (c) 2014, 2015 Association for Decentralized Information Management
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
10 * Semantum Oy - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.modeling;
14 import java.util.ArrayList;
15 import java.util.Collection;
16 import java.util.Collections;
17 import java.util.List;
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;
44 import gnu.trove.set.hash.THashSet;
47 * @author Antti Villberg
49 public class MigrateModel {
51 public static final Logger LOGGER = LoggerFactory.getLogger(MigrateModel.class);
53 public static class MigrationOperation {
55 public NamedResource instanceToMigrate;
56 public NamedResource targetType;
57 public NamedResource targetSymbol;
59 public MigrationOperation(NamedResource nr, NamedResource targetType, NamedResource targetSymbol) {
60 this.instanceToMigrate = nr;
61 this.targetType = targetType;
62 this.targetSymbol = targetSymbol;
66 public String toString() {
67 return instanceToMigrate.getName();
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);
78 String targetURI = graph.getURI(targetType.getResource());
79 return URIStringUtils.unescape(sourceURI) + " into " + URIStringUtils.unescape(targetURI);
83 private Resource getPossibleReplacement(ReadGraph graph, Resource type, Resource predicate) throws DatabaseException {
84 Layer0 L0 = Layer0.getInstance(graph);
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)
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;
105 public String replace(WriteGraph graph, Resource instanceToMigrate, Resource type) throws DatabaseException {
107 Layer0 L0 = Layer0.getInstance(graph);
108 StructuralResource2 STR = StructuralResource2.getInstance(graph);
110 Collection<Resource> currentTypes = graph.getPrincipalTypes(instanceToMigrate);
111 if(currentTypes.size() == 1 && currentTypes.contains(type)) return null;
113 StringBuilder problems = new StringBuilder();
115 // Validate operation
116 for(Statement stm : graph.getStatements(instanceToMigrate, L0.IsWeaklyRelatedTo)) {
118 Resource predicate = stm.getPredicate();
119 if(stm.isAsserted(instanceToMigrate)) continue;
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");
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");
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");
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");
153 Collection<Resource> sourceConnetionTypes = graph.syncRequest(new AllowedConnectionTypes(predicate));
154 Collection<Resource> replacementConnectionTypes = graph.syncRequest(new AllowedConnectionTypes(replacement));
156 Set<Resource> sourceSet = new THashSet<>(sourceConnetionTypes);
157 Set<Resource> replacementSet = new THashSet<>(replacementConnectionTypes);
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");
169 if(problems.length() > 0) {
170 return problems.toString();
174 for(Statement stm : graph.getStatements(instanceToMigrate, L0.IsWeaklyRelatedTo)) {
176 Resource predicate = stm.getPredicate();
177 if(stm.isAsserted(instanceToMigrate)) continue;
179 if(L0.InstanceOf.equals(predicate)) {
181 graph.claim(instanceToMigrate, L0.InstanceOf, type);
184 if(graph.isInstanceOf(predicate, STR.Property) || graph.isInstanceOf(predicate, STR.ConnectionRelation)) {
185 Resource replacement = getPossibleReplacement(graph, type, predicate);
187 if(replacement == null) continue;
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>";
197 String rangeName = graph.getPossibleRelatedValue(replacementRange, L0.HasName);
198 if (rangeName == null) rangeName = "<unknown>";
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>";
204 String replacementName = graph.getRelatedValue(replacement, L0.HasName);
205 LOGGER.warn("{}: Ignored incompatible value of type {} for predicate {} with range {}", instanceName, literalTypeName, replacementName, rangeName);
210 graph.claim(stm.getSubject(), replacement, object);
219 public String perform(WriteGraph graph) throws DatabaseException {
221 Resource instance = instanceToMigrate.getResource();
222 Resource type = targetType.getResource();
223 Resource symbol = targetSymbol != null ? targetSymbol.getResource() : null;
225 String result = replace(graph, instance, type);
226 if(result != null) return result;
228 ModelingResources MOD = ModelingResources.getInstance(graph);
229 Resource element = graph.getPossibleObject(instance, MOD.ComponentToElement);
230 if(element == null) return null;
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";
249 return replace(graph, element, targetSymbol);
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();
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))
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);
273 Collection<Resource> instances = graph.adapt(oldComponentType, Instances.class).find(graph, model);
275 for(Resource instance : instances) {
276 new MigrationOperation(
277 new NamedResource("", instance),