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