Package org.eclipse.ui.internal.keys

Source Code of org.eclipse.ui.internal.keys.BindingPersistence

/*******************************************************************************
* Copyright (c) 2005, 2006 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.ui.internal.keys;

import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

import org.eclipse.core.commands.Command;
import org.eclipse.core.commands.ParameterizedCommand;
import org.eclipse.core.commands.common.HandleObject;
import org.eclipse.core.commands.common.NotDefinedException;
import org.eclipse.core.commands.util.Tracing;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtensionDelta;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.IRegistryChangeEvent;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.bindings.Binding;
import org.eclipse.jface.bindings.BindingManager;
import org.eclipse.jface.bindings.Scheme;
import org.eclipse.jface.bindings.keys.IKeyLookup;
import org.eclipse.jface.bindings.keys.KeyBinding;
import org.eclipse.jface.bindings.keys.KeyLookupFactory;
import org.eclipse.jface.bindings.keys.KeySequence;
import org.eclipse.jface.bindings.keys.KeyStroke;
import org.eclipse.jface.bindings.keys.ParseException;
import org.eclipse.jface.bindings.keys.SWTKeySupport;
import org.eclipse.jface.contexts.IContextIds;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.swt.SWT;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.IWorkbenchPreferenceConstants;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.WorkbenchException;
import org.eclipse.ui.XMLMemento;
import org.eclipse.ui.commands.ICommandService;
import org.eclipse.ui.internal.WorkbenchPlugin;
import org.eclipse.ui.internal.misc.Policy;
import org.eclipse.ui.internal.registry.IWorkbenchRegistryConstants;
import org.eclipse.ui.internal.services.PreferencePersistence;
import org.eclipse.ui.keys.IBindingService;

/**
* <p>
* A static class for accessing the registry and the preference store.
* </p>
*
* @since 3.1
*/
final class BindingPersistence extends PreferencePersistence {

  /**
   * Whether this class should print out debugging information when it reads
   * in data, or writes to the preference store.
   */
  private static final boolean DEBUG = Policy.DEBUG_KEY_BINDINGS;

  /**
   * The index of the active scheme configuration elements in the indexed
   * array.
   *
   * @see BindingPersistence#read()
   */
  private static final int INDEX_ACTIVE_SCHEME = 0;

  /**
   * The index of the binding definition configuration elements in the indexed
   * array.
   *
   * @see BindingPersistence#read()
   */
  private static final int INDEX_BINDING_DEFINITIONS = 1;

  /**
   * The index of the scheme definition configuration elements in the indexed
   * array.
   *
   * @see BindingPersistence#read(BindingManager, ICommandService)
   */
  private static final int INDEX_SCHEME_DEFINITIONS = 2;

  /**
   * The name of the default scope in 2.1.x.
   */
  private static final String LEGACY_DEFAULT_SCOPE = "org.eclipse.ui.globalScope"; //$NON-NLS-1$

  /**
   * A look-up map for 2.1.x style <code>string</code> keys on a
   * <code>keyBinding</code> element.
   */
  private static final Map r2_1KeysByName = new HashMap();

  static {
    final IKeyLookup lookup = KeyLookupFactory.getDefault();
    r2_1KeysByName.put(IKeyLookup.BACKSPACE_NAME, lookup
        .formalKeyLookupInteger(IKeyLookup.BACKSPACE_NAME));
    r2_1KeysByName.put(IKeyLookup.TAB_NAME, lookup
        .formalKeyLookupInteger(IKeyLookup.TAB_NAME));
    r2_1KeysByName.put(IKeyLookup.RETURN_NAME, lookup
        .formalKeyLookupInteger(IKeyLookup.RETURN_NAME));
    r2_1KeysByName.put(IKeyLookup.ENTER_NAME, lookup
        .formalKeyLookupInteger(IKeyLookup.ENTER_NAME));
    r2_1KeysByName.put(IKeyLookup.ESCAPE_NAME, lookup
        .formalKeyLookupInteger(IKeyLookup.ESCAPE_NAME));
    r2_1KeysByName.put(IKeyLookup.ESC_NAME, lookup
        .formalKeyLookupInteger(IKeyLookup.ESC_NAME));
    r2_1KeysByName.put(IKeyLookup.DELETE_NAME, lookup
        .formalKeyLookupInteger(IKeyLookup.DELETE_NAME));
    r2_1KeysByName.put(IKeyLookup.SPACE_NAME, lookup
        .formalKeyLookupInteger(IKeyLookup.SPACE_NAME));
    r2_1KeysByName.put(IKeyLookup.ARROW_UP_NAME, lookup
        .formalKeyLookupInteger(IKeyLookup.ARROW_UP_NAME));
    r2_1KeysByName.put(IKeyLookup.ARROW_DOWN_NAME, lookup
        .formalKeyLookupInteger(IKeyLookup.ARROW_DOWN_NAME));
    r2_1KeysByName.put(IKeyLookup.ARROW_LEFT_NAME, lookup
        .formalKeyLookupInteger(IKeyLookup.ARROW_LEFT_NAME));
    r2_1KeysByName.put(IKeyLookup.ARROW_RIGHT_NAME, lookup
        .formalKeyLookupInteger(IKeyLookup.ARROW_RIGHT_NAME));
    r2_1KeysByName.put(IKeyLookup.PAGE_UP_NAME, lookup
        .formalKeyLookupInteger(IKeyLookup.PAGE_UP_NAME));
    r2_1KeysByName.put(IKeyLookup.PAGE_DOWN_NAME, lookup
        .formalKeyLookupInteger(IKeyLookup.PAGE_DOWN_NAME));
    r2_1KeysByName.put(IKeyLookup.HOME_NAME, lookup
        .formalKeyLookupInteger(IKeyLookup.HOME_NAME));
    r2_1KeysByName.put(IKeyLookup.END_NAME, lookup
        .formalKeyLookupInteger(IKeyLookup.END_NAME));
    r2_1KeysByName.put(IKeyLookup.INSERT_NAME, lookup
        .formalKeyLookupInteger(IKeyLookup.INSERT_NAME));
    r2_1KeysByName.put(IKeyLookup.F1_NAME, lookup
        .formalKeyLookupInteger(IKeyLookup.F1_NAME));
    r2_1KeysByName.put(IKeyLookup.F2_NAME, lookup
        .formalKeyLookupInteger(IKeyLookup.F2_NAME));
    r2_1KeysByName.put(IKeyLookup.F3_NAME, lookup
        .formalKeyLookupInteger(IKeyLookup.F3_NAME));
    r2_1KeysByName.put(IKeyLookup.F4_NAME, lookup
        .formalKeyLookupInteger(IKeyLookup.F4_NAME));
    r2_1KeysByName.put(IKeyLookup.F5_NAME, lookup
        .formalKeyLookupInteger(IKeyLookup.F5_NAME));
    r2_1KeysByName.put(IKeyLookup.F6_NAME, lookup
        .formalKeyLookupInteger(IKeyLookup.F6_NAME));
    r2_1KeysByName.put(IKeyLookup.F7_NAME, lookup
        .formalKeyLookupInteger(IKeyLookup.F7_NAME));
    r2_1KeysByName.put(IKeyLookup.F8_NAME, lookup
        .formalKeyLookupInteger(IKeyLookup.F8_NAME));
    r2_1KeysByName.put(IKeyLookup.F9_NAME, lookup
        .formalKeyLookupInteger(IKeyLookup.F9_NAME));
    r2_1KeysByName.put(IKeyLookup.F10_NAME, lookup
        .formalKeyLookupInteger(IKeyLookup.F10_NAME));
    r2_1KeysByName.put(IKeyLookup.F11_NAME, lookup
        .formalKeyLookupInteger(IKeyLookup.F11_NAME));
    r2_1KeysByName.put(IKeyLookup.F12_NAME, lookup
        .formalKeyLookupInteger(IKeyLookup.F12_NAME));
  }

  /**
   * Converts a 2.1.x style key sequence (as parsed from the
   * <code>string</code> attribute of the <code>keyBinding</code>) to a
   * 3.1 key sequence.
   *
   * @param r21KeySequence
   *            The sequence of 2.1.x key strokes that should be converted
   *            into a 3.1 key sequence; never <code>null</code>.
   * @return A 3.1 key sequence; never <code>null</code>.
   */
  private static final KeySequence convert2_1Sequence(int[] r21KeySequence) {
    final int r21KeySequenceLength = r21KeySequence.length;
    final KeyStroke[] keyStrokes = new KeyStroke[r21KeySequenceLength];
    for (int i = 0; i < r21KeySequenceLength; i++) {
      keyStrokes[i] = convert2_1Stroke(r21KeySequence[i]);
    }

    return KeySequence.getInstance(keyStrokes);
  }

  /**
   * Converts a 2.1.x style key stroke (as parsed from the <code>string</code>
   * attribute of the <code>keyBinding</code> to a 3.1 key stroke.
   *
   * @param r21Stroke
   *            The 2.1.x stroke to convert; must never be <code>null</code>.
   * @return A 3.1 key stroke; never <code>null</code>.
   */
  private static final KeyStroke convert2_1Stroke(final int r21Stroke) {
    return SWTKeySupport.convertAcceleratorToKeyStroke(r21Stroke);
  }

  /**
   * Returns the default scheme identifier for the currently running
   * application.
   *
   * @return The default scheme identifier (<code>String</code>); never
   *         <code>null</code>, but may be empty or point to an undefined
   *         scheme.
   */
  static final String getDefaultSchemeId() {
    final IPreferenceStore store = PlatformUI.getPreferenceStore();
    return store
        .getDefaultString(IWorkbenchPreferenceConstants.KEY_CONFIGURATION_ID);
  }

  /**
   * Parses a 2.1.x <code>string</code> attribute of the
   * <code>keyBinding</code> element.
   *
   * @param string
   *            The string to parse; must not be <code>null</code>.
   * @return An array of integer values -- each integer representing a single
   *         key stroke. This array may be empty, but it is never
   *         <code>null</code>.
   */
  private static final int[] parse2_1Sequence(final String string) {
    final StringTokenizer stringTokenizer = new StringTokenizer(string);
    final int length = stringTokenizer.countTokens();
    final int[] strokes = new int[length];

    for (int i = 0; i < length; i++) {
      strokes[i] = parse2_1Stroke(stringTokenizer.nextToken());
    }

    return strokes;
  }

  /**
   * Parses a single 2.1.x key stroke string, as provided by
   * <code>parse2_1Sequence</code>.
   *
   * @param string
   *            The string to parse; must not be <code>null</code>.
   * @return An single integer value representing this key stroke.
   */
  private static final int parse2_1Stroke(final String string) {
    final StringTokenizer stringTokenizer = new StringTokenizer(string,
        KeyStroke.KEY_DELIMITER, true);

    // Copy out the tokens so we have random access.
    final int size = stringTokenizer.countTokens();
    final String[] tokens = new String[size];
    for (int i = 0; stringTokenizer.hasMoreTokens(); i++) {
      tokens[i] = stringTokenizer.nextToken();
    }

    int value = 0;
    if (size % 2 == 1) {
      String token = tokens[size - 1];
      final Integer integer = (Integer) r2_1KeysByName.get(token
          .toUpperCase());

      if (integer != null) {
        value = integer.intValue();
      } else if (token.length() == 1) {
        value = token.toUpperCase().charAt(0);
      }

      if (value != 0) {
        for (int i = 0; i < size - 1; i++) {
          token = tokens[i];

          if (i % 2 == 0) {
            if (token.equalsIgnoreCase(IKeyLookup.CTRL_NAME)) {
              if ((value & SWT.CTRL) != 0) {
                return 0;
              }

              value |= SWT.CTRL;

            } else if (token.equalsIgnoreCase(IKeyLookup.ALT_NAME)) {
              if ((value & SWT.ALT) != 0) {
                return 0;
              }

              value |= SWT.ALT;

            } else if (token
                .equalsIgnoreCase(IKeyLookup.SHIFT_NAME)) {
              if ((value & SWT.SHIFT) != 0) {
                return 0;
              }

              value |= SWT.SHIFT;

            } else if (token
                .equalsIgnoreCase(IKeyLookup.COMMAND_NAME)) {
              if ((value & SWT.COMMAND) != 0) {
                return 0;
              }

              value |= SWT.COMMAND;

            } else {
              return 0;

            }

          } else if (!KeyStroke.KEY_DELIMITER.equals(token)) {
            return 0;
          }
        }
      }
    }

    return value;
  }

  /**
   * <p>
   * Reads the registry and the preference store, and determines the
   * identifier for the scheme that should be active. There is a complicated
   * order of priorities for this. The registry will only be read if there is
   * no user preference, and the default active scheme id is different than
   * the default default active scheme id.
   * </p>
   * <ol>
   * <li>A non-default preference.</li>
   * <li>The legacy preference XML memento.</li>
   * <li>A default preference value that is different than the default
   * default active scheme id.</li>
   * <li>The registry.</li>
   * <li>The default default active scheme id.</li>
   * </ol>
   *
   * @param configurationElements
   *            The configuration elements from the commands extension point;
   *            must not be <code>null</code>.
   * @param configurationElementCount
   *            The number of configuration elements that are really in the
   *            array.
   * @param preferences
   *            The memento wrapping the commands preference key; may be
   *            <code>null</code>.
   * @param bindingManager
   *            The binding manager that should be updated with the active
   *            scheme. This binding manager must already have its schemes
   *            defined. This value must not be <code>null</code>.
   */
  private static final void readActiveScheme(
      final IConfigurationElement[] configurationElements,
      final int configurationElementCount, final IMemento preferences,
      final BindingManager bindingManager) {
    // A non-default preference.
    final IPreferenceStore store = PlatformUI.getPreferenceStore();
    final String defaultActiveSchemeId = store
        .getDefaultString(IWorkbenchPreferenceConstants.KEY_CONFIGURATION_ID);
    final String preferenceActiveSchemeId = store
        .getString(IWorkbenchPreferenceConstants.KEY_CONFIGURATION_ID);
    if ((preferenceActiveSchemeId != null)
        && (!preferenceActiveSchemeId.equals(defaultActiveSchemeId))) {
      try {
        bindingManager.setActiveScheme(bindingManager
            .getScheme(preferenceActiveSchemeId));
        return;
      } catch (final NotDefinedException e) {
        // Let's keep looking....
      }
    }

    // A legacy preference XML memento.
    if (preferences != null) {
      final IMemento[] preferenceMementos = preferences
          .getChildren(TAG_ACTIVE_KEY_CONFIGURATION);
      int preferenceMementoCount = preferenceMementos.length;
      for (int i = preferenceMementoCount - 1; i >= 0; i--) {
        final IMemento memento = preferenceMementos[i];
        String id = memento.getString(ATT_KEY_CONFIGURATION_ID);
        if (id != null) {
          try {
            bindingManager.setActiveScheme(bindingManager
                .getScheme(id));
            return;
          } catch (final NotDefinedException e) {
            // Let's keep looking....
          }
        }
      }
    }

    // A default preference value that is different than the default.
    if ((defaultActiveSchemeId != null && defaultActiveSchemeId.length() > 0)
        && (!defaultActiveSchemeId
            .equals(IBindingService.DEFAULT_DEFAULT_ACTIVE_SCHEME_ID))) {
      try {
        bindingManager.setActiveScheme(bindingManager
            .getScheme(defaultActiveSchemeId));
        return;
      } catch (final NotDefinedException e) {
        // Let's keep looking....
      }
    }

    // The registry.
    for (int i = configurationElementCount - 1; i >= 0; i--) {
      final IConfigurationElement configurationElement = configurationElements[i];

      String id = configurationElement
          .getAttribute(ATT_KEY_CONFIGURATION_ID);
      if (id != null) {
        try {
          bindingManager
              .setActiveScheme(bindingManager.getScheme(id));
          return;
        } catch (final NotDefinedException e) {
          // Let's keep looking....
        }
      }

      id = configurationElement.getAttribute(ATT_VALUE);
      if (id != null) {
        try {
          bindingManager
              .setActiveScheme(bindingManager.getScheme(id));
          return;
        } catch (final NotDefinedException e) {
          // Let's keep looking....
        }
      }
    }

    // The default default active scheme id.
    try {
      bindingManager
          .setActiveScheme(bindingManager
              .getScheme(IBindingService.DEFAULT_DEFAULT_ACTIVE_SCHEME_ID));
    } catch (final NotDefinedException e) {
      //this is bad - the default default scheme should always exist
      throw new Error(
          "The default default active scheme id is not defined."); //$NON-NLS-1$
    }
  }

  /**
   * Reads all of the binding definitions from the preferences.
   *
   * @param preferences
   *            The memento for the commands preferences key.
   * @param bindingManager
   *            The binding manager to which the bindings should be added;
   *            must not be <code>null</code>.
   * @param commandService
   *            The command service for the workbench; must not be
   *            <code>null</code>.
   */
  private static final void readBindingsFromPreferences(
      final IMemento preferences, final BindingManager bindingManager,
      final ICommandService commandService) {
    List warningsToLog = new ArrayList(1);

    if (preferences != null) {
      final IMemento[] preferenceMementos = preferences
          .getChildren(TAG_KEY_BINDING);
      int preferenceMementoCount = preferenceMementos.length;
      for (int i = preferenceMementoCount - 1; i >= 0; i--) {
        final IMemento memento = preferenceMementos[i];

        // Read out the command id.
        String commandId = readOptional(memento, ATT_COMMAND_ID);
        if (commandId == null) {
          commandId = readOptional(memento, ATT_COMMAND);
        }
        final Command command;
        if (commandId != null) {
          command = commandService.getCommand(commandId);
        } else {
          command = null;
        }

        // Read out the scheme id.
        String schemeId = readOptional(memento,
            ATT_KEY_CONFIGURATION_ID);
        if (schemeId == null) {
          schemeId = readRequired(memento, ATT_CONFIGURATION,
              warningsToLog,
              "Key bindings need a scheme or key configuration"); //$NON-NLS-1$
          if (schemeId == null) {
            continue;
          }
        }

        // Read out the context id.
        String contextId = readOptional(memento, ATT_CONTEXT_ID);
        if (contextId == null) {
          contextId = readOptional(memento, ATT_SCOPE);
        }
        if (LEGACY_DEFAULT_SCOPE.equals(contextId)) {
          contextId = null;
        }
        if (contextId == null) {
          contextId = IContextIds.CONTEXT_ID_WINDOW;
        }

        // Read out the key sequence.
        String keySequenceText = readOptional(memento, ATT_KEY_SEQUENCE);
        KeySequence keySequence = null;
        if (keySequenceText == null) {
          keySequenceText = readRequired(memento, ATT_STRING,
              warningsToLog,
              "Key bindings need a key sequence or string"); //$NON-NLS-1$
          if (keySequenceText == null) {
            continue;
          }

          // The key sequence is in the old-style format.
          keySequence = convert2_1Sequence(parse2_1Sequence(keySequenceText));

        } else {
          // The key sequence is in the new-style format.
          try {
            keySequence = KeySequence.getInstance(keySequenceText);
          } catch (final ParseException e) {
            addWarning(warningsToLog, "Could not parse", null, //$NON-NLS-1$
                commandId, "keySequence", keySequenceText); //$NON-NLS-1$
            continue;
          }
          if (keySequence.isEmpty() || !keySequence.isComplete()) {
            addWarning(
                warningsToLog,
                "Key bindings cannot use an empty or incomplete key sequence", //$NON-NLS-1$
                null, commandId, "keySequence", keySequence //$NON-NLS-1$
                    .toString());
            continue;
          }

        }

        // Read out the locale and platform.
        final String locale = readOptional(memento, ATT_LOCALE);
        final String platform = readOptional(memento, ATT_PLATFORM);

        // Read out the parameters
        final ParameterizedCommand parameterizedCommand;
        if (command == null) {
          parameterizedCommand = null;
        } else {
          parameterizedCommand = readParameters(memento,
              warningsToLog, command);
        }

        final Binding binding = new KeyBinding(keySequence,
            parameterizedCommand, schemeId, contextId, locale,
            platform, null, Binding.USER);
        bindingManager.addBinding(binding);
      }
    }

    // If there were any warnings, then log them now.
    logWarnings(warningsToLog,
        "Warnings while parsing the key bindings from the preference store"); //$NON-NLS-1$
  }

  /**
   * Reads all of the binding definitions from the commands extension point.
   *
   * @param configurationElements
   *            The configuration elements in the commands extension point;
   *            must not be <code>null</code>, but may be empty.
   * @param configurationElementCount
   *            The number of configuration elements that are really in the
   *            array.
   * @param bindingManager
   *            The binding manager to which the bindings should be added;
   *            must not be <code>null</code>.
   * @param commandService
   *            The command service for the workbench; must not be
   *            <code>null</code>.
   */
  private static final void readBindingsFromRegistry(
      final IConfigurationElement[] configurationElements,
      final int configurationElementCount,
      final BindingManager bindingManager,
      final ICommandService commandService) {
    final Collection bindings = new ArrayList(configurationElementCount);
    final List warningsToLog = new ArrayList(1);

    for (int i = 0; i < configurationElementCount; i++) {
      final IConfigurationElement configurationElement = configurationElements[i];

      /*
       * Read out the command id. Doing this before determining if the key
       * binding is actually valid is a bit wasteful. However, it is
       * helpful to have the command identifier when logging syntax
       * errors.
       */
      String commandId = configurationElement
          .getAttribute(ATT_COMMAND_ID);
      if ((commandId == null) || (commandId.length() == 0)) {
        commandId = configurationElement.getAttribute(ATT_COMMAND);
      }
      if ((commandId != null) && (commandId.length() == 0)) {
        commandId = null;
      }
      final Command command;
      if (commandId != null) {
        command = commandService.getCommand(commandId);
        if (!command.isDefined()) {
          // Reference to an undefined command. This is invalid.
          addWarning(warningsToLog,
              "Cannot bind to an undefined command", //$NON-NLS-1$
              configurationElement, commandId);
          continue;
        }
      } else {
        command = null;
      }

      // Read out the scheme id.
      String schemeId = configurationElement.getAttribute(ATT_SCHEME_ID);
      if ((schemeId == null) || (schemeId.length() == 0)) {
        schemeId = configurationElement
            .getAttribute(ATT_KEY_CONFIGURATION_ID);
        if ((schemeId == null) || (schemeId.length() == 0)) {
          schemeId = configurationElement
              .getAttribute(ATT_CONFIGURATION);
          if ((schemeId == null) || (schemeId.length() == 0)) {
            // The scheme id should never be null. This is invalid.
            addWarning(warningsToLog, "Key bindings need a scheme", //$NON-NLS-1$
                configurationElement, commandId);
            continue;
          }
        }
      }

      // Read out the context id.
      String contextId = configurationElement
          .getAttribute(ATT_CONTEXT_ID);
      if (LEGACY_DEFAULT_SCOPE.equals(contextId)) {
        contextId = null;
      } else if ((contextId == null) || (contextId.length() == 0)) {
        contextId = configurationElement.getAttribute(ATT_SCOPE);
        if (LEGACY_DEFAULT_SCOPE.equals(contextId)) {
          contextId = null;
        }
      }
      if ((contextId == null) || (contextId.length() == 0)) {
        contextId = IContextIds.CONTEXT_ID_WINDOW;
      }

      // Read out the key sequence.
      KeySequence keySequence = null;
      String keySequenceText = configurationElement
          .getAttribute(ATT_SEQUENCE);
      if ((keySequenceText == null) || (keySequenceText.length() == 0)) {
        keySequenceText = configurationElement
            .getAttribute(ATT_KEY_SEQUENCE);
      }
      if ((keySequenceText == null) || (keySequenceText.length() == 0)) {
        keySequenceText = configurationElement.getAttribute(ATT_STRING);
        if ((keySequenceText == null)
            || (keySequenceText.length() == 0)) {
          // The key sequence should never be null. This is pointless
          addWarning(
              warningsToLog,
              "Defining a key binding with no key sequence has no effect", //$NON-NLS-1$
              configurationElement, commandId);
          continue;
        }

        // The key sequence is in the old-style format.
        try {
          keySequence = convert2_1Sequence(parse2_1Sequence(keySequenceText));
        } catch (final IllegalArgumentException e) {
          addWarning(warningsToLog, "Could not parse key sequence", //$NON-NLS-1$
              configurationElement, commandId, "keySequence", //$NON-NLS-1$
              keySequenceText);
          continue;
        }

      } else {
        // The key sequence is in the new-style format.
        try {
          keySequence = KeySequence.getInstance(keySequenceText);
        } catch (final ParseException e) {
          addWarning(warningsToLog, "Could not parse key sequence", //$NON-NLS-1$
              configurationElement, commandId, "keySequence", //$NON-NLS-1$
              keySequenceText);
          continue;
        }
        if (keySequence.isEmpty() || !keySequence.isComplete()) {
          addWarning(
              warningsToLog,
              "Key bindings should not have an empty or incomplete key sequence", //$NON-NLS-1$
              configurationElement, commandId, "keySequence", //$NON-NLS-1$
              keySequence.toString());
          continue;
        }

      }

      // Read out the locale and platform.
      String locale = configurationElement.getAttribute(ATT_LOCALE);
      if ((locale != null) && (locale.length() == 0)) {
        locale = null;
      }
      String platform = configurationElement.getAttribute(ATT_PLATFORM);
      if ((platform != null) && (platform.length() == 0)) {
        platform = null;
      }

      // Read out the parameters, if any.
      final ParameterizedCommand parameterizedCommand;
      if (command == null) {
        parameterizedCommand = null;
      } else {
        parameterizedCommand = readParameters(configurationElement,
            warningsToLog, command);
      }

      final Binding binding = new KeyBinding(keySequence,
          parameterizedCommand, schemeId, contextId, locale,
          platform, null, Binding.SYSTEM);
      bindings.add(binding);
    }

    final Binding[] bindingArray = (Binding[]) bindings
        .toArray(new Binding[bindings.size()]);
    bindingManager.setBindings(bindingArray);

    logWarnings(
        warningsToLog,
        "Warnings while parsing the key bindings from the 'org.eclipse.ui.commands' extension point"); //$NON-NLS-1$
  }

  /**
   * Reads all of the scheme definitions from the registry.
   *
   * @param configurationElements
   *            The configuration elements in the commands extension point;
   *            must not be <code>null</code>, but may be empty.
   * @param configurationElementCount
   *            The number of configuration elements that are really in the
   *            array.
   * @param bindingManager
   *            The binding manager to which the schemes should be added; must
   *            not be <code>null</code>.
   */
  private static final void readSchemesFromRegistry(
      final IConfigurationElement[] configurationElements,
      final int configurationElementCount,
      final BindingManager bindingManager) {
    // Undefine all the previous handle objects.
    final HandleObject[] handleObjects = bindingManager.getDefinedSchemes();
    if (handleObjects != null) {
      for (int i = 0; i < handleObjects.length; i++) {
        handleObjects[i].undefine();
      }
    }

    final List warningsToLog = new ArrayList(1);

    for (int i = 0; i < configurationElementCount; i++) {
      final IConfigurationElement configurationElement = configurationElements[i];

      // Read out the attributes.
      final String id = readRequired(configurationElement, ATT_ID,
          warningsToLog, "Schemes need an id"); //$NON-NLS-1$
      if (id == null) {
        continue;
      }
      final String name = readRequired(configurationElement, ATT_NAME,
          warningsToLog, "A scheme needs a name", id); //$NON-NLS-1$
      if (name == null) {
        continue;
      }
      final String description = readOptional(configurationElement,
          ATT_DESCRIPTION);

      String parentId = configurationElement.getAttribute(ATT_PARENT_ID);
      if ((parentId != null) && (parentId.length() == 0)) {
        parentId = configurationElement.getAttribute(ATT_PARENT);
        if ((parentId != null) && (parentId.length() == 0)) {
          parentId = null;
        }
      }

      // Define the scheme.
      final Scheme scheme = bindingManager.getScheme(id);
      scheme.define(name, description, parentId);
    }

    logWarnings(
        warningsToLog,
        "Warnings while parsing the key bindings from the 'org.eclipse.ui.bindings', 'org.eclipse.ui.acceleratorConfigurations' and 'org.eclipse.ui.commands' extension point"); //$NON-NLS-1$
  }

  /**
   * Writes the given active scheme and bindings to the preference store. Only
   * bindings that are of the <code>Binding.USER</code> type will be
   * written; the others will be ignored.
   *
   * @param activeScheme
   *            The scheme which should be persisted; may be <code>null</code>.
   * @param bindings
   *            The bindings which should be persisted; may be
   *            <code>null</code>
   * @throws IOException
   *             If something happens while trying to write to the workbench
   *             preference store.
   */
  static final void write(final Scheme activeScheme, final Binding[] bindings)
      throws IOException {
    // Print out debugging information, if requested.
    if (DEBUG) {
      Tracing.printTrace("BINDINGS", "Persisting active scheme '" //$NON-NLS-1$ //$NON-NLS-2$
          + activeScheme.getId() + '\'');
      Tracing.printTrace("BINDINGS", "Persisting bindings"); //$NON-NLS-1$ //$NON-NLS-2$
    }

    // Write the simple preference key to the UI preference store.
    writeActiveScheme(activeScheme);

    // Build the XML block for writing the bindings and active scheme.
    final XMLMemento xmlMemento = XMLMemento
        .createWriteRoot(EXTENSION_COMMANDS);
    if (activeScheme != null) {
      writeActiveSchemeToPreferences(xmlMemento, activeScheme);
    }
    if (bindings != null) {
      final int bindingsLength = bindings.length;
      for (int i = 0; i < bindingsLength; i++) {
        final Binding binding = bindings[i];
        if (binding.getType() == Binding.USER) {
          writeBindingToPreferences(xmlMemento, binding);
        }
      }
    }

    // Write the XML block to the workbench preference store.
    final IPreferenceStore preferenceStore = WorkbenchPlugin.getDefault()
        .getPreferenceStore();
    final Writer writer = new StringWriter();
    try {
      xmlMemento.save(writer);
      preferenceStore.setValue(EXTENSION_COMMANDS, writer.toString());
    } finally {
      writer.close();
    }
  }

  /**
   * Writes the active scheme to its own preference key. This key is used by
   * RCP applications as part of their plug-in customization.
   *
   * @param scheme
   *            The scheme to write to the preference store. If the scheme is
   *            <code>null</code>, then it is removed.
   */
  private static final void writeActiveScheme(final Scheme scheme) {
    final IPreferenceStore store = PlatformUI.getPreferenceStore();
    final String schemeId = (scheme == null) ? null : scheme.getId();
    final String defaultSchemeId = store
        .getDefaultString(IWorkbenchPreferenceConstants.KEY_CONFIGURATION_ID);
    if ((defaultSchemeId == null) ? (scheme != null) : (!defaultSchemeId
        .equals(schemeId))) {
      store.setValue(IWorkbenchPreferenceConstants.KEY_CONFIGURATION_ID,
          scheme.getId());
    } else {
      store
          .setToDefault(IWorkbenchPreferenceConstants.KEY_CONFIGURATION_ID);
    }
  }

  /**
   * Writes the active scheme to the memento. If the scheme is
   * <code>null</code>, then all schemes in the memento are removed.
   *
   * @param memento
   *            The memento to which the scheme should be written; must not be
   *            <code>null</code>.
   * @param scheme
   *            The scheme that should be written; must not be
   *            <code>null</code>.
   */
  private static final void writeActiveSchemeToPreferences(
      final IMemento memento, final Scheme scheme) {
    // Add this active scheme, if it is not the default.
    final IPreferenceStore store = PlatformUI.getPreferenceStore();
    final String schemeId = scheme.getId();
    final String defaultSchemeId = store
        .getDefaultString(IWorkbenchPreferenceConstants.KEY_CONFIGURATION_ID);
    if ((defaultSchemeId == null) ? (schemeId != null) : (!defaultSchemeId
        .equals(schemeId))) {
      final IMemento child = memento
          .createChild(TAG_ACTIVE_KEY_CONFIGURATION);
      child.putString(ATT_KEY_CONFIGURATION_ID, schemeId);
    }
  }

  /**
   * Writes the binding to the memento. This creates a new child element on
   * the memento, and places the properties of the binding as its attributes.
   *
   * @param parent
   *            The parent memento for the binding element; must not be
   *            <code>null</code>.
   * @param binding
   *            The binding to write; must not be <code>null</code>.
   */
  private static final void writeBindingToPreferences(final IMemento parent,
      final Binding binding) {
    final IMemento element = parent.createChild(TAG_KEY_BINDING);
    element.putString(ATT_CONTEXT_ID, binding.getContextId());
    final ParameterizedCommand parameterizedCommand = binding
        .getParameterizedCommand();
    final String commandId = (parameterizedCommand == null) ? null
        : parameterizedCommand.getId();
    element.putString(ATT_COMMAND_ID, commandId);
    element.putString(ATT_KEY_CONFIGURATION_ID, binding.getSchemeId());
    element.putString(ATT_KEY_SEQUENCE, binding.getTriggerSequence()
        .toString());
    element.putString(ATT_LOCALE, binding.getLocale());
    element.putString(ATT_PLATFORM, binding.getPlatform());
    if (parameterizedCommand != null) {
      final Map parameterizations = parameterizedCommand
          .getParameterMap();
      final Iterator parameterizationItr = parameterizations.entrySet()
          .iterator();
      while (parameterizationItr.hasNext()) {
        final Map.Entry entry = (Map.Entry) parameterizationItr.next();
        final String id = (String) entry.getKey();
        final String value = (String) entry.getValue();
        final IMemento parameterElement = element
            .createChild(TAG_PARAMETER);
        parameterElement.putString(ATT_ID, id);
        parameterElement.putString(ATT_VALUE, value);
      }
    }
  }

  /**
   * The binding manager which should be populated with the values from the
   * registry and preference store; must not be <code>null</code>.
   */
  private final BindingManager bindingManager;

  /**
   * The command service for the workbench; must not be <code>null</code>.
   */
  private final ICommandService commandService;

  /**
   * Constructs a new instance of <code>BindingPersistence</code>.
   *
   * @param bindingManager
   *            The binding manager which should be populated with the values
   *            from the registry and preference store; must not be
   *            <code>null</code>.
   * @param commandService
   *            The command service for the workbench; must not be
   *            <code>null</code>.
   */
  BindingPersistence(final BindingManager bindingManager,
      final ICommandService commandService) {
    this.bindingManager = bindingManager;
    this.commandService = commandService;
  }

  protected final boolean isChangeImportant(final IRegistryChangeEvent event) {
    final IExtensionDelta[] acceleratorConfigurationDeltas = event
        .getExtensionDeltas(
            PlatformUI.PLUGIN_ID,
            IWorkbenchRegistryConstants.PL_ACCELERATOR_CONFIGURATIONS);
    if (acceleratorConfigurationDeltas.length == 0) {
      final IExtensionDelta[] bindingDeltas = event.getExtensionDeltas(
          PlatformUI.PLUGIN_ID,
          IWorkbenchRegistryConstants.PL_BINDINGS);
      if (bindingDeltas.length == 0) {
        final IExtensionDelta[] commandDeltas = event
            .getExtensionDeltas(PlatformUI.PLUGIN_ID,
                IWorkbenchRegistryConstants.PL_COMMANDS);
        if (commandDeltas.length == 0) {
          final IExtensionDelta[] acceleratorScopeDeltas = event
              .getExtensionDeltas(
                  PlatformUI.PLUGIN_ID,
                  IWorkbenchRegistryConstants.PL_ACCELERATOR_SCOPES);
          if (acceleratorScopeDeltas.length == 0) {
            final IExtensionDelta[] contextDeltas = event
                .getExtensionDeltas(PlatformUI.PLUGIN_ID,
                    IWorkbenchRegistryConstants.PL_CONTEXTS);
            if (contextDeltas.length == 0) {
              final IExtensionDelta[] actionDefinitionDeltas = event
                  .getExtensionDeltas(
                      PlatformUI.PLUGIN_ID,
                      IWorkbenchRegistryConstants.PL_ACTION_DEFINITIONS);
              if (actionDefinitionDeltas.length == 0) {
                return false;
              }
            }
          }
        }
      }
    }

    return true;
  }

  protected final boolean isChangeImportant(final PropertyChangeEvent event) {
    return EXTENSION_COMMANDS.equals(event.getProperty());
  }

  /**
   * Reads all of the binding information from the registry and from the
   * preference store.
   */
  protected final void read() {
    super.read();

    // Create the extension registry mementos.
    final IExtensionRegistry registry = Platform.getExtensionRegistry();
    int activeSchemeElementCount = 0;
    int bindingDefinitionCount = 0;
    int schemeDefinitionCount = 0;
    final IConfigurationElement[][] indexedConfigurationElements = new IConfigurationElement[3][];

    // Sort the bindings extension point based on element name.
    final IConfigurationElement[] bindingsExtensionPoint = registry
        .getConfigurationElementsFor(EXTENSION_BINDINGS);
    for (int i = 0; i < bindingsExtensionPoint.length; i++) {
      final IConfigurationElement configurationElement = bindingsExtensionPoint[i];
      final String name = configurationElement.getName();

      // Check if it is a binding definition.
      if (TAG_KEY.equals(name)) {
        addElementToIndexedArray(configurationElement,
            indexedConfigurationElements,
            INDEX_BINDING_DEFINITIONS, bindingDefinitionCount++);
      } else
      // Check to see if it is a scheme definition.
      if (TAG_SCHEME.equals(name)) {
        addElementToIndexedArray(configurationElement,
            indexedConfigurationElements, INDEX_SCHEME_DEFINITIONS,
            schemeDefinitionCount++);
      }

    }

    // Sort the commands extension point based on element name.
    final IConfigurationElement[] commandsExtensionPoint = registry
        .getConfigurationElementsFor(EXTENSION_COMMANDS);
    for (int i = 0; i < commandsExtensionPoint.length; i++) {
      final IConfigurationElement configurationElement = commandsExtensionPoint[i];
      final String name = configurationElement.getName();

      // Check if it is a binding definition.
      if (TAG_KEY_BINDING.equals(name)) {
        addElementToIndexedArray(configurationElement,
            indexedConfigurationElements,
            INDEX_BINDING_DEFINITIONS, bindingDefinitionCount++);

        // Check if it is a scheme defintion.
      } else if (TAG_KEY_CONFIGURATION.equals(name)) {
        addElementToIndexedArray(configurationElement,
            indexedConfigurationElements, INDEX_SCHEME_DEFINITIONS,
            schemeDefinitionCount++);

        // Check if it is an active scheme identifier.
      } else if (TAG_ACTIVE_KEY_CONFIGURATION.equals(name)) {
        addElementToIndexedArray(configurationElement,
            indexedConfigurationElements, INDEX_ACTIVE_SCHEME,
            activeSchemeElementCount++);
      }
    }

    /*
     * Sort the accelerator configuration extension point into the scheme
     * definitions.
     */
    final IConfigurationElement[] acceleratorConfigurationsExtensionPoint = registry
        .getConfigurationElementsFor(EXTENSION_ACCELERATOR_CONFIGURATIONS);
    for (int i = 0; i < acceleratorConfigurationsExtensionPoint.length; i++) {
      final IConfigurationElement configurationElement = acceleratorConfigurationsExtensionPoint[i];
      final String name = configurationElement.getName();

      // Check if the name matches the accelerator configuration element
      if (TAG_ACCELERATOR_CONFIGURATION.equals(name)) {
        addElementToIndexedArray(configurationElement,
            indexedConfigurationElements, INDEX_SCHEME_DEFINITIONS,
            schemeDefinitionCount++);
      }
    }

    // Create the preference memento.
    final IPreferenceStore store = WorkbenchPlugin.getDefault()
        .getPreferenceStore();
    final String preferenceString = store.getString(EXTENSION_COMMANDS);
    IMemento preferenceMemento = null;
    if ((preferenceString != null) && (preferenceString.length() > 0)) {
      final Reader reader = new StringReader(preferenceString);
      try {
        preferenceMemento = XMLMemento.createReadRoot(reader);
      } catch (final WorkbenchException e) {
        // Could not initialize the preference memento.
      }
    }

    // Read the scheme definitions.
    readSchemesFromRegistry(
        indexedConfigurationElements[INDEX_SCHEME_DEFINITIONS],
        schemeDefinitionCount, bindingManager);
    readActiveScheme(indexedConfigurationElements[INDEX_ACTIVE_SCHEME],
        activeSchemeElementCount, preferenceMemento, bindingManager);
    readBindingsFromRegistry(
        indexedConfigurationElements[INDEX_BINDING_DEFINITIONS],
        bindingDefinitionCount, bindingManager, commandService);
    readBindingsFromPreferences(preferenceMemento, bindingManager,
        commandService);
  }
}
TOP

Related Classes of org.eclipse.ui.internal.keys.BindingPersistence

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.