package com.buildml.eclipse.actions.handlers;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.jface.viewers.TreeSelection;
import org.eclipse.ui.handlers.HandlerUtil;
import com.buildml.eclipse.MainEditor;
import com.buildml.eclipse.bobj.UIAction;
import com.buildml.eclipse.utils.AlertDialog;
import com.buildml.eclipse.utils.ConversionUtils;
import com.buildml.eclipse.utils.EclipsePartUtils;
import com.buildml.eclipse.utils.UndoOpAdapter;
import com.buildml.eclipse.utils.dialogs.SubGroupSelectionDialog;
import com.buildml.eclipse.utils.errors.FatalError;
import com.buildml.model.IActionMgr;
import com.buildml.model.IBuildStore;
import com.buildml.model.types.ActionSet;
import com.buildml.model.undo.MultiUndoOp;
import com.buildml.refactor.CanNotRefactorException;
import com.buildml.refactor.IImportRefactorer;
/**
* An Eclipse UI Handler for managing the "Merge Actions" UI command.
* This handler merges multiple actions into a single action, providing
* the user feedback on anything that may have gone wrong in the process.
*
* @author Peter Smith <psmith@arapiki.com>
*/
public class HandlerMergeActions extends AbstractHandler {
/*=====================================================================================*
* PUBLIC METHODS
*=====================================================================================*/
/* (non-Javadoc)
* @see org.eclipse.core.commands.IHandler#execute(org.eclipse.core.commands.ExecutionEvent)
*/
@Override
public Object execute(ExecutionEvent event) throws ExecutionException {
MainEditor mainEditor = EclipsePartUtils.getActiveMainEditor();
IBuildStore buildStore = mainEditor.getBuildStore();
IImportRefactorer refactorer = mainEditor.getImportRefactorer();
TreeSelection selection = (TreeSelection)HandlerUtil.getCurrentSelection(event);
String mode = event.getParameter("com.buildml.eclipse.commandParameters.mergeAction");
/*
* In "selected" mode, we merge together all of the selected actions (into a single
* action).
*/
if (mode.equals("selected")) {
/* build an ActionSet of all the selected actions */
ActionSet selectedActions = EclipsePartUtils.getActionSetFromSelection(buildStore, selection);
MultiUndoOp multiOp = new MultiUndoOp();
performMergeActions(refactorer, buildStore, selectedActions, multiOp);
new UndoOpAdapter("Merge Actions", multiOp).invoke();
}
/*
* In "groups" mode, we provide a dialog box where the user can select the size of
* each sub-group. We then proceed to merge those subgroups.
*/
else if (mode.equals("groups")) {
MultiUndoOp multiOp = new MultiUndoOp();
performGroupMerges(refactorer, buildStore, selection, multiOp);
new UndoOpAdapter("Merge Actions Into Groups", multiOp).invoke();
}
/* else, not supported - throw internal error */
else {
throw new FatalError("Unsupported mode in com.buildml.eclipse.commandParameters.mergeAction");
}
return null;
}
/*=====================================================================================*
* PRIVATE METHODS
*=====================================================================================*/
/**
* Perform the merge action operation, with appropriate sanity tests, error reporting,
* and scheduling of a MultiUndoOp. This method will merge all specified actions into
* a single action.
*
* @param refactorer The IImportRefactorer to do the merging work.
* @param buildStore The IBuildStore that stores the actions.
* @param selectedActions The actions to be merged.
* @param multiOp The MultiUndoOp that all operations will be added to.
*/
private void performMergeActions(IImportRefactorer refactorer,
IBuildStore buildStore, ActionSet selectedActions, MultiUndoOp multiOp) {
IActionMgr actionMgr = buildStore.getActionMgr();
/* attempt to perform the "merge action" operation */
try {
refactorer.mergeActions(multiOp, selectedActions);
} catch (CanNotRefactorException e) {
Integer actionInError[] = e.getCauseIDs();
switch (e.getCauseCode()) {
case ACTION_NOT_ATOMIC:
String actionString = ConversionUtils.getActionsAsText(actionMgr, actionInError);
AlertDialog.displayErrorDialog("Merge failed",
"The \"merge actions\" operation failed because the following " +
((actionInError.length == 1) ? "action is not atomic:\n\n" :
"actions are not atomic:\n\n") + actionString);
break;
default:
AlertDialog.displayErrorDialog("Merge failed",
"The \"merge actions\" operation failed for the following reason: " +
e.getCauseCode());
}
}
}
/*-------------------------------------------------------------------------------------*/
/**
* Given the current selection, prompt the user to divide that selection into sub-groups,
* with each sub-group being merged into an action.
*
* @param refactorer The IImportRefactorer to do the merging work.
* @param buildStore The IBuildStore that contains the actions.
* @param selection The selection set of tree elements.
* @param multiOp The undo/redo operation into which we schedule merges.
*
*/
private void performGroupMerges(IImportRefactorer refactorer, IBuildStore buildStore,
TreeSelection selection, MultiUndoOp multiOp) {
IActionMgr actionMgr = buildStore.getActionMgr();
/*
* Compute two ordered lists: 1) The selection action IDs, 2) The command strings
* for those action IDs. The order is important, as it'll dictate which actions
* are merged into which sub-groups.
*/
List<Integer> actionIDs = new ArrayList<Integer>(selection.size());
List<String> actionCmds = new ArrayList<String>(selection.size());
Iterator<Object> iter = selection.iterator();
while (iter.hasNext()) {
Object item = iter.next();
if (item instanceof UIAction) {
UIAction action = (UIAction)item;
int actionId = action.getId();
String cmdString = (String) actionMgr.getSlotValue(actionId, IActionMgr.COMMAND_SLOT_ID);
actionIDs.add(actionId);
actionCmds.add(cmdString);
}
}
/*
* Pass the list to a Dialog, to ask the user the size of the sub-group to use.
*/
SubGroupSelectionDialog dialog =
new SubGroupSelectionDialog(actionCmds.toArray(new String[actionCmds.size()]));
int status = dialog.open();
if (status == SubGroupSelectionDialog.CANCEL) {
return;
}
/*
* Fetch the selected sub-group size (0 == no selection was made).
*/
int groupSize = dialog.getGroupSize();
if (groupSize == 0) {
return;
}
/*
* Now, perform the "merge action" operation on each subgroup. We create a new
* ActionSet for each subgroup and use performMergeActions() to merge them each
* into a single action.
*/
int size = actionIDs.size();
ActionSet actionsToMerge = null;
for (int i = 0; i != size; i++) {
/* are we starting a new sub-group? */
if ((i % groupSize) == 0) {
actionsToMerge = new ActionSet(actionMgr);
}
/* add the action ID to this sub group */
actionsToMerge.add(actionIDs.get(i));
/* did we reach the end of the sub-group? */
if (((i+1) % groupSize) == 0) {
performMergeActions(refactorer, buildStore, actionsToMerge, multiOp);
}
}
}
/*-------------------------------------------------------------------------------------*/
}