///*******************************************************************************
// * Copyright (c) 2004, 2007 IBM Corporation and others.
// * 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:
// * IBM Corporation - initial API and implementation
// *******************************************************************************/
//package org.eclipse.jface.bindings;
//
//import java.io.BufferedWriter;
//import java.io.IOException;
//import java.io.StringWriter;
//import java.util.ArrayList;
//import java.util.Arrays;
//import java.util.Collection;
//import java.util.Collections;
//import java.util.HashMap;
//import java.util.HashSet;
//import java.util.Iterator;
//import java.util.List;
//import java.util.Locale;
//import java.util.Map;
//import java.util.Set;
//import java.util.StringTokenizer;
//
//import org.eclipse.core.commands.CommandManager;
//import org.eclipse.core.commands.ParameterizedCommand;
//import org.eclipse.core.commands.common.HandleObjectManager;
//import org.eclipse.core.commands.common.NotDefinedException;
//import org.eclipse.core.commands.contexts.Context;
//import org.eclipse.core.commands.contexts.ContextManager;
//import org.eclipse.core.commands.contexts.ContextManagerEvent;
//import org.eclipse.core.commands.contexts.IContextManagerListener;
//import org.eclipse.core.commands.util.Tracing;
//import org.eclipse.core.runtime.IStatus;
//import org.eclipse.core.runtime.MultiStatus;
//import org.eclipse.core.runtime.Status;
//import org.eclipse.jface.bindings.keys.IKeyLookup;
//import org.eclipse.jface.bindings.keys.KeyLookupFactory;
//import org.eclipse.jface.bindings.keys.KeyStroke;
//import org.eclipse.jface.contexts.IContextIds;
//import org.eclipse.jface.util.Policy;
//import org.eclipse.jface.util.Util;
//
///**
// * <p>
// * A central repository for bindings -- both in the defined and undefined
// * states. Schemes and bindings can be created and retrieved using this manager.
// * It is possible to listen to changes in the collection of schemes and bindings
// * by adding a listener to the manager.
// * </p>
// * <p>
// * The binding manager is very sensitive to performance. Misusing the manager
// * can render an application unenjoyable to use. As such, each of the public
// * methods states the current run-time performance. In future releases, it is
// * guaranteed that the method will run in at least the stated time constraint --
// * though it might get faster. Where possible, we have also tried to be memory
// * efficient.
// * </p>
// *
// * @since 1.0
// */
//public final class BindingManager extends HandleObjectManager implements
// IContextManagerListener, ISchemeListener {
//
// /**
// * This flag can be set to <code>true</code> if the binding manager should
// * print information to <code>System.out</code> when certain boundary
// * conditions occur.
// */
// public static boolean DEBUG = false;
//
// /**
// * Returned for optimized lookup.
// */
// private static final TriggerSequence[] EMPTY_TRIGGER_SEQUENCE = new TriggerSequence[0];
//
// /**
// * The separator character used in locales.
// */
// private static final String LOCALE_SEPARATOR = "_"; //$NON-NLS-1$
//
// /**
// * </p>
// * A utility method for adding entries to a map. The map is checked for
// * entries at the key. If such an entry exists, it is expected to be a
// * <code>Collection</code>. The value is then appended to the collection.
// * If no such entry exists, then a collection is created, and the value
// * added to the collection.
// * </p>
// *
// * @param map
// * The map to modify; if this value is <code>null</code>, then
// * this method simply returns.
// * @param key
// * The key to look up in the map; may be <code>null</code>.
// * @param value
// * The value to look up in the map; may be <code>null</code>.
// */
// private static final void addReverseLookup(final Map map, final Object key,
// final Object value) {
// if (map == null) {
// return;
// }
//
// final Object currentValue = map.get(key);
// if (currentValue != null) {
// final Collection values = (Collection) currentValue;
// values.add(value);
// } else { // currentValue == null
// final Collection values = new ArrayList(1);
// values.add(value);
// map.put(key, values);
// }
// }
//
// /**
// * <p>
// * Takes a fully-specified string, and converts it into an array of
// * increasingly less-specific strings. So, for example, "en_GB" would become
// * ["en_GB", "en", "", null].
// * </p>
// * <p>
// * This method runs in linear time (O(n)) over the length of the string.
// * </p>
// *
// * @param string
// * The string to break apart into its less specific components;
// * should not be <code>null</code>.
// * @param separator
// * The separator that indicates a separation between a degrees of
// * specificity; should not be <code>null</code>.
// * @return An array of strings from the most specific (i.e.,
// * <code>string</code>) to the least specific (i.e.,
// * <code>null</code>).
// */
// private static final String[] expand(String string, final String separator) {
// // Test for boundary conditions.
// if (string == null || separator == null) {
// return new String[0];
// }
//
// final List strings = new ArrayList();
// final StringBuffer stringBuffer = new StringBuffer();
// string = string.trim(); // remove whitespace
// if (string.length() > 0) {
// final StringTokenizer stringTokenizer = new StringTokenizer(string,
// separator);
// while (stringTokenizer.hasMoreElements()) {
// if (stringBuffer.length() > 0) {
// stringBuffer.append(separator);
// }
// stringBuffer.append(((String) stringTokenizer.nextElement())
// .trim());
// strings.add(stringBuffer.toString());
// }
// }
// Collections.reverse(strings);
// strings.add(Util.ZERO_LENGTH_STRING);
// strings.add(null);
// return (String[]) strings.toArray(new String[strings.size()]);
// }
//
// /**
// * The active bindings. This is a map of triggers (
// * <code>TriggerSequence</code>) to bindings (<code>Binding</code>).
// * This value will only be <code>null</code> if the active bindings have
// * not yet been computed. Otherwise, this value may be empty.
// */
// private Map activeBindings = null;
//
// /**
// * The active bindings indexed by fully-parameterized commands. This is a
// * map of fully-parameterized commands (<code>ParameterizedCommand</code>)
// * to triggers ( <code>TriggerSequence</code>). This value will only be
// * <code>null</code> if the active bindings have not yet been computed.
// * Otherwise, this value may be empty.
// */
// private Map activeBindingsByParameterizedCommand = null;
//
// /**
// * The scheme that is currently active. An active scheme is the one that is
// * currently dictating which bindings will actually work. This value may be
// * <code>null</code> if there is no active scheme. If the active scheme
// * becomes undefined, then this should automatically revert to
// * <code>null</code>.
// */
// private Scheme activeScheme = null;
//
// /**
// * The array of scheme identifiers, starting with the active scheme and
// * moving up through its parents. This value may be <code>null</code> if
// * there is no active scheme.
// */
// private String[] activeSchemeIds = null;
//
// /**
// * The number of bindings in the <code>bindings</code> array.
// */
// private int bindingCount = 0;
//
// /**
// * A cache of context IDs that weren't defined.
// */
// private Set bindingErrors = new HashSet();
//
// /**
// * The array of all bindings currently handled by this manager. This array
// * is the raw list of bindings, as provided to this manager. This value may
// * be <code>null</code> if there are no bindings. The size of this array
// * is not necessarily the number of bindings.
// */
// private Binding[] bindings = null;
//
// /**
// * A cache of the bindings previously computed by this manager. This value
// * may be empty, but it is never <code>null</code>. This is a map of
// * <code>CachedBindingSet</code> to <code>CachedBindingSet</code>.
// */
// private Map cachedBindings = new HashMap();
//
// /**
// * The command manager for this binding manager. This manager is only needed
// * for the <code>getActiveBindingsFor(String)</code> method. This value is
// * guaranteed to never be <code>null</code>.
// */
// private final CommandManager commandManager;
//
// /**
// * The context manager for this binding manager. For a binding manager to
// * function, it needs to listen for changes to the contexts. This value is
// * guaranteed to never be <code>null</code>.
// */
// private final ContextManager contextManager;
//
// /**
// * The locale for this manager. This defaults to the current locale. The
// * value will never be <code>null</code>.
// */
// private String locale = Locale.getDefault().toString();
//
// /**
// * The array of locales, starting with the active locale and moving up
// * through less specific representations of the locale. For example,
// * ["en_US", "en", "", null]. This value will never be <code>null</code>.
// */
// private String[] locales = expand(locale, LOCALE_SEPARATOR);
//
// /**
// * The platform for this manager. This defaults to the current platform. The
// * value will never be <code>null</code>.
// */
//// private String platform = SWT.getPlatform();
// private String platform = ""; //$NON-NLS-1$
//
// /**
// * The array of platforms, starting with the active platform and moving up
// * through less specific representations of the platform. For example,
// * ["gtk", "", null]. This value will never be <code>null,/code>.
// */
// private String[] platforms = expand(platform, Util.ZERO_LENGTH_STRING);
//
// /**
// * A map of prefixes (<code>TriggerSequence</code>) to a map of
// * available completions (possibly <code>null</code>, which means there
// * is an exact match). The available completions is a map of trigger (<code>TriggerSequence</code>)
// * to bindings (<code>Binding</code>). This value may be
// * <code>null</code> if there is no existing solution.
// */
// private Map prefixTable = null;
//
// private Set triggerConflicts = new HashSet();
//
// /**
// * <p>
// * Constructs a new instance of <code>BindingManager</code>.
// * </p>
// * <p>
// * This method completes in amortized constant time (O(1)).
// * </p>
// *
// * @param contextManager
// * The context manager that will support this binding manager.
// * This value must not be <code>null</code>.
// * @param commandManager
// * The command manager that will support this binding manager.
// * This value must not be <code>null</code>.
// */
// public BindingManager(final ContextManager contextManager,
// final CommandManager commandManager) {
// if (contextManager == null) {
// throw new NullPointerException(
// "A binding manager requires a context manager"); //$NON-NLS-1$
// }
//
// if (commandManager == null) {
// throw new NullPointerException(
// "A binding manager requires a command manager"); //$NON-NLS-1$
// }
//
// this.contextManager = contextManager;
// contextManager.addContextManagerListener(this);
// this.commandManager = commandManager;
// }
//
// /**
// * <p>
// * Adds a single new binding to the existing array of bindings. If the array
// * is currently <code>null</code>, then a new array is created and this
// * binding is added to it. This method does not detect duplicates.
// * </p>
// * <p>
// * This method completes in amortized <code>O(1)</code>.
// * </p>
// *
// * @param binding
// * The binding to be added; must not be <code>null</code>.
// */
// public final void addBinding(final Binding binding) {
// if (binding == null) {
// throw new NullPointerException("Cannot add a null binding"); //$NON-NLS-1$
// }
//
// if (bindings == null) {
// bindings = new Binding[1];
// } else if (bindingCount >= bindings.length) {
// final Binding[] oldBindings = bindings;
// bindings = new Binding[oldBindings.length * 2];
// System.arraycopy(oldBindings, 0, bindings, 0, oldBindings.length);
// }
// bindings[bindingCount++] = binding;
// clearCache();
// }
//
// /**
// * <p>
// * Adds a listener to this binding manager. The listener will be notified
// * when the set of defined schemes or bindings changes. This can be used to
// * track the global appearance and disappearance of bindings.
// * </p>
// * <p>
// * This method completes in amortized constant time (<code>O(1)</code>).
// * </p>
// *
// * @param listener
// * The listener to attach; must not be <code>null</code>.
// */
// public final void addBindingManagerListener(
// final IBindingManagerListener listener) {
// addListenerObject(listener);
// }
//
// /**
// * <p>
// * Builds a prefix table look-up for a map of active bindings.
// * </p>
// * <p>
// * This method takes <code>O(mn)</code>, where <code>m</code> is the
// * length of the trigger sequences and <code>n</code> is the number of
// * bindings.
// * </p>
// *
// * @param activeBindings
// * The map of triggers (<code>TriggerSequence</code>) to
// * command ids (<code>String</code>) which are currently
// * active. This value may be <code>null</code> if there are no
// * active bindings, and it may be empty. It must not be
// * <code>null</code>.
// * @return A map of prefixes (<code>TriggerSequence</code>) to a map of
// * available completions (possibly <code>null</code>, which means
// * there is an exact match). The available completions is a map of
// * trigger (<code>TriggerSequence</code>) to command identifier (<code>String</code>).
// * This value will never be <code>null</code>, but may be empty.
// */
// private final Map buildPrefixTable(final Map activeBindings) {
// final Map prefixTable = new HashMap();
//
// final Iterator bindingItr = activeBindings.entrySet().iterator();
// while (bindingItr.hasNext()) {
// final Map.Entry entry = (Map.Entry) bindingItr.next();
// final TriggerSequence triggerSequence = (TriggerSequence) entry
// .getKey();
//
// // Add the perfect match.
// if (!prefixTable.containsKey(triggerSequence)) {
// prefixTable.put(triggerSequence, null);
// }
//
// final TriggerSequence[] prefixes = triggerSequence.getPrefixes();
// final int prefixesLength = prefixes.length;
// if (prefixesLength == 0) {
// continue;
// }
//
// // Break apart the trigger sequence.
// final Binding binding = (Binding) entry.getValue();
// for (int i = 0; i < prefixesLength; i++) {
// final TriggerSequence prefix = prefixes[i];
// final Object value = prefixTable.get(prefix);
// if ((prefixTable.containsKey(prefix)) && (value instanceof Map)) {
// ((Map) value).put(triggerSequence, binding);
// } else {
// final Map map = new HashMap();
// prefixTable.put(prefix, map);
// map.put(triggerSequence, binding);
// }
// }
// }
//
// return prefixTable;
// }
//
// /**
// * <p>
// * Clears the cache, and the existing solution. If debugging is turned on,
// * then this will also print a message to standard out.
// * </p>
// * <p>
// * This method completes in <code>O(1)</code>.
// * </p>
// */
// private final void clearCache() {
// if (DEBUG) {
// Tracing.printTrace("BINDINGS", "Clearing cache"); //$NON-NLS-1$ //$NON-NLS-2$
// }
// cachedBindings.clear();
// clearSolution();
// }
//
// /**
// * <p>
// * Clears the existing solution.
// * </p>
// * <p>
// * This method completes in <code>O(1)</code>.
// */
// private final void clearSolution() {
// setActiveBindings(null, null, null);
// }
//
// /**
// * Compares the identifier of two schemes, and decides which scheme is the
// * youngest (i.e., the child) of the two. Both schemes should be active
// * schemes.
// *
// * @param schemeId1
// * The identifier of the first scheme; must not be
// * <code>null</code>.
// * @param schemeId2
// * The identifier of the second scheme; must not be
// * <code>null</code>.
// * @return <code>0</code> if the two schemes are equal of if neither
// * scheme is active; <code>1</code> if the second scheme is the
// * youngest; and <code>-1</code> if the first scheme is the
// * youngest.
// * @since 1.0
// */
// private final int compareSchemes(final String schemeId1,
// final String schemeId2) {
// if (!schemeId2.equals(schemeId1)) {
// for (int i = 0; i < activeSchemeIds.length; i++) {
// final String schemePointer = activeSchemeIds[i];
// if (schemeId2.equals(schemePointer)) {
// return 1;
//
// } else if (schemeId1.equals(schemePointer)) {
// return -1;
//
// }
//
// }
// }
//
// return 0;
// }
//
// /**
// * <p>
// * Computes the bindings given the context tree, and inserts them into the
// * <code>commandIdsByTrigger</code>. It is assumed that
// * <code>locales</code>,<code>platforsm</code> and
// * <code>schemeIds</code> correctly reflect the state of the application.
// * This method does not deal with caching.
// * </p>
// * <p>
// * This method completes in <code>O(n)</code>, where <code>n</code> is
// * the number of bindings.
// * </p>
// *
// * @param activeContextTree
// * The map representing the tree of active contexts. The map is
// * one of child to parent, each being a context id (
// * <code>String</code>). The keys are never <code>null</code>,
// * but the values may be (i.e., no parent). This map may be
// * empty. It may be <code>null</code> if we shouldn't consider
// * contexts.
// * @param bindingsByTrigger
// * The empty of map that is intended to be filled with triggers (
// * <code>TriggerSequence</code>) to bindings (
// * <code>Binding</code>). This value must not be
// * <code>null</code> and must be empty.
// * @param triggersByCommandId
// * The empty of map that is intended to be filled with command
// * identifiers (<code>String</code>) to triggers (
// * <code>TriggerSequence</code>). This value must either be
// * <code>null</code> (indicating that these values are not
// * needed), or empty (indicating that this map should be
// * computed).
// */
// private final void computeBindings(final Map activeContextTree,
// final Map bindingsByTrigger, final Map triggersByCommandId) {
// /*
// * FIRST PASS: Remove all of the bindings that are marking deletions.
// */
// final Binding[] trimmedBindings = removeDeletions(bindings);
//
// /*
// * SECOND PASS: Just throw in bindings that match the current state. If
// * there is more than one match for a binding, then create a list.
// */
// final Map possibleBindings = new HashMap();
// final int length = trimmedBindings.length;
// for (int i = 0; i < length; i++) {
// final Binding binding = trimmedBindings[i];
// boolean found;
//
// // Check the context.
// final String contextId = binding.getContextId();
// if ((activeContextTree != null)
// && (!activeContextTree.containsKey(contextId))) {
// continue;
// }
//
// // Check the locale.
// if (!localeMatches(binding)) {
// continue;
// }
//
// // Check the platform.
// if (!platformMatches(binding)) {
// continue;
// }
//
// // Check the scheme ids.
// final String schemeId = binding.getSchemeId();
// found = false;
// if (activeSchemeIds != null) {
// for (int j = 0; j < activeSchemeIds.length; j++) {
// if (Util.equals(schemeId, activeSchemeIds[j])) {
// found = true;
// break;
// }
// }
// }
// if (!found) {
// continue;
// }
//
// // Insert the match into the list of possible matches.
// final TriggerSequence trigger = binding.getTriggerSequence();
// final Object existingMatch = possibleBindings.get(trigger);
// if (existingMatch instanceof Binding) {
// possibleBindings.remove(trigger);
// final Collection matches = new ArrayList();
// matches.add(existingMatch);
// matches.add(binding);
// possibleBindings.put(trigger, matches);
//
// } else if (existingMatch instanceof Collection) {
// final Collection matches = (Collection) existingMatch;
// matches.add(binding);
//
// } else {
// possibleBindings.put(trigger, binding);
// }
// }
//
// MultiStatus conflicts = new MultiStatus("org.eclipse.jface", 0, //$NON-NLS-1$
// "Keybinding conflicts occurred. They may interfere with normal accelerator operation.", //$NON-NLS-1$
// null);
// /*
// * THIRD PASS: In this pass, we move any non-conflicting bindings
// * directly into the map. In the case of conflicts, we apply some
// * further logic to try to resolve them. If the conflict can't be
// * resolved, then we log the problem.
// */
// final Iterator possibleBindingItr = possibleBindings.entrySet()
// .iterator();
// while (possibleBindingItr.hasNext()) {
// final Map.Entry entry = (Map.Entry) possibleBindingItr.next();
// final TriggerSequence trigger = (TriggerSequence) entry.getKey();
// final Object match = entry.getValue();
// /*
// * What we do depends slightly on whether we are trying to build a
// * list of all possible bindings (disregarding context), or a flat
// * map given the currently active contexts.
// */
// if (activeContextTree == null) {
// // We are building the list of all possible bindings.
// final Collection bindings = new ArrayList();
// if (match instanceof Binding) {
// bindings.add(match);
// bindingsByTrigger.put(trigger, bindings);
// addReverseLookup(triggersByCommandId, ((Binding) match)
// .getParameterizedCommand(), trigger);
//
// } else if (match instanceof Collection) {
// bindings.addAll((Collection) match);
// bindingsByTrigger.put(trigger, bindings);
//
// final Iterator matchItr = bindings.iterator();
// while (matchItr.hasNext()) {
// addReverseLookup(triggersByCommandId,
// ((Binding) matchItr.next())
// .getParameterizedCommand(), trigger);
// }
// }
//
// } else {
// // We are building the flat map of trigger to commands.
// if (match instanceof Binding) {
// final Binding binding = (Binding) match;
// bindingsByTrigger.put(trigger, binding);
// addReverseLookup(triggersByCommandId, binding
// .getParameterizedCommand(), trigger);
//
// } else if (match instanceof Collection) {
// final Binding winner = resolveConflicts((Collection) match,
// activeContextTree);
// if (winner == null) {
// // warn once ... so as not to flood the logs
// if (triggerConflicts.add(trigger)) {
// final StringWriter sw = new StringWriter();
// final BufferedWriter buffer = new BufferedWriter(sw);
// try {
// buffer.write("A conflict occurred for "); //$NON-NLS-1$
// buffer.write(trigger.toString());
// buffer.write(':');
// Iterator i = ((Collection) match).iterator();
// while (i.hasNext()) {
// buffer.newLine();
// buffer.write(i.next().toString());
// }
// buffer.flush();
// } catch (IOException e) {
// // we should not get this
// }
// conflicts.add(new Status(IStatus.WARNING,
// "org.eclipse.jface", //$NON-NLS-1$
// sw.toString()));
// }
// if (DEBUG) {
// Tracing.printTrace("BINDINGS", //$NON-NLS-1$
// "A conflict occurred for " + trigger); //$NON-NLS-1$
// Tracing.printTrace("BINDINGS", " " + match); //$NON-NLS-1$ //$NON-NLS-2$
// }
// } else {
// bindingsByTrigger.put(trigger, winner);
// addReverseLookup(triggersByCommandId, winner
// .getParameterizedCommand(), trigger);
// }
// }
// }
// }
// if (conflicts.getSeverity() != IStatus.OK) {
// Policy.getLog().log(conflicts);
// }
// }
//
// /**
// * <p>
// * Notifies this manager that the context manager has changed. This method
// * is intended for internal use only.
// * </p>
// * <p>
// * This method completes in <code>O(1)</code>.
// * </p>
// */
// public final void contextManagerChanged(
// final ContextManagerEvent contextManagerEvent) {
// if (contextManagerEvent.isActiveContextsChanged()) {
//// clearSolution();
// recomputeBindings();
// }
// }
//
// /**
// * Returns the number of strokes in an array of triggers. It is assumed that
// * there is one natural key per trigger. The strokes are counted based on
// * the type of key. Natural keys are worth one; ctrl is worth two; shift is
// * worth four; and alt is worth eight.
// *
// * @param triggers
// * The triggers on which to count strokes; must not be
// * <code>null</code>.
// * @return The value of the strokes in the triggers.
// * @since 1.0
// */
// private final int countStrokes(final Trigger[] triggers) {
// int strokeCount = triggers.length;
// for (int i = 0; i < triggers.length; i++) {
// final Trigger trigger = triggers[i];
// if (trigger instanceof KeyStroke) {
// final KeyStroke keyStroke = (KeyStroke) trigger;
// final int modifierKeys = keyStroke.getModifierKeys();
// final IKeyLookup lookup = KeyLookupFactory.getDefault();
// if ((modifierKeys & lookup.getAlt()) != 0) {
// strokeCount += 8;
// }
// if ((modifierKeys & lookup.getCtrl()) != 0) {
// strokeCount += 2;
// }
// if ((modifierKeys & lookup.getShift()) != 0) {
// strokeCount += 4;
// }
// if ((modifierKeys & lookup.getCommand()) != 0) {
// strokeCount += 2;
// }
// } else {
// strokeCount += 99;
// }
// }
//
// return strokeCount;
// }
//
// /**
// * <p>
// * Creates a tree of context identifiers, representing the hierarchical
// * structure of the given contexts. The tree is structured as a mapping from
// * child to parent.
// * </p>
// * <p>
// * This method completes in <code>O(n)</code>, where <code>n</code> is
// * the height of the context tree.
// * </p>
// *
// * @param contextIds
// * The set of context identifiers to be converted into a tree;
// * must not be <code>null</code>.
// * @return The tree of contexts to use; may be empty, but never
// * <code>null</code>. The keys and values are both strings.
// */
// private final Map createContextTreeFor(final Set contextIds) {
// final Map contextTree = new HashMap();
//
// final Iterator contextIdItr = contextIds.iterator();
// while (contextIdItr.hasNext()) {
// String childContextId = (String) contextIdItr.next();
// while (childContextId != null) {
// // Check if we've already got the part of the tree from here up.
// if (contextTree.containsKey(childContextId)) {
// break;
// }
//
// // Retrieve the context.
// final Context childContext = contextManager
// .getContext(childContextId);
//
// // Add the child-parent pair to the tree.
// try {
// final String parentContextId = childContext.getParentId();
// contextTree.put(childContextId, parentContextId);
// childContextId = parentContextId;
// } catch (final NotDefinedException e) {
// break; // stop ascending
// }
// }
// }
//
// return contextTree;
// }
//
// /**
// * <p>
// * Creates a tree of context identifiers, representing the hierarchical
// * structure of the given contexts. The tree is structured as a mapping from
// * child to parent. In this tree, the key binding specific filtering of
// * contexts will have taken place.
// * </p>
// * <p>
// * This method completes in <code>O(n^2)</code>, where <code>n</code>
// * is the height of the context tree.
// * </p>
// *
// * @param contextIds
// * The set of context identifiers to be converted into a tree;
// * must not be <code>null</code>.
// * @return The tree of contexts to use; may be empty, but never
// * <code>null</code>. The keys and values are both strings.
// */
// private final Map createFilteredContextTreeFor(final Set contextIds) {
// // Check to see whether a dialog or window is active.
// boolean dialog = false;
// boolean window = false;
// Iterator contextIdItr = contextIds.iterator();
// while (contextIdItr.hasNext()) {
// final String contextId = (String) contextIdItr.next();
// if (IContextIds.CONTEXT_ID_DIALOG.equals(contextId)) {
// dialog = true;
// continue;
// }
// if (IContextIds.CONTEXT_ID_WINDOW.equals(contextId)) {
// window = true;
// continue;
// }
// }
//
// /*
// * Remove all context identifiers for contexts whose parents are dialog
// * or window, and the corresponding dialog or window context is not
// * active.
// */
// contextIdItr = contextIds.iterator();
// while (contextIdItr.hasNext()) {
// String contextId = (String) contextIdItr.next();
// Context context = contextManager.getContext(contextId);
// try {
// String parentId = context.getParentId();
// while (parentId != null) {
// if (IContextIds.CONTEXT_ID_DIALOG.equals(parentId)) {
// if (!dialog) {
// contextIdItr.remove();
// }
// break;
// }
// if (IContextIds.CONTEXT_ID_WINDOW.equals(parentId)) {
// if (!window) {
// contextIdItr.remove();
// }
// break;
// }
// if (IContextIds.CONTEXT_ID_DIALOG_AND_WINDOW
// .equals(parentId)) {
// if ((!window) && (!dialog)) {
// contextIdItr.remove();
// }
// break;
// }
//
// context = contextManager.getContext(parentId);
// parentId = context.getParentId();
// }
// } catch (NotDefinedException e) {
// // since this context was part of an undefined hierarchy,
// // I'm going to yank it out as a bad bet
// contextIdItr.remove();
//
// // This is a logging optimization, only log the error once.
// if (context == null || !bindingErrors.contains(context.getId())) {
// if (context != null) {
// bindingErrors.add(context.getId());
// }
//
// // now log like you've never logged before!
// Policy
// .getLog()
// .log(
// new Status(
// IStatus.ERROR,
// Policy.JFACE,
// IStatus.OK,
// "Undefined context while filtering dialog/window contexts", //$NON-NLS-1$
// e));
// }
// }
// }
//
// return createContextTreeFor(contextIds);
// }
//
// /**
// * <p>
// * Notifies all of the listeners to this manager that the defined or active
// * schemes of bindings have changed.
// * </p>
// * <p>
// * The time this method takes to complete is dependent on external
// * listeners.
// * </p>
// *
// * @param event
// * The event to send to all of the listeners; must not be
// * <code>null</code>.
// */
// private final void fireBindingManagerChanged(final BindingManagerEvent event) {
// if (event == null) {
// throw new NullPointerException();
// }
//
// final Object[] listeners = getListeners();
// for (int i = 0; i < listeners.length; i++) {
// final IBindingManagerListener listener = (IBindingManagerListener) listeners[i];
// listener.bindingManagerChanged(event);
// }
// }
//
// /**
// * <p>
// * Returns the active bindings. The caller must not modify the returned map.
// * </p>
// * <p>
// * This method completes in <code>O(1)</code>. If the active bindings are
// * not yet computed, then this completes in <code>O(nn)</code>, where
// * <code>n</code> is the number of bindings.
// * </p>
// *
// * @return The map of triggers (<code>TriggerSequence</code>) to
// * bindings (<code>Binding</code>) which are currently active.
// * This value may be <code>null</code> if there are no active
// * bindings, and it may be empty.
// */
// private final Map getActiveBindings() {
// if (activeBindings == null) {
// recomputeBindings();
// }
//
// return activeBindings;
// }
//
// /**
// * <p>
// * Returns the active bindings indexed by command identifier. The caller
// * must not modify the returned map.
// * </p>
// * <p>
// * This method completes in <code>O(1)</code>. If the active bindings are
// * not yet computed, then this completes in <code>O(nn)</code>, where
// * <code>n</code> is the number of bindings.
// * </p>
// *
// * @return The map of fully-parameterized commands (<code>ParameterizedCommand</code>)
// * to triggers (<code>TriggerSequence</code>) which are
// * currently active. This value may be <code>null</code> if there
// * are no active bindings, and it may be empty.
// */
// private final Map getActiveBindingsByParameterizedCommand() {
// if (activeBindingsByParameterizedCommand == null) {
// recomputeBindings();
// }
//
// return activeBindingsByParameterizedCommand;
// }
//
// /**
// * <p>
// * Computes the bindings for the current state of the application, but
// * disregarding the current contexts. This can be useful when trying to
// * display all the possible bindings.
// * </p>
// * <p>
// * This method completes in <code>O(n)</code>, where <code>n</code> is
// * the number of bindings.
// * </p>
// *
// * @return A map of trigger (<code>TriggerSequence</code>) to bindings (
// * <code>Collection</code> containing <code>Binding</code>).
// * This map may be empty, but it is never <code>null</code>.
// */
// public final Map getActiveBindingsDisregardingContext() {
// if (bindings == null) {
// // Not yet initialized. This is happening too early. Do nothing.
// return Collections.EMPTY_MAP;
// }
//
// // Build a cached binding set for that state.
// final CachedBindingSet bindingCache = new CachedBindingSet(null,
// locales, platforms, activeSchemeIds);
//
// /*
// * Check if the cached binding set already exists. If so, simply set the
// * active bindings and return.
// */
// CachedBindingSet existingCache = (CachedBindingSet) cachedBindings
// .get(bindingCache);
// if (existingCache == null) {
// existingCache = bindingCache;
// cachedBindings.put(existingCache, existingCache);
// }
// Map commandIdsByTrigger = existingCache.getBindingsByTrigger();
// if (commandIdsByTrigger != null) {
// if (DEBUG) {
// Tracing.printTrace("BINDINGS", "Cache hit"); //$NON-NLS-1$ //$NON-NLS-2$
// }
//
// return Collections.unmodifiableMap(commandIdsByTrigger);
// }
//
// // There is no cached entry for this.
// if (DEBUG) {
// Tracing.printTrace("BINDINGS", "Cache miss"); //$NON-NLS-1$ //$NON-NLS-2$
// }
//
// // Compute the active bindings.
// commandIdsByTrigger = new HashMap();
// final Map triggersByParameterizedCommand = new HashMap();
// computeBindings(null, commandIdsByTrigger,
// triggersByParameterizedCommand);
// existingCache.setBindingsByTrigger(commandIdsByTrigger);
// existingCache.setTriggersByCommandId(triggersByParameterizedCommand);
// return Collections.unmodifiableMap(commandIdsByTrigger);
// }
//
// /**
// * <p>
// * Computes the bindings for the current state of the application, but
// * disregarding the current contexts. This can be useful when trying to
// * display all the possible bindings.
// * </p>
// * <p>
// * This method completes in <code>O(n)</code>, where <code>n</code> is
// * the number of bindings.
// * </p>
// *
// * @return A map of trigger (<code>TriggerSequence</code>) to bindings (
// * <code>Collection</code> containing <code>Binding</code>).
// * This map may be empty, but it is never <code>null</code>.
// * @since 1.0
// */
// private final Map getActiveBindingsDisregardingContextByParameterizedCommand() {
// if (bindings == null) {
// // Not yet initialized. This is happening too early. Do nothing.
// return Collections.EMPTY_MAP;
// }
//
// // Build a cached binding set for that state.
// final CachedBindingSet bindingCache = new CachedBindingSet(null,
// locales, platforms, activeSchemeIds);
//
// /*
// * Check if the cached binding set already exists. If so, simply set the
// * active bindings and return.
// */
// CachedBindingSet existingCache = (CachedBindingSet) cachedBindings
// .get(bindingCache);
// if (existingCache == null) {
// existingCache = bindingCache;
// cachedBindings.put(existingCache, existingCache);
// }
// Map triggersByParameterizedCommand = existingCache
// .getTriggersByCommandId();
// if (triggersByParameterizedCommand != null) {
// if (DEBUG) {
// Tracing.printTrace("BINDINGS", "Cache hit"); //$NON-NLS-1$ //$NON-NLS-2$
// }
//
// return Collections.unmodifiableMap(triggersByParameterizedCommand);
// }
//
// // There is no cached entry for this.
// if (DEBUG) {
// Tracing.printTrace("BINDINGS", "Cache miss"); //$NON-NLS-1$ //$NON-NLS-2$
// }
//
// // Compute the active bindings.
// final Map commandIdsByTrigger = new HashMap();
// triggersByParameterizedCommand = new HashMap();
// computeBindings(null, commandIdsByTrigger,
// triggersByParameterizedCommand);
// existingCache.setBindingsByTrigger(commandIdsByTrigger);
// existingCache.setTriggersByCommandId(triggersByParameterizedCommand);
//
// return Collections.unmodifiableMap(triggersByParameterizedCommand);
// }
//
// /**
// * <p>
// * Computes the bindings for the current state of the application, but
// * disregarding the current contexts. This can be useful when trying to
// * display all the possible bindings.
// * </p>
// * <p>
// * This method completes in <code>O(n)</code>, where <code>n</code> is
// * the number of bindings.
// * </p>
// *
// * @return All of the active bindings (<code>Binding</code>), not sorted
// * in any fashion. This collection may be empty, but it is never
// * <code>null</code>.
// */
// public final Collection getActiveBindingsDisregardingContextFlat() {
// final Collection bindingCollections = getActiveBindingsDisregardingContext()
// .values();
// final Collection mergedBindings = new ArrayList();
// final Iterator bindingCollectionItr = bindingCollections.iterator();
// while (bindingCollectionItr.hasNext()) {
// final Collection bindingCollection = (Collection) bindingCollectionItr
// .next();
// if ((bindingCollection != null) && (!bindingCollection.isEmpty())) {
// mergedBindings.addAll(bindingCollection);
// }
// }
//
// return mergedBindings;
// }
//
// /**
// * <p>
// * Returns the active bindings for a particular command identifier, but
// * discounting the current contexts. This method operates in O(n) time over
// * the number of bindings.
// * </p>
// * <p>
// * This method completes in <code>O(1)</code>. If the active bindings are
// * not yet computed, then this completes in <code>O(nn)</code>, where
// * <code>n</code> is the number of bindings.
// * </p>
// *
// * @param parameterizedCommand
// * The fully-parameterized command whose bindings are requested.
// * This argument may be <code>null</code>.
// * @return The array of active triggers (<code>TriggerSequence</code>)
// * for a particular command identifier. This value is guaranteed to
// * never be <code>null</code>, but it may be empty.
// * @since 1.0
// */
// public final TriggerSequence[] getActiveBindingsDisregardingContextFor(
// final ParameterizedCommand parameterizedCommand) {
// final Object object = getActiveBindingsDisregardingContextByParameterizedCommand()
// .get(parameterizedCommand);
// if (object instanceof Collection) {
// final Collection collection = (Collection) object;
// return (TriggerSequence[]) collection
// .toArray(new TriggerSequence[collection.size()]);
// }
//
// return EMPTY_TRIGGER_SEQUENCE;
// }
//
// /**
// * <p>
// * Returns the active bindings for a particular command identifier. This
// * method operates in O(n) time over the number of bindings.
// * </p>
// * <p>
// * This method completes in <code>O(1)</code>. If the active bindings are
// * not yet computed, then this completes in <code>O(nn)</code>, where
// * <code>n</code> is the number of bindings.
// * </p>
// *
// * @param parameterizedCommand
// * The fully-parameterized command whose bindings are requested.
// * This argument may be <code>null</code>.
// * @return The array of active triggers (<code>TriggerSequence</code>)
// * for a particular command identifier. This value is guaranteed to
// * never be <code>null</code>, but it may be empty.
// */
// public final TriggerSequence[] getActiveBindingsFor(
// final ParameterizedCommand parameterizedCommand) {
// final Object object = getActiveBindingsByParameterizedCommand().get(
// parameterizedCommand);
// if (object instanceof Collection) {
// final Collection collection = (Collection) object;
// return (TriggerSequence[]) collection
// .toArray(new TriggerSequence[collection.size()]);
// }
//
// return EMPTY_TRIGGER_SEQUENCE;
// }
//
// /**
// * <p>
// * Returns the active bindings for a particular command identifier. This
// * method operates in O(n) time over the number of bindings.
// * </p>
// * <p>
// * This method completes in <code>O(1)</code>. If the active bindings are
// * not yet computed, then this completes in <code>O(nn)</code>, where
// * <code>n</code> is the number of bindings.
// * </p>
// *
// * @param commandId
// * The identifier of the command whose bindings are requested.
// * This argument may be <code>null</code>. It is assumed that
// * the command has no parameters.
// * @return The array of active triggers (<code>TriggerSequence</code>)
// * for a particular command identifier. This value is guaranteed not
// * to be <code>null</code>, but it may be empty.
// */
// public final TriggerSequence[] getActiveBindingsFor(final String commandId) {
// final ParameterizedCommand parameterizedCommand = new ParameterizedCommand(
// commandManager.getCommand(commandId), null);
// final Object object = getActiveBindingsByParameterizedCommand().get(
// parameterizedCommand);
// if (object instanceof Collection) {
// final Collection collection = (Collection) object;
// return (TriggerSequence[]) collection
// .toArray(new TriggerSequence[collection.size()]);
// }
//
// return EMPTY_TRIGGER_SEQUENCE;
// }
//
// /**
// * A variation on {@link BindingManager#getActiveBindingsFor(String)} that
// * returns an array of bindings, rather than trigger sequences. This method
// * is needed for doing "best" calculations on the active bindings.
// *
// * @param commandId
// * The identifier of the command for which the active bindings
// * should be retrieved; must not be <code>null</code>.
// * @return The active bindings for the given command; this value may be
// * <code>null</code> if there are no active bindings.
// * @since 1.0
// */
// private final Binding[] getActiveBindingsFor1(final String commandId) {
// final TriggerSequence[] triggers = getActiveBindingsFor(commandId);
// if (triggers.length == 0) {
// return null;
// }
//
// final Map activeBindings = getActiveBindings();
// if (activeBindings != null) {
// final Binding[] bindings = new Binding[triggers.length];
// for (int i = 0; i < triggers.length; i++) {
// final TriggerSequence triggerSequence = triggers[i];
// final Object object = activeBindings.get(triggerSequence);
// final Binding binding = (Binding) object;
// bindings[i] = binding;
// }
// return bindings;
// }
//
// return null;
// }
//
// /**
// * <p>
// * Gets the currently active scheme.
// * </p>
// * <p>
// * This method completes in <code>O(1)</code>.
// * </p>
// *
// * @return The active scheme; may be <code>null</code> if there is no
// * active scheme. If a scheme is returned, it is guaranteed to be
// * defined.
// */
// public final Scheme getActiveScheme() {
// return activeScheme;
// }
//
// /**
// * Gets the best active binding for a command. The best binding is the one
// * that would be most appropriate to show in a menu. Bindings which belong
// * to a child scheme are given preference over those in a parent scheme.
// * Bindings which belong to a particular locale or platform are given
// * preference over those that do not. The rest of the calculaton is based
// * most on various concepts of "length", as well as giving some modifier
// * keys preference (e.g., <code>Alt</code> is less likely to appear than
// * <code>Ctrl</code>).
// *
// * @param commandId
// * The identifier of the command for which the best active
// * binding should be retrieved; must not be <code>null</code>.
// * @return The trigger sequence for the best binding; may be
// * <code>null</code> if no bindings are active for the given
// * command.
// * @since 1.0
// */
// public final TriggerSequence getBestActiveBindingFor(final String commandId) {
// final Binding[] bindings = getActiveBindingsFor1(commandId);
// if ((bindings == null) || (bindings.length == 0)) {
// return null;
// }
//
// Binding bestBinding = bindings[0];
// int compareTo;
// for (int i = 1; i < bindings.length; i++) {
// final Binding currentBinding = bindings[i];
//
// // Bindings in a child scheme are always given preference.
// final String bestSchemeId = bestBinding.getSchemeId();
// final String currentSchemeId = currentBinding.getSchemeId();
// compareTo = compareSchemes(bestSchemeId, currentSchemeId);
// if (compareTo > 0) {
// bestBinding = currentBinding;
// }
// if (compareTo != 0) {
// continue;
// }
//
// /*
// * Bindings with a locale are given preference over those that do
// * not.
// */
// final String bestLocale = bestBinding.getLocale();
// final String currentLocale = currentBinding.getLocale();
// if ((bestLocale == null) && (currentLocale != null)) {
// bestBinding = currentBinding;
// }
// if (!(Util.equals(bestLocale, currentLocale))) {
// continue;
// }
//
// /*
// * Bindings with a platform are given preference over those that do
// * not.
// */
// final String bestPlatform = bestBinding.getPlatform();
// final String currentPlatform = currentBinding.getPlatform();
// if ((bestPlatform == null) && (currentPlatform != null)) {
// bestBinding = currentBinding;
// }
// if (!(Util.equals(bestPlatform, currentPlatform))) {
// continue;
// }
//
// /*
// * Check to see which has the least number of triggers in the
// * trigger sequence.
// */
// final TriggerSequence bestTriggerSequence = bestBinding
// .getTriggerSequence();
// final TriggerSequence currentTriggerSequence = currentBinding
// .getTriggerSequence();
// final Trigger[] bestTriggers = bestTriggerSequence.getTriggers();
// final Trigger[] currentTriggers = currentTriggerSequence
// .getTriggers();
// compareTo = bestTriggers.length - currentTriggers.length;
// if (compareTo > 0) {
// bestBinding = currentBinding;
// }
// if (compareTo != 0) {
// continue;
// }
//
// /*
// * Compare the number of keys pressed in each trigger sequence. Some
// * types of keys count less than others (i.e., some types of
// * modifiers keys are less likely to be chosen).
// */
// compareTo = countStrokes(bestTriggers)
// - countStrokes(currentTriggers);
// if (compareTo > 0) {
// bestBinding = currentBinding;
// }
// if (compareTo != 0) {
// continue;
// }
//
// // If this is still a tie, then just chose the shortest text.
// compareTo = bestTriggerSequence.format().length()
// - currentTriggerSequence.format().length();
// if (compareTo > 0) {
// bestBinding = currentBinding;
// }
// }
//
// return bestBinding.getTriggerSequence();
// }
//
// /**
// * Gets the formatted string representing the best active binding for a
// * command. The best binding is the one that would be most appropriate to
// * show in a menu. Bindings which belong to a child scheme are given
// * preference over those in a parent scheme. The rest of the calculaton is
// * based most on various concepts of "length", as well as giving some
// * modifier keys preference (e.g., <code>Alt</code> is less likely to
// * appear than <code>Ctrl</code>).
// *
// * @param commandId
// * The identifier of the command for which the best active
// * binding should be retrieved; must not be <code>null</code>.
// * @return The formatted string for the best binding; may be
// * <code>null</code> if no bindings are active for the given
// * command.
// * @since 1.0
// */
// public final String getBestActiveBindingFormattedFor(final String commandId) {
// final TriggerSequence binding = getBestActiveBindingFor(commandId);
// if (binding != null) {
// return binding.format();
// }
//
// return null;
// }
//
// /**
// * <p>
// * Returns the set of all bindings managed by this class.
// * </p>
// * <p>
// * This method completes in <code>O(1)</code>.
// * </p>
// *
// * @return The array of all bindings. This value may be <code>null</code>
// * and it may be empty.
// */
// public final Binding[] getBindings() {
// if (bindings == null) {
// return null;
// }
//
// final Binding[] returnValue = new Binding[bindingCount];
// System.arraycopy(bindings, 0, returnValue, 0, bindingCount);
// return returnValue;
// }
//
// /**
// * <p>
// * Returns the array of schemes that are defined.
// * </p>
// * <p>
// * This method completes in <code>O(1)</code>.
// * </p>
// *
// * @return The array of defined schemes; this value may be empty or
// * <code>null</code>.
// */
// public final Scheme[] getDefinedSchemes() {
// return (Scheme[]) definedHandleObjects
// .toArray(new Scheme[definedHandleObjects.size()]);
// }
//
// /**
// * <p>
// * Returns the active locale for this binding manager. The locale is in the
// * same format as <code>Locale.getDefault().toString()</code>.
// * </p>
// * <p>
// * This method completes in <code>O(1)</code>.
// * </p>
// *
// * @return The active locale; never <code>null</code>.
// */
// public final String getLocale() {
// return locale;
// }
//
// /**
// * <p>
// * Returns all of the possible bindings that start with the given trigger
// * (but are not equal to the given trigger).
// * </p>
// * <p>
// * This method completes in <code>O(1)</code>. If the bindings aren't
// * currently computed, then this completes in <code>O(n)</code>, where
// * <code>n</code> is the number of bindings.
// * </p>
// *
// * @param trigger
// * The prefix to look for; must not be <code>null</code>.
// * @return A map of triggers (<code>TriggerSequence</code>) to bindings (<code>Binding</code>).
// * This map may be empty, but it is never <code>null</code>.
// */
// public final Map getPartialMatches(final TriggerSequence trigger) {
// final Map partialMatches = (Map) getPrefixTable().get(trigger);
// if (partialMatches == null) {
// return Collections.EMPTY_MAP;
// }
//
// return partialMatches;
// }
//
// /**
// * <p>
// * Returns the command identifier for the active binding matching this
// * trigger, if any.
// * </p>
// * <p>
// * This method completes in <code>O(1)</code>. If the bindings aren't
// * currently computed, then this completes in <code>O(n)</code>, where
// * <code>n</code> is the number of bindings.
// * </p>
// *
// * @param trigger
// * The trigger to match; may be <code>null</code>.
// * @return The binding that matches, if any; <code>null</code> otherwise.
// */
// public final Binding getPerfectMatch(final TriggerSequence trigger) {
// return (Binding) getActiveBindings().get(trigger);
// }
//
// /**
// * <p>
// * Returns the active platform for this binding manager. The platform is in
// * the same format as <code>SWT.getPlatform()</code>.
// * </p>
// * <p>
// * This method completes in <code>O(1)</code>.
// * </p>
// *
// * @return The active platform; never <code>null</code>.
// */
// public final String getPlatform() {
// return platform;
// }
//
// /**
// * <p>
// * Returns the prefix table. The caller must not modify the returned map.
// * </p>
// * <p>
// * This method completes in <code>O(1)</code>. If the active bindings are
// * not yet computed, then this completes in <code>O(n)</code>, where
// * <code>n</code> is the number of bindings.
// * </p>
// *
// * @return A map of prefixes (<code>TriggerSequence</code>) to a map of
// * available completions (possibly <code>null</code>, which means
// * there is an exact match). The available completions is a map of
// * trigger (<code>TriggerSequence</code>) to binding (<code>Binding</code>).
// * This value will never be <code>null</code> but may be empty.
// */
// private final Map getPrefixTable() {
// if (prefixTable == null) {
// recomputeBindings();
// }
//
// return prefixTable;
// }
//
// /**
// * <p>
// * Gets the scheme with the given identifier. If the scheme does not already
// * exist, then a new (undefined) scheme is created with that identifier.
// * This guarantees that schemes will remain unique.
// * </p>
// * <p>
// * This method completes in amortized <code>O(1)</code>.
// * </p>
// *
// * @param schemeId
// * The identifier for the scheme to retrieve; must not be
// * <code>null</code>.
// * @return A scheme with the given identifier.
// */
// public final Scheme getScheme(final String schemeId) {
// checkId(schemeId);
//
// Scheme scheme = (Scheme) handleObjectsById.get(schemeId);
// if (scheme == null) {
// scheme = new Scheme(schemeId);
// handleObjectsById.put(schemeId, scheme);
// scheme.addSchemeListener(this);
// }
//
// return scheme;
// }
//
// /**
// * <p>
// * Ascends all of the parents of the scheme until no more parents are found.
// * </p>
// * <p>
// * This method completes in <code>O(n)</code>, where <code>n</code> is
// * the height of the context tree.
// * </p>
// *
// * @param schemeId
// * The id of the scheme for which the parents should be found;
// * may be <code>null</code>.
// * @return The array of scheme ids (<code>String</code>) starting with
// * <code>schemeId</code> and then ascending through its ancestors.
// */
// private final String[] getSchemeIds(String schemeId) {
// final List strings = new ArrayList();
// while (schemeId != null) {
// strings.add(schemeId);
// try {
// schemeId = getScheme(schemeId).getParentId();
// } catch (final NotDefinedException e) {
// Policy.getLog().log(
// new Status(IStatus.ERROR, Policy.JFACE, IStatus.OK,
// "Failed ascending scheme parents", //$NON-NLS-1$
// e));
// return new String[0];
// }
// }
//
// return (String[]) strings.toArray(new String[strings.size()]);
// }
//
// /**
// * <p>
// * Returns whether the given trigger sequence is a partial match for the
// * given sequence.
// * </p>
// * <p>
// * This method completes in <code>O(1)</code>. If the bindings aren't
// * currently computed, then this completes in <code>O(n)</code>, where
// * <code>n</code> is the number of bindings.
// * </p>
// *
// * @param trigger
// * The sequence which should be the prefix for some binding;
// * should not be <code>null</code>.
// * @return <code>true</code> if the trigger can be found in the active
// * bindings; <code>false</code> otherwise.
// */
// public final boolean isPartialMatch(final TriggerSequence trigger) {
// return (getPrefixTable().get(trigger) != null);
// }
//
// /**
// * <p>
// * Returns whether the given trigger sequence is a perfect match for the
// * given sequence.
// * </p>
// * <p>
// * This method completes in <code>O(1)</code>. If the bindings aren't
// * currently computed, then this completes in <code>O(n)</code>, where
// * <code>n</code> is the number of bindings.
// * </p>
// *
// * @param trigger
// * The sequence which should match exactly; should not be
// * <code>null</code>.
// * @return <code>true</code> if the trigger can be found in the active
// * bindings; <code>false</code> otherwise.
// */
// public final boolean isPerfectMatch(final TriggerSequence trigger) {
// return getActiveBindings().containsKey(trigger);
// }
//
// /**
// * <p>
// * Tests whether the locale for the binding matches one of the active
// * locales.
// * </p>
// * <p>
// * This method completes in <code>O(n)</code>, where <code>n</code> is
// * the number of active locales.
// * </p>
// *
// * @param binding
// * The binding with which to test; must not be <code>null</code>.
// * @return <code>true</code> if the binding's locale matches;
// * <code>false</code> otherwise.
// */
// private final boolean localeMatches(final Binding binding) {
// boolean matches = false;
//
// final String locale = binding.getLocale();
// if (locale == null) {
// return true; // shortcut a common case
// }
//
// for (int i = 0; i < locales.length; i++) {
// if (Util.equals(locales[i], locale)) {
// matches = true;
// break;
// }
// }
//
// return matches;
// }
//
// /**
// * <p>
// * Tests whether the platform for the binding matches one of the active
// * platforms.
// * </p>
// * <p>
// * This method completes in <code>O(n)</code>, where <code>n</code> is
// * the number of active platforms.
// * </p>
// *
// * @param binding
// * The binding with which to test; must not be <code>null</code>.
// * @return <code>true</code> if the binding's platform matches;
// * <code>false</code> otherwise.
// */
// private final boolean platformMatches(final Binding binding) {
// boolean matches = false;
//
// final String platform = binding.getPlatform();
// if (platform == null) {
// return true; // shortcut a common case
// }
//
// for (int i = 0; i < platforms.length; i++) {
// if (Util.equals(platforms[i], platform)) {
// matches = true;
// break;
// }
// }
//
// return matches;
// }
//
// /**
// * <p>
// * This recomputes the bindings based on changes to the state of the world.
// * This computation can be triggered by changes to contexts, the active
// * scheme, the locale, or the platform. This method tries to use the cache
// * of pre-computed bindings, if possible. When this method completes,
// * <code>activeBindings</code> will be set to the current set of bindings
// * and <code>cachedBindings</code> will contain an instance of
// * <code>CachedBindingSet</code> representing these bindings.
// * </p>
// * <p>
// * This method completes in <code>O(n+pn)</code>, where <code>n</code>
// * is the number of bindings, and <code>p</code> is the average number of
// * triggers in a trigger sequence.
// * </p>
// */
// private final void recomputeBindings() {
// if (bindings == null) {
// // Not yet initialized. This is happening too early. Do nothing.
// setActiveBindings(Collections.EMPTY_MAP, Collections.EMPTY_MAP,
// Collections.EMPTY_MAP);
// return;
// }
//
// // Figure out the current state.
// final Set activeContextIds = new HashSet(contextManager
// .getActiveContextIds());
// final Map activeContextTree = createFilteredContextTreeFor(activeContextIds);
//
// // Build a cached binding set for that state.
// final CachedBindingSet bindingCache = new CachedBindingSet(
// activeContextTree, locales, platforms, activeSchemeIds);
//
// /*
// * Check if the cached binding set already exists. If so, simply set the
// * active bindings and return.
// */
// CachedBindingSet existingCache = (CachedBindingSet) cachedBindings
// .get(bindingCache);
// if (existingCache == null) {
// existingCache = bindingCache;
// cachedBindings.put(existingCache, existingCache);
// }
// Map commandIdsByTrigger = existingCache.getBindingsByTrigger();
// if (commandIdsByTrigger != null) {
// if (DEBUG) {
// Tracing.printTrace("BINDINGS", "Cache hit"); //$NON-NLS-1$ //$NON-NLS-2$
// }
// setActiveBindings(commandIdsByTrigger, existingCache
// .getTriggersByCommandId(), existingCache.getPrefixTable());
// return;
// }
//
// // There is no cached entry for this.
// if (DEBUG) {
// Tracing.printTrace("BINDINGS", "Cache miss"); //$NON-NLS-1$ //$NON-NLS-2$
// }
//
// // Compute the active bindings.
// commandIdsByTrigger = new HashMap();
// final Map triggersByParameterizedCommand = new HashMap();
// computeBindings(activeContextTree, commandIdsByTrigger,
// triggersByParameterizedCommand);
// existingCache.setBindingsByTrigger(commandIdsByTrigger);
// existingCache.setTriggersByCommandId(triggersByParameterizedCommand);
// setActiveBindings(commandIdsByTrigger, triggersByParameterizedCommand,
// buildPrefixTable(commandIdsByTrigger));
// existingCache.setPrefixTable(prefixTable);
// }
//
// /**
// * <p>
// * Remove the specific binding by identity. Does nothing if the binding is
// * not in the manager.
// * </p>
// * <p>
// * This method completes in <code>O(n)</code>, where <code>n</code> is
// * the number of bindings.
// * </p>
// *
// * @param binding
// * The binding to be removed; must not be <code>null</code>.
// * @since 1.0
// */
// public final void removeBinding(final Binding binding) {
// if (bindings == null || bindings.length < 1) {
// return;
// }
//
// final Binding[] newBindings = new Binding[bindings.length];
// boolean bindingsChanged = false;
// int index = 0;
// for (int i = 0; i < bindingCount; i++) {
// final Binding b = bindings[i];
// if (b == binding) {
// bindingsChanged = true;
// } else {
// newBindings[index++] = b;
// }
// }
//
// if (bindingsChanged) {
// this.bindings = newBindings;
// bindingCount = index;
// clearCache();
// }
// }
//
// /**
// * <p>
// * Removes a listener from this binding manager.
// * </p>
// * <p>
// * This method completes in amortized <code>O(1)</code>.
// * </p>
// *
// * @param listener
// * The listener to be removed; must not be <code>null</code>.
// */
// public final void removeBindingManagerListener(
// final IBindingManagerListener listener) {
// removeListenerObject(listener);
// }
//
// /**
// * <p>
// * Removes any binding that matches the given values -- regardless of
// * command identifier.
// * </p>
// * <p>
// * This method completes in <code>O(n)</code>, where <code>n</code> is
// * the number of bindings.
// * </p>
// *
// * @param sequence
// * The sequence to match; may be <code>null</code>.
// * @param schemeId
// * The scheme id to match; may be <code>null</code>.
// * @param contextId
// * The context id to match; may be <code>null</code>.
// * @param locale
// * The locale to match; may be <code>null</code>.
// * @param platform
// * The platform to match; may be <code>null</code>.
// * @param windowManager
// * The window manager to match; may be <code>null</code>. TODO
// * Currently ignored.
// * @param type
// * The type to look for.
// *
// */
// public final void removeBindings(final TriggerSequence sequence,
// final String schemeId, final String contextId, final String locale,
// final String platform, final String windowManager, final int type) {
// if ((bindings == null) || (bindingCount < 1)) {
// return;
// }
//
// final Binding[] newBindings = new Binding[bindings.length];
// boolean bindingsChanged = false;
// int index = 0;
// for (int i = 0; i < bindingCount; i++) {
// final Binding binding = bindings[i];
// boolean equals = true;
// equals &= Util.equals(sequence, binding.getTriggerSequence());
// equals &= Util.equals(schemeId, binding.getSchemeId());
// equals &= Util.equals(contextId, binding.getContextId());
// equals &= Util.equals(locale, binding.getLocale());
// equals &= Util.equals(platform, binding.getPlatform());
// equals &= (type == binding.getType());
// if (equals) {
// bindingsChanged = true;
// } else {
// newBindings[index++] = binding;
// }
// }
//
// if (bindingsChanged) {
// this.bindings = newBindings;
// bindingCount = index;
// clearCache();
// }
// }
//
// /**
// * <p>
// * Attempts to remove deletion markers from the collection of bindings.
// * </p>
// * <p>
// * This method completes in <code>O(n)</code>, where <code>n</code> is
// * the number of bindings.
// * </p>
// *
// * @param bindings
// * The bindings from which the deleted items should be removed.
// * This array should not be <code>null</code>, but may be
// * empty.
// * @return The array of bindings with the deletions removed; never
// * <code>null</code>, but may be empty. Contains only instances
// * of <code>Binding</code>.
// */
// private final Binding[] removeDeletions(final Binding[] bindings) {
// final Map deletions = new HashMap();
// final Binding[] bindingsCopy = new Binding[bindingCount];
// System.arraycopy(bindings, 0, bindingsCopy, 0, bindingCount);
// int deletedCount = 0;
//
// // Extract the deletions.
// for (int i = 0; i < bindingCount; i++) {
// final Binding binding = bindingsCopy[i];
// if ((binding.getParameterizedCommand() == null)
// && (localeMatches(binding)) && (platformMatches(binding))) {
// final TriggerSequence sequence = binding.getTriggerSequence();
// final Object currentValue = deletions.get(sequence);
// if (currentValue instanceof Binding) {
// final Collection collection = new ArrayList(2);
// collection.add(currentValue);
// collection.add(binding);
// deletions.put(sequence, collection);
// } else if (currentValue instanceof Collection) {
// final Collection collection = (Collection) currentValue;
// collection.add(binding);
// } else {
// deletions.put(sequence, binding);
// }
// bindingsCopy[i] = null;
// deletedCount++;
// }
// }
//
// if (DEBUG) {
// Tracing.printTrace("BINDINGS", "There are " + deletions.size() //$NON-NLS-1$ //$NON-NLS-2$
// + " deletion markers"); //$NON-NLS-1$
// }
//
// // Remove the deleted items.
// for (int i = 0; i < bindingCount; i++) {
// final Binding binding = bindingsCopy[i];
// if (binding != null) {
// final Object deletion = deletions.get(binding
// .getTriggerSequence());
// if (deletion instanceof Binding) {
// if (((Binding) deletion).deletes(binding)) {
// bindingsCopy[i] = null;
// deletedCount++;
// }
//
// } else if (deletion instanceof Collection) {
// final Collection collection = (Collection) deletion;
// final Iterator iterator = collection.iterator();
// while (iterator.hasNext()) {
// final Object deletionBinding = iterator.next();
// if (deletionBinding instanceof Binding) {
// if (((Binding) deletionBinding).deletes(binding)) {
// bindingsCopy[i] = null;
// deletedCount++;
// break;
// }
// }
// }
//
// }
// }
// }
//
// // Compact the array.
// final Binding[] returnValue = new Binding[bindingCount - deletedCount];
// int index = 0;
// for (int i = 0; i < bindingCount; i++) {
// final Binding binding = bindingsCopy[i];
// if (binding != null) {
// returnValue[index++] = binding;
// }
// }
//
// return returnValue;
// }
//
// /**
// * <p>
// * Attempts to resolve the conflicts for the given bindings.
// * </p>
// * <p>
// * This method completes in <code>O(n)</code>, where <code>n</code> is
// * the number of bindings.
// * </p>
// *
// * @param bindings
// * The bindings which all match the same trigger sequence; must
// * not be <code>null</code>, and should contain at least two
// * items. This collection should only contain instances of
// * <code>Binding</code> (i.e., no <code>null</code> values).
// * @param activeContextTree
// * The tree of contexts to be used for all of the comparison. All
// * of the keys should be active context identifiers (i.e., never
// * <code>null</code>). The values will be their parents (i.e.,
// * possibly <code>null</code>). Both keys and values are
// * context identifiers (<code>String</code>). This map should
// * never be empty, and must never be <code>null</code>.
// * @return The binding which best matches the current state. If there is a
// * tie, then return <code>null</code>.
// */
// private final Binding resolveConflicts(final Collection bindings,
// final Map activeContextTree) {
// /*
// * This flag is used to indicate when the bestMatch binding conflicts
// * with another binding. We keep the best match binding so that we know
// * if we find a better binding. However, if we don't find a better
// * binding, then we known to return null.
// */
// boolean conflict = false;
//
// final Iterator bindingItr = bindings.iterator();
// Binding bestMatch = (Binding) bindingItr.next();
//
// /*
// * Iterate over each binding and compare it with the best match. If a
// * better match is found, then replace the best match and set the
// * conflict flag to false. If a conflict is found, then leave the best
// * match and set the conflict flag. Otherwise, just continue.
// */
// while (bindingItr.hasNext()) {
// final Binding current = (Binding) bindingItr.next();
//
// /*
// * SCHEME: Test whether the current is in a child scheme. Bindings
// * defined in a child scheme will always take priority over bindings
// * defined in a parent scheme.
// */
// final String currentSchemeId = current.getSchemeId();
// final String bestSchemeId = bestMatch.getSchemeId();
// final int compareTo = compareSchemes(bestSchemeId, currentSchemeId);
// if (compareTo > 0) {
// bestMatch = current;
// conflict = false;
// }
// if (compareTo != 0) {
// continue;
// }
//
// /*
// * CONTEXTS: Check for context superiority. Bindings defined in a
// * child context will take priority over bindings defined in a
// * parent context -- assuming that the schemes lead to a conflict.
// */
// final String currentContext = current.getContextId();
// final String bestContext = bestMatch.getContextId();
// if (!currentContext.equals(bestContext)) {
// boolean goToNextBinding = false;
//
// // Ascend the current's context tree.
// String contextPointer = currentContext;
// while (contextPointer != null) {
// if (contextPointer.equals(bestContext)) {
// // the current wins
// bestMatch = current;
// conflict = false;
// goToNextBinding = true;
// break;
// }
// contextPointer = (String) activeContextTree
// .get(contextPointer);
// }
//
// // Ascend the best match's context tree.
// contextPointer = bestContext;
// while (contextPointer != null) {
// if (contextPointer.equals(currentContext)) {
// // the best wins
// goToNextBinding = true;
// break;
// }
// contextPointer = (String) activeContextTree
// .get(contextPointer);
// }
//
// if (goToNextBinding) {
// continue;
// }
// }
//
// /*
// * TYPE: Test for type superiority.
// */
// if (current.getType() > bestMatch.getType()) {
// bestMatch = current;
// conflict = false;
// continue;
// } else if (bestMatch.getType() > current.getType()) {
// continue;
// }
//
// // We could not resolve the conflict between these two.
// conflict = true;
// }
//
// // If the best match represents a conflict, then return null.
// if (conflict) {
// return null;
// }
//
// // Otherwise, we have a winner....
// return bestMatch;
// }
//
// /**
// * <p>
// * Notifies this manager that a scheme has changed. This method is intended
// * for internal use only.
// * </p>
// * <p>
// * This method calls out to listeners, and so the time it takes to complete
// * is dependent on third-party code.
// * </p>
// *
// * @param schemeEvent
// * An event describing the change in the scheme.
// */
// public final void schemeChanged(final SchemeEvent schemeEvent) {
// if (schemeEvent.isDefinedChanged()) {
// final Scheme scheme = schemeEvent.getScheme();
// final boolean schemeIdAdded = scheme.isDefined();
// boolean activeSchemeChanged = false;
// if (schemeIdAdded) {
// definedHandleObjects.add(scheme);
// } else {
// definedHandleObjects.remove(scheme);
//
// if (activeScheme == scheme) {
// activeScheme = null;
// activeSchemeIds = null;
// activeSchemeChanged = true;
//
// // Clear the binding solution.
// clearSolution();
// }
// }
//
// if (isListenerAttached()) {
// fireBindingManagerChanged(new BindingManagerEvent(this, false,
// null, activeSchemeChanged, scheme, schemeIdAdded,
// false, false));
// }
// }
// }
//
// /**
// * Sets the active bindings and the prefix table. This ensures that the two
// * values change at the same time, and that any listeners are notified
// * appropriately.
// *
// * @param activeBindings
// * This is a map of triggers ( <code>TriggerSequence</code>)
// * to bindings (<code>Binding</code>). This value will only
// * be <code>null</code> if the active bindings have not yet
// * been computed. Otherwise, this value may be empty.
// * @param activeBindingsByCommandId
// * This is a map of fully-parameterized commands (<code>ParameterizedCommand</code>)
// * to triggers ( <code>TriggerSequence</code>). This value
// * will only be <code>null</code> if the active bindings have
// * not yet been computed. Otherwise, this value may be empty.
// * @param prefixTable
// * A map of prefixes (<code>TriggerSequence</code>) to a map
// * of available completions (possibly <code>null</code>, which
// * means there is an exact match). The available completions is a
// * map of trigger (<code>TriggerSequence</code>) to binding (<code>Binding</code>).
// * This value may be <code>null</code> if there is no existing
// * solution.
// */
// private final void setActiveBindings(final Map activeBindings,
// final Map activeBindingsByCommandId, final Map prefixTable) {
// this.activeBindings = activeBindings;
// final Map previousBindingsByParameterizedCommand = this.activeBindingsByParameterizedCommand;
// this.activeBindingsByParameterizedCommand = activeBindingsByCommandId;
// this.prefixTable = prefixTable;
//
// fireBindingManagerChanged(new BindingManagerEvent(this, true,
// previousBindingsByParameterizedCommand, false, null, false,
// false, false));
// }
//
// /**
// * <p>
// * Selects one of the schemes as the active scheme. This scheme must be
// * defined.
// * </p>
// * <p>
// * This method completes in <code>O(n)</code>, where <code>n</code> is
// * the height of the context tree.
// * </p>
// *
// * @param scheme
// * The scheme to become active; must not be <code>null</code>.
// * @throws NotDefinedException
// * If the given scheme is currently undefined.
// */
// public final void setActiveScheme(final Scheme scheme)
// throws NotDefinedException {
// if (scheme == null) {
// throw new NullPointerException("Cannot activate a null scheme"); //$NON-NLS-1$
// }
//
// if ((scheme == null) || (!scheme.isDefined())) {
// throw new NotDefinedException(
// "Cannot activate an undefined scheme. " //$NON-NLS-1$
// + scheme.getId());
// }
//
// if (Util.equals(activeScheme, scheme)) {
// return;
// }
//
// activeScheme = scheme;
// activeSchemeIds = getSchemeIds(activeScheme.getId());
// clearSolution();
// fireBindingManagerChanged(new BindingManagerEvent(this, false, null,
// true, null, false, false, false));
// }
//
// /**
// * <p>
// * Changes the set of bindings for this binding manager. Changing the set of
// * bindings all at once ensures that: (1) duplicates are removed; and (2)
// * avoids unnecessary intermediate computations. This method clears the
// * existing bindings, but does not trigger a recomputation (other method
// * calls are required to do that).
// * </p>
// * <p>
// * This method completes in <code>O(n)</code>, where <code>n</code> is
// * the number of bindings.
// * </p>
// *
// * @param bindings
// * The new array of bindings; may be <code>null</code>. This
// * set is copied into a local data structure.
// */
// public final void setBindings(final Binding[] bindings) {
// if (Arrays.equals(this.bindings, bindings)) {
// return; // nothing has changed
// }
//
// if ((bindings == null) || (bindings.length == 0)) {
// this.bindings = null;
// bindingCount = 0;
// } else {
// final int bindingsLength = bindings.length;
// this.bindings = new Binding[bindingsLength];
// System.arraycopy(bindings, 0, this.bindings, 0, bindingsLength);
// bindingCount = bindingsLength;
// }
// clearCache();
// }
//
// /**
// * <p>
// * Changes the locale for this binding manager. The locale can be used to
// * provide locale-specific bindings. If the locale is different than the
// * current locale, this will force a recomputation of the bindings. The
// * locale is in the same format as
// * <code>Locale.getDefault().toString()</code>.
// * </p>
// * <p>
// * This method completes in <code>O(1)</code>.
// * </p>
// *
// * @param locale
// * The new locale; must not be <code>null</code>.
// * @see Locale#getDefault()
// */
// public final void setLocale(final String locale) {
// if (locale == null) {
// throw new NullPointerException("The locale cannot be null"); //$NON-NLS-1$
// }
//
// if (!Util.equals(this.locale, locale)) {
// this.locale = locale;
// this.locales = expand(locale, LOCALE_SEPARATOR);
// clearSolution();
// fireBindingManagerChanged(new BindingManagerEvent(this, false,
// null, false, null, false, true, false));
// }
// }
//
// /**
// * <p>
// * Changes the platform for this binding manager. The platform can be used
// * to provide platform-specific bindings. If the platform is different than
// * the current platform, then this will force a recomputation of the
// * bindings. The locale is in the same format as
// * <code>SWT.getPlatform()</code>.
// * </p>
// * <p>
// * This method completes in <code>O(1)</code>.
// * </p>
// *
// * @param platform
// * The new platform; must not be <code>null</code>.
//// * @see org.eclipse.swt.SWT#getPlatform()
// */
// public final void setPlatform(final String platform) {
// if (platform == null) {
// throw new NullPointerException("The platform cannot be null"); //$NON-NLS-1$
// }
//
// if (!Util.equals(this.platform, platform)) {
// this.platform = platform;
// this.platforms = expand(platform, Util.ZERO_LENGTH_STRING);
// clearSolution();
// fireBindingManagerChanged(new BindingManagerEvent(this, false,
// null, false, null, false, false, true));
// }
// }
//}