--- /dev/null
+package org.simantics.modeling.adapters;\r
+\r
+import gnu.trove.map.hash.THashMap;\r
+import gnu.trove.set.hash.THashSet;\r
+\r
+import java.util.Arrays;\r
+import java.util.Collection;\r
+import java.util.Collections;\r
+import java.util.Comparator;\r
+import java.util.HashMap;\r
+import java.util.Map;\r
+import java.util.Set;\r
+\r
+import org.eclipse.jface.dialogs.IDialogConstants;\r
+import org.eclipse.jface.dialogs.MessageDialog;\r
+import org.eclipse.jface.resource.ImageDescriptor;\r
+import org.eclipse.ui.PlatformUI;\r
+import org.simantics.databoard.Bindings;\r
+import org.simantics.databoard.util.URIStringUtils;\r
+import org.simantics.db.ReadGraph;\r
+import org.simantics.db.Resource;\r
+import org.simantics.db.Session;\r
+import org.simantics.db.WriteGraph;\r
+import org.simantics.db.common.request.PossibleIndexRoot;\r
+import org.simantics.db.common.request.WriteRequest;\r
+import org.simantics.db.common.utils.NameUtils;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.db.layer0.adapter.Instances;\r
+import org.simantics.db.layer0.adapter.Remover;\r
+import org.simantics.db.layer0.adapter.impl.AbstractRemover;\r
+import org.simantics.db.layer0.adapter.impl.EntityRemover;\r
+import org.simantics.db.layer0.exception.CannotRemoveException;\r
+import org.simantics.db.layer0.util.Layer0Utils;\r
+import org.simantics.db.layer0.util.RemoverUtil;\r
+import org.simantics.layer0.Layer0;\r
+import org.simantics.modeling.ModelingResources;\r
+import org.simantics.modeling.adapters.RemoveInstancesDialog.Content;\r
+import org.simantics.utils.strings.AlphanumComparator;\r
+import org.simantics.utils.ui.BundleUtils;\r
+\r
+/**\r
+ * @author Tuukka Lehtonen\r
+ */\r
+public class ExistingInstancesRemover extends AbstractRemover {\r
+\r
+ protected String typeDescription;\r
+\r
+ public ExistingInstancesRemover(Resource resource, String removedTypeDescription) {\r
+ super(resource);\r
+ this.typeDescription = removedTypeDescription;\r
+ }\r
+\r
+ @Override\r
+ public void remove(WriteGraph graph) throws DatabaseException {\r
+ remove(graph, resource, resource, resource);\r
+ }\r
+\r
+ protected void remove(WriteGraph graph, Resource resource, Resource typeResource, Resource typeNameResource) throws DatabaseException {\r
+// System.out.println("resource: " + NameUtils.getURIOrSafeNameInternal(graph, resource));\r
+// System.out.println("type resource: " + NameUtils.getURIOrSafeNameInternal(graph, typeResource));\r
+// System.out.println("type name resource: " + NameUtils.getURIOrSafeNameInternal(graph, typeNameResource));\r
+ if (Layer0Utils.isContainerPublished(graph, resource))\r
+ throw new CannotRemoveException("Items in published libraries cannot be removed. Please create a new version to perform modifications.");\r
+\r
+ Resource root = graph.syncRequest(new PossibleIndexRoot(resource));\r
+ if (root == null) {\r
+ justRemove(graph);\r
+ // Not part of an index root? Just remove everything because\r
+ // we can't find instances anyway.\r
+ return;\r
+ }\r
+\r
+ Layer0 L0 = Layer0.getInstance(graph);\r
+ final String componentTypeName = graph.getPossibleRelatedValue(typeNameResource, L0.HasName, Bindings.STRING);\r
+ if (componentTypeName == null) {\r
+ justRemove(graph);\r
+ return;\r
+ }\r
+\r
+ Set<Resource> instances = discoverInstances(graph, typeResource);\r
+ if (!instances.isEmpty()) {\r
+ confirmRemoval(graph, instances, typeDescription, componentTypeName);\r
+ } else {\r
+ justRemove(graph);\r
+ }\r
+ }\r
+\r
+ protected Set<Resource> discoverInstances(WriteGraph graph, Resource type) throws DatabaseException {\r
+ Resource indexRoot = graph.syncRequest(new PossibleIndexRoot(type));\r
+ if (indexRoot == null)\r
+ return Collections.emptySet();\r
+\r
+// @SuppressWarnings("rawtypes")\r
+// Function modules = graph.adapt(Layer0X.getInstance(graph).Dependencies, Function.class);\r
+// @SuppressWarnings("unchecked")\r
+// List<Map<String, Object>> result = (List<Map<String, Object>>) modules.apply(graph, root, "Types:\"" + IndexQueries.escape(componentTypeName) + "\"");\r
+// if (result.isEmpty()) {\r
+// justRemove(graph);\r
+// return;\r
+// }\r
+//\r
+// final Set<Resource> instances = new HashSet<Resource>();\r
+// for (Map<String, Object> entry : result) {\r
+// Resource c = (Resource) entry.get(Dependencies.FIELD_RESOURCE);\r
+// if (c != null && graph.hasStatement(c, L0.InstanceOf, resource)) {\r
+// instances.add(c);\r
+// }\r
+// }\r
+\r
+ Set<Resource> result = new THashSet<Resource>();\r
+ Instances search = graph.adapt(type, Instances.class);\r
+ discoverInstances(graph, type, indexRoot, search, result);\r
+\r
+ // It is possible that resource is an instance of the specified type\r
+ // also. However we assume that the specified resource is removed last\r
+ // only after all the instances have been removed. That's why we remove\r
+ // resource from the calculated set of instances at this point.\r
+ result.remove(resource);\r
+\r
+// for (Resource r : result)\r
+// System.out.println("found instance: " + NameUtils.getURIOrSafeNameInternal(graph, r));\r
+\r
+ return result;\r
+ }\r
+\r
+ private void discoverInstances(WriteGraph graph, Resource typeResource, Resource indexRoot, Instances instances, Set<Resource> result) throws DatabaseException {\r
+ Layer0 L0 = Layer0.getInstance(graph);\r
+ Collection<Resource> rs = instances.find(graph, indexRoot);\r
+ result.addAll(rs);\r
+\r
+ for (Resource linkee : graph.getObjects(indexRoot, L0.IsLinkedTo_Inverse)) {\r
+ discoverInstances(graph, typeResource, linkee, instances, result);\r
+ }\r
+ }\r
+\r
+ protected void confirmRemoval(WriteGraph graph, final Set<Resource> instances, final String typeDescription, final String typeName) throws DatabaseException {\r
+ Map<Resource, String> instanceAddresses = resolveInstanceAddresses(graph, instances);\r
+ Map<Resource, String> unremovable = findUnremovable(graph, instances);\r
+ final RemoveInstancesDialog.Content[] content = new RemoveInstancesDialog.Content[instanceAddresses.size()];\r
+ int i = 0;\r
+ ImageDescriptor problemImage = BundleUtils.getImageDescriptorFromPlugin("com.famfamfam.silk", "icons/error.png");\r
+ for (Map.Entry<Resource, String> entry : instanceAddresses.entrySet()) {\r
+ content[i] = new RemoveInstancesDialog.Content(entry.getValue());\r
+ content[i].details = unremovable.get(entry.getKey());\r
+ if (content[i].details != null) {\r
+ content[i].image = problemImage;\r
+ }\r
+ ++i;\r
+ }\r
+ Arrays.sort(content, new Comparator<RemoveInstancesDialog.Content>() {\r
+ @Override\r
+ public int compare(Content o1, Content o2) {\r
+ return AlphanumComparator.CASE_INSENSITIVE_COMPARATOR.compare(o1.label, o2.label);\r
+ }\r
+ });\r
+\r
+ if (!unremovable.isEmpty()) {\r
+ if (PlatformUI.isWorkbenchRunning()) {\r
+ PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {\r
+ @Override\r
+ public void run() {\r
+ RemoveInstancesDialog dialog = new RemoveInstancesDialog(\r
+ PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),\r
+ "Cannot Remove " + typeDescription,\r
+ typeDescription + " '" + typeName\r
+ + "' is still in use and all of its "\r
+ + instances.size()\r
+ + " instances cannot be removed.\n\nSelect instances marked with errors from the table below to see why they cannot be removed.",\r
+ MessageDialog.ERROR,\r
+ new String[] { IDialogConstants.OK_LABEL },\r
+ 0,\r
+ content);\r
+ dialog.open();\r
+ }\r
+ });\r
+ }\r
+ return;\r
+ }\r
+\r
+ final Session session = graph.getSession();\r
+\r
+ if (PlatformUI.isWorkbenchRunning()) {\r
+ PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {\r
+ @Override\r
+ public void run() {\r
+ RemoveInstancesDialog dialog = new RemoveInstancesDialog(\r
+ PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),\r
+ "Remove " + typeDescription + "?",\r
+ typeDescription + " '" + typeName + "' is still in use. Are you sure you want to remove it and all its " + instances.size() + " instances?",\r
+ content);\r
+ int result = dialog.open();\r
+ boolean doIt = result == IDialogConstants.OK_ID;\r
+ if (!doIt)\r
+ return;\r
+ session.asyncRequest(new WriteRequest() {\r
+ @Override\r
+ public void perform(WriteGraph graph) throws DatabaseException {\r
+ justRemoveWithInstances(graph, instances);\r
+ }\r
+ });\r
+ }\r
+ });\r
+ } else {\r
+ // Just do it without confirmation when no user agent is available.\r
+ justRemoveWithInstances(graph, instances);\r
+ }\r
+ }\r
+\r
+ protected void justRemoveWithInstances(WriteGraph graph, Collection<Resource> instances) throws DatabaseException {\r
+ for (Resource instance : instances)\r
+ RemoverUtil.remove(graph, instance);\r
+ justRemove(graph);\r
+ }\r
+\r
+ protected Map<Resource, String> resolveInstanceAddresses(ReadGraph graph, Set<Resource> instances) throws DatabaseException {\r
+ Layer0 L0 = Layer0.getInstance(graph);\r
+ ModelingResources MOD = ModelingResources.getInstance(graph);\r
+ Map<Resource, String> result = new THashMap<Resource, String>();\r
+ for (Resource instance : instances) {\r
+ Resource component = graph.getPossibleObject(instance, MOD.ElementToComponent);\r
+ if (component != null)\r
+ instance = component;\r
+ String instanceUri = graph.getPossibleURI(instance);\r
+ if (instanceUri != null) {\r
+ Resource root = graph.syncRequest(new PossibleIndexRoot(instance));\r
+ if (root != null) {\r
+ Resource rootParent = graph.getPossibleObject(root, L0.PartOf);\r
+ if (rootParent == null)\r
+ rootParent = root;\r
+\r
+ String rootUri = graph.getPossibleURI(rootParent);\r
+ if (rootUri != null) {\r
+ String instanceRelativeUri = instanceUri.substring(rootUri.length());\r
+ result.put(instance, URIStringUtils.unescape( instanceRelativeUri ));\r
+ continue;\r
+ }\r
+ }\r
+\r
+ result.put(instance, URIStringUtils.unescape( instanceUri ));\r
+ continue;\r
+ }\r
+\r
+ // Fallback logic\r
+ result.put(instance, NameUtils.getSafeName(graph, instance, true));\r
+ }\r
+ return result;\r
+ }\r
+\r
+ protected void justRemove(WriteGraph graph) throws DatabaseException {\r
+ graph.deny(resource, Layer0.getInstance(graph).PartOf);\r
+ EntityRemover.remove(graph, resource);\r
+ }\r
+\r
+ private Map<Resource, String> findUnremovable(ReadGraph graph, Collection<Resource> instances) throws DatabaseException {\r
+ Map<Resource, String> result = null;\r
+ Map<Object, Object> aux = new HashMap<Object, Object>();\r
+ for (Resource r : instances) {\r
+ Remover remover = graph.getPossibleAdapter(r, Remover.class);\r
+ if (remover == null)\r
+ continue;\r
+ aux.clear();\r
+ String problem = remover.canRemove(graph, aux);\r
+ if (problem != null) {\r
+ if (result == null)\r
+ result = new THashMap<Resource, String>();\r
+ result.put(r, problem);\r
+ }\r
+ }\r
+ return result == null ? Collections.<Resource, String>emptyMap() : result;\r
+ }\r
+\r
+}
\ No newline at end of file