/******************************************************************************* * Copyright (c) 2013 Arapiki Solutions Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * psmith - initial API and * implementation and/or initial documentation *******************************************************************************/ package com.buildml.eclipse.packages.features; import java.util.ArrayList; import java.util.Arrays; import org.eclipse.graphiti.features.IDeleteFeature; import org.eclipse.graphiti.features.IFeatureProvider; import org.eclipse.graphiti.features.context.IContext; import org.eclipse.graphiti.features.context.IDeleteContext; import org.eclipse.graphiti.pattern.IPattern; import com.buildml.eclipse.bobj.UIFileActionConnection; import com.buildml.eclipse.bobj.UIMergeFileGroupConnection; import com.buildml.eclipse.packages.DiagramFeatureProvider; import com.buildml.eclipse.packages.PackageDiagramEditor; import com.buildml.eclipse.utils.GraphitiUtils; import com.buildml.eclipse.utils.UndoOpAdapter; import com.buildml.model.IBuildStore; import com.buildml.model.IFileGroupMgr; import com.buildml.model.undo.ActionUndoOp; import com.buildml.model.undo.FileGroupUndoOp; import com.buildml.model.undo.MultiUndoOp; /** * A customized "DeleteFeature" that calls upon the appropriate Pattern to do the work * of deleting a selected pictogram and its underlying business object. * * @author Peter Smith <psmith@arapiki.com> */ public class PackageDiagramDeleteFeature implements IDeleteFeature { /*=====================================================================================* * FIELDS/TYPES *=====================================================================================*/ /** The feature provider that owns the patterns we use */ private DiagramFeatureProvider featureProvider; /** The IBuildStore we operate on */ private IBuildStore buildStore; /*=====================================================================================* * CONSTRUCTORS *=====================================================================================*/ /** * Create a new feature provider for handling our delete operations within Graphiti. * @param diagramFeatureProvider The feature provider that owns the patterns. */ public PackageDiagramDeleteFeature(DiagramFeatureProvider diagramFeatureProvider) { this.featureProvider = diagramFeatureProvider; PackageDiagramEditor pde = (PackageDiagramEditor)featureProvider.getDiagramTypeProvider().getDiagramEditor(); buildStore = pde.getBuildStore(); } /*=====================================================================================* * PUBLIC METHODS *=====================================================================================*/ /* (non-Javadoc) * @see org.eclipse.graphiti.features.IFeature#isAvailable(org.eclipse.graphiti.features.context.IContext) */ @Override public boolean isAvailable(IContext context) { return true; } /*-------------------------------------------------------------------------------------*/ /* (non-Javadoc) * @see org.eclipse.graphiti.features.IFeature#canExecute(org.eclipse.graphiti.features.context.IContext) */ @Override public boolean canExecute(IContext context) { return true; } /*-------------------------------------------------------------------------------------*/ /** * Proceed to execute the delete operation. Only the pattern can do this. */ @Override public void execute(IContext context) { /* identify the pattern to be used */ IDeleteContext deleteContext = (IDeleteContext)context; IPattern pattern = featureProvider.getPatternForPictogramElement(deleteContext.getPictogramElement()); if (pattern != null) { pattern.delete(deleteContext); } /* connections don't have patterns, so we need a special method to delete them */ else { Object bo = GraphitiUtils.getBusinessObject(deleteContext.getPictogramElement()); if (bo instanceof UIFileActionConnection) { deleteFileActionConnection(deleteContext); } else if (bo instanceof UIMergeFileGroupConnection) { deleteMergeFileGroupConnection(deleteContext); } } } /*-------------------------------------------------------------------------------------*/ /** * We don't use Graphiti's undo/redo stack. */ @Override public boolean canUndo(IContext context) { return false; } /*-------------------------------------------------------------------------------------*/ /** * We don't want to appear in Graphiti's undo/redo stack. We'll maintain our own. */ @Override public boolean hasDoneChanges() { return false; } /*-------------------------------------------------------------------------------------*/ /* (non-Javadoc) * @see org.eclipse.graphiti.IName#getName() */ @Override public String getName() { return "PackageDiagramDeleteFeature"; } /*-------------------------------------------------------------------------------------*/ /* (non-Javadoc) * @see org.eclipse.graphiti.IDescription#getDescription() */ @Override public String getDescription() { return "BuildML Delete Feature"; } /*-------------------------------------------------------------------------------------*/ /* (non-Javadoc) * @see org.eclipse.graphiti.features.IFeatureProviderHolder#getFeatureProvider() */ @Override public IFeatureProvider getFeatureProvider() { return featureProvider; } /*-------------------------------------------------------------------------------------*/ /** * Determine if we can delete the type of business object that's selected. Only the * pattern will know this. */ @Override public boolean canDelete(IDeleteContext context) { /* identify the pattern to be used */ IPattern pattern = featureProvider.getPatternForPictogramElement(context.getPictogramElement()); if (pattern != null) { return pattern.canDelete(context); } Object bo = GraphitiUtils.getBusinessObject(context.getPictogramElement()); if (bo instanceof UIFileActionConnection) { return true; } else if (bo instanceof UIMergeFileGroupConnection) { return true; } return false; } /*-------------------------------------------------------------------------------------*/ /* * (non-Javadoc) * @see org.eclipse.graphiti.func.IDelete#preDelete(org.eclipse.graphiti.features.context.IDeleteContext) */ @Override public void preDelete(IDeleteContext context) { /* unused */ } /*-------------------------------------------------------------------------------------*/ /* (non-Javadoc) * @see org.eclipse.graphiti.func.IDelete#delete(org.eclipse.graphiti.features.context.IDeleteContext) */ @Override public void delete(IDeleteContext context) { /* unused */ } /*-------------------------------------------------------------------------------------*/ /* (non-Javadoc) * @see org.eclipse.graphiti.func.IDelete#postDelete(org.eclipse.graphiti.features.context.IDeleteContext) */ @Override public void postDelete(IDeleteContext context) { /* unused */ } /*=====================================================================================* * PRIVATE METHODS *=====================================================================================*/ /** * A special-purpose method for deleting a fileGroup-action connection arrow. There are * two possible scenarios. If the connection has a filter, the delete operation will remove * the filter only. If it doesn't have a filter, the whole connection will be removed. * Therefore, the user might need to press "delete" twice to completely remove the * connection. * * @param context The Graphiti context of the delete operation. */ private void deleteFileActionConnection(IDeleteContext context) { Object bo = GraphitiUtils.getBusinessObject(context.getPictogramElement()); if (!(bo instanceof UIFileActionConnection)) { return; } UIFileActionConnection connection = (UIFileActionConnection)bo; /* If there's filter, delete it */ if (connection.hasFilter()) { MultiUndoOp multiOp = new MultiUndoOp(); /* set the action's slot to refer to the filter's predecessor */ ActionUndoOp actionOp = new ActionUndoOp(buildStore, connection.getActionId()); actionOp.recordSlotChange(connection.getSlotId(), connection.getFilterGroupId(), connection.getFileGroupId()); multiOp.add(actionOp); /* set the filter group's content to empty, so it'll be garbage collected upon shutdown */ emptyFilterGroup(multiOp, connection.getFilterGroupId()); /* record and invoke the undo/redo operation */ new UndoOpAdapter("Delete Filter", multiOp).invoke(); } /* no filter, so delete the connection */ else { /* create an undo/redo operation to set the slot value back to null */ ActionUndoOp op = new ActionUndoOp(buildStore, connection.getActionId()); op.recordSlotChange(connection.getSlotId(), connection.getFileGroupId(), null); new UndoOpAdapter("Delete Connection", op).invoke(); } } /*-------------------------------------------------------------------------------------*/ /** * A special-purpose method for deleting a merge file group connection arrow. There are * two possible scenarios. If the connection has a filter, the delete operation will remove * the filter only. If it doesn't have a filter, the whole connection will be removed. * Therefore, the user might need to press "delete" twice to completely remove the * connection. * * @param context The Graphiti context of the delete operation. */ private void deleteMergeFileGroupConnection(IDeleteContext context) { /* locate the connection information */ Object bo = GraphitiUtils.getBusinessObject(context.getPictogramElement()); if (!(bo instanceof UIMergeFileGroupConnection)) { return; } IFileGroupMgr fileGroupMgr = buildStore.getFileGroupMgr(); UIMergeFileGroupConnection connection = (UIMergeFileGroupConnection)bo; int mergeFileGroupId = connection.getTargetFileGroupId(); int subFileGroupId = connection.getSourceFileGroupId(); int index = connection.getIndex(); /* * If there's a filter, remove that only. */ if (connection.hasFilter()) { MultiUndoOp multiOp = new MultiUndoOp(); /* replace the merge group's "index" entry with the filter group's predecessor */ FileGroupUndoOp mergeGroupOp = new FileGroupUndoOp(buildStore, mergeFileGroupId); Integer members[] = fileGroupMgr.getSubGroups(mergeFileGroupId); ArrayList<Integer> currentMembers = new ArrayList<Integer>(Arrays.asList(members)); ArrayList<Integer> newMembers = new ArrayList<Integer>(currentMembers); newMembers.set(index, subFileGroupId); mergeGroupOp.recordMembershipChange(currentMembers, newMembers); multiOp.add(mergeGroupOp); /* set the filter group's content to empty, so it'll be garbage collected upon shutdown */ emptyFilterGroup(multiOp, connection.getFilterGroupId()); /* record and invoke the operation */ new UndoOpAdapter("Delete Filter", multiOp).invoke(); } /* * If there's no filter, remove the whole connection. */ else { /* our new membership will be the same as the current membership, with subFileGroupId removed */ FileGroupUndoOp op = new FileGroupUndoOp(buildStore, mergeFileGroupId); Integer members[] = fileGroupMgr.getSubGroups(mergeFileGroupId); ArrayList<Integer> currentMembers = new ArrayList<Integer>(Arrays.asList(members)); ArrayList<Integer> newMembers = new ArrayList<Integer>(currentMembers); newMembers.remove(index); op.recordMembershipChange(currentMembers, newMembers); /* record and invoke the operation */ new UndoOpAdapter("Delete Connection", op).invoke(); } } /*-------------------------------------------------------------------------------------*/ /** * Helper method for creating the undo/redo commands for emptying a filter file group. * * @param multiOp The BmlMultiOperation to append the commands to. * @param groupId The filter group's ID. */ private void emptyFilterGroup(MultiUndoOp multiOp, int groupId) { FileGroupUndoOp fileGroupOp = new FileGroupUndoOp(buildStore, groupId); IFileGroupMgr fileGroupMgr = buildStore.getFileGroupMgr(); String currentMembers[] = fileGroupMgr.getPathStrings(groupId); if (currentMembers != null) { fileGroupOp.recordMembershipChange(Arrays.asList(currentMembers), new ArrayList<String>()); multiOp.add(fileGroupOp); } } /*-------------------------------------------------------------------------------------*/ }