/******************************************************************************* * Copyright (c) 2012-2015 Codenvy, S.A. * 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: * Codenvy, S.A. - initial API and implementation *******************************************************************************/ package org.eclipse.che.ide.api.action; import org.eclipse.che.ide.api.constraints.Anchor; import org.eclipse.che.ide.api.constraints.Constraints; import org.eclipse.che.ide.util.Pair; import org.eclipse.che.ide.util.loging.Log; import java.util.ArrayList; import java.util.Collection; import java.util.List; /** * A default implementation of {@link ActionGroup}. Provides the ability * to add children actions and separators between them. In most of the * cases you will be using this implementation but note that there are * cases where children are determined * on rules different than just positional constraints, that's when you need * to implement your own <code>ActionGroup</code>. * * @author Evgen Vidolob */ public class DefaultActionGroup extends ActionGroup { /** Contains instances of AnAction */ private final List<Action> mySortedChildren = new ArrayList<>(); /** Contains instances of Pair */ private final List<Pair<Action, Constraints>> myPairs = new ArrayList<>(); private ActionManager actionManager; public DefaultActionGroup(ActionManager actionManager) { this(null, false, actionManager); } /** * Creates an action group containing the specified actions. * * @param actions * the actions to add to the group */ public DefaultActionGroup(ActionManager actionManager, Action... actions) { this(null, false, actionManager); for (Action action : actions) { add(action); } } public DefaultActionGroup(String shortName, boolean popup, ActionManager actionManager) { super(shortName, popup); this.actionManager = actionManager; } private static int findIndex(String actionId, List<Action> actions, ActionManager actionManager) { for (int i = 0; i < actions.size(); i++) { Action action = actions.get(i); String id = actionManager.getId(action); if (id != null && id.equals(actionId)) { return i; } } return -1; } /** * Adds the specified action to the tail. * * @param action * Action to be added * @param actionManager * ActionManager instance */ public final void add(Action action, ActionManager actionManager) { add(action, new Constraints(Anchor.LAST, null), actionManager); } public final void add(Action action) { addAction(action, new Constraints(Anchor.LAST, null)); } public final ActionInGroup addAction(Action action) { return addAction(action, new Constraints(Anchor.LAST, null)); } /** Adds a separator to the tail. */ public final void addSeparator() { add(Separator.getInstance()); } public final void add(Action action, Constraints constraint) { add(action, constraint, actionManager); } public final ActionInGroup addAction(Action action, Constraints constraint) { return addAction(action, constraint, actionManager); } /** * Adds the specified action with the specified constraint. * * @param action * Action to be added; cannot be null * @param constraint * Constraint to be used for determining action's position; cannot be null * @throws IllegalArgumentException * in case when: * <li>action is null * <li>constraint is null * <li>action is already in the group */ public final void add(Action action, Constraints constraint, ActionManager actionManager) { addAction(action, constraint, actionManager); } public final ActionInGroup addAction(Action action, Constraints constraint, ActionManager actionManager) { if (action == this) { throw new IllegalArgumentException("Cannot add a group to itself"); } // Check that action isn't already registered if (!(action instanceof Separator)) { if (mySortedChildren.contains(action)) { throw new IllegalArgumentException("cannot add an action twice: " + action); } for (Pair<Action, Constraints> pair : myPairs) { if (action.equals(pair.first)) { throw new IllegalArgumentException("cannot add an action twice: " + action); } } } constraint = constraint.clone(); if (constraint.myAnchor == Anchor.FIRST) { mySortedChildren.add(0, action); } else if (constraint.myAnchor == Anchor.LAST) { mySortedChildren.add(action); } else { if (addToSortedList(action, constraint, actionManager)) { actionAdded(action, actionManager); } else { myPairs.add(new Pair<>(action, constraint)); } } return new ActionInGroup(this, action); } private void actionAdded(Action addedAction, ActionManager actionManager) { String addedActionId = actionManager.getId(addedAction); if (addedActionId == null) { return; } outer: while (!myPairs.isEmpty()) { for (int i = 0; i < myPairs.size(); i++) { Pair<Action, Constraints> pair = myPairs.get(i); if (addToSortedList(pair.first, pair.second, actionManager)) { myPairs.remove(i); continue outer; } } break; } } private boolean addToSortedList(Action action, Constraints constraint, ActionManager actionManager) { int index = findIndex(constraint.myRelativeToActionId, mySortedChildren, actionManager); if (index == -1) { return false; } if (constraint.myAnchor == Anchor.BEFORE) { mySortedChildren.add(index, action); } else { mySortedChildren.add(index + 1, action); } return true; } /** * Removes specified action from group. * * @param action * Action to be removed */ public final void remove(Action action) { if (!mySortedChildren.remove(action)) { for (int i = 0; i < myPairs.size(); i++) { Pair<Action, Constraints> pair = myPairs.get(i); if (pair.first.equals(action)) { myPairs.remove(i); break; } } } } /** Removes all children actions (separators as well) from the group. */ public final void removeAll() { mySortedChildren.clear(); myPairs.clear(); } /** * Returns group's children in the order determined by constraints. * * @param e * not used * @return An array of children actions */ @Override public final Action[] getChildren(ActionEvent e) { boolean hasNulls = false; // Mix sorted actions and pairs int sortedSize = mySortedChildren.size(); Action[] children = new Action[sortedSize + myPairs.size()]; for (int i = 0; i < sortedSize; i++) { Action action = mySortedChildren.get(i); if (action == null) { Log.error(getClass(), "Empty sorted child: " + this + ", " + getClass() + "; index=" + i); } hasNulls |= action == null; children[i] = action; } for (int i = 0; i < myPairs.size(); i++) { final Pair<Action, Constraints> pair = myPairs.get(i); Action action = pair.first; if (action == null) { Log.error(getClass(), "Empty pair child: " + this + ", " + getClass() + "; index=" + i); } hasNulls |= action == null; children[i + sortedSize] = action; } if (hasNulls) { return mapNotNull(children, new Action[0]); } return children; } private Action[] mapNotNull(Action[] arr, Action[] emptyArray) { List<Action> result = new ArrayList<>(arr.length); for (Action t : arr) { if (t != null) { result.add(t); } } return result.toArray(emptyArray); } /** * Returns the number of contained children (including separators). * * @return number of children in the group */ public final int getChildrenCount() { return mySortedChildren.size() + myPairs.size(); } public final Action[] getChildActionsOrStubs() { // Mix sorted actions and pairs int sortedSize = mySortedChildren.size(); Action[] children = new Action[sortedSize + myPairs.size()]; for (int i = 0; i < sortedSize; i++) { children[i] = mySortedChildren.get(i); } for (int i = 0; i < myPairs.size(); i++) { children[i + sortedSize] = myPairs.get(i).first; } return children; } public final void addAll(ActionGroup group) { for (Action each : group.getChildren(null)) { add(each); } } public final void addAll(Collection<Action> actionList) { for (Action each : actionList) { add(each); } } public final void addAll(Action... actions) { for (Action each : actions) { add(each); } } public void addSeparator(String separatorText) { add(new Separator(separatorText)); } }