0001: /*******************************************************************************
0002: * Copyright (c) 2005, 2006 IBM Corporation and others.
0003: * All rights reserved. This program and the accompanying materials
0004: * are made available under the terms of the Eclipse Public License v1.0
0005: * which accompanies this distribution, and is available at
0006: * http://www.eclipse.org/legal/epl-v10.html
0007: *
0008: * Contributors:
0009: * IBM Corporation - initial API and implementation
0010: *******************************************************************************/package org.eclipse.ui.internal.keys;
0011:
0012: import java.io.IOException;
0013: import java.io.Reader;
0014: import java.io.StringReader;
0015: import java.io.StringWriter;
0016: import java.io.Writer;
0017: import java.util.ArrayList;
0018: import java.util.Collection;
0019: import java.util.HashMap;
0020: import java.util.Iterator;
0021: import java.util.List;
0022: import java.util.Map;
0023: import java.util.StringTokenizer;
0024:
0025: import org.eclipse.core.commands.Command;
0026: import org.eclipse.core.commands.ParameterizedCommand;
0027: import org.eclipse.core.commands.common.HandleObject;
0028: import org.eclipse.core.commands.common.NotDefinedException;
0029: import org.eclipse.core.commands.util.Tracing;
0030: import org.eclipse.core.runtime.IConfigurationElement;
0031: import org.eclipse.core.runtime.IExtensionDelta;
0032: import org.eclipse.core.runtime.IExtensionRegistry;
0033: import org.eclipse.core.runtime.IRegistryChangeEvent;
0034: import org.eclipse.core.runtime.Platform;
0035: import org.eclipse.jface.bindings.Binding;
0036: import org.eclipse.jface.bindings.BindingManager;
0037: import org.eclipse.jface.bindings.Scheme;
0038: import org.eclipse.jface.bindings.keys.IKeyLookup;
0039: import org.eclipse.jface.bindings.keys.KeyBinding;
0040: import org.eclipse.jface.bindings.keys.KeyLookupFactory;
0041: import org.eclipse.jface.bindings.keys.KeySequence;
0042: import org.eclipse.jface.bindings.keys.KeyStroke;
0043: import org.eclipse.jface.bindings.keys.ParseException;
0044: import org.eclipse.jface.bindings.keys.SWTKeySupport;
0045: import org.eclipse.jface.contexts.IContextIds;
0046: import org.eclipse.jface.preference.IPreferenceStore;
0047: import org.eclipse.jface.util.PropertyChangeEvent;
0048: import org.eclipse.swt.SWT;
0049: import org.eclipse.ui.IMemento;
0050: import org.eclipse.ui.IWorkbenchPreferenceConstants;
0051: import org.eclipse.ui.PlatformUI;
0052: import org.eclipse.ui.WorkbenchException;
0053: import org.eclipse.ui.XMLMemento;
0054: import org.eclipse.ui.commands.ICommandService;
0055: import org.eclipse.ui.internal.WorkbenchPlugin;
0056: import org.eclipse.ui.internal.misc.Policy;
0057: import org.eclipse.ui.internal.registry.IWorkbenchRegistryConstants;
0058: import org.eclipse.ui.internal.services.PreferencePersistence;
0059: import org.eclipse.ui.keys.IBindingService;
0060:
0061: /**
0062: * <p>
0063: * A static class for accessing the registry and the preference store.
0064: * </p>
0065: *
0066: * @since 3.1
0067: */
0068: final class BindingPersistence extends PreferencePersistence {
0069:
0070: /**
0071: * Whether this class should print out debugging information when it reads
0072: * in data, or writes to the preference store.
0073: */
0074: private static final boolean DEBUG = Policy.DEBUG_KEY_BINDINGS;
0075:
0076: /**
0077: * The index of the active scheme configuration elements in the indexed
0078: * array.
0079: *
0080: * @see BindingPersistence#read()
0081: */
0082: private static final int INDEX_ACTIVE_SCHEME = 0;
0083:
0084: /**
0085: * The index of the binding definition configuration elements in the indexed
0086: * array.
0087: *
0088: * @see BindingPersistence#read()
0089: */
0090: private static final int INDEX_BINDING_DEFINITIONS = 1;
0091:
0092: /**
0093: * The index of the scheme definition configuration elements in the indexed
0094: * array.
0095: *
0096: * @see BindingPersistence#read(BindingManager, ICommandService)
0097: */
0098: private static final int INDEX_SCHEME_DEFINITIONS = 2;
0099:
0100: /**
0101: * The name of the default scope in 2.1.x.
0102: */
0103: private static final String LEGACY_DEFAULT_SCOPE = "org.eclipse.ui.globalScope"; //$NON-NLS-1$
0104:
0105: /**
0106: * A look-up map for 2.1.x style <code>string</code> keys on a
0107: * <code>keyBinding</code> element.
0108: */
0109: private static final Map r2_1KeysByName = new HashMap();
0110:
0111: static {
0112: final IKeyLookup lookup = KeyLookupFactory.getDefault();
0113: r2_1KeysByName.put(IKeyLookup.BACKSPACE_NAME, lookup
0114: .formalKeyLookupInteger(IKeyLookup.BACKSPACE_NAME));
0115: r2_1KeysByName.put(IKeyLookup.TAB_NAME, lookup
0116: .formalKeyLookupInteger(IKeyLookup.TAB_NAME));
0117: r2_1KeysByName.put(IKeyLookup.RETURN_NAME, lookup
0118: .formalKeyLookupInteger(IKeyLookup.RETURN_NAME));
0119: r2_1KeysByName.put(IKeyLookup.ENTER_NAME, lookup
0120: .formalKeyLookupInteger(IKeyLookup.ENTER_NAME));
0121: r2_1KeysByName.put(IKeyLookup.ESCAPE_NAME, lookup
0122: .formalKeyLookupInteger(IKeyLookup.ESCAPE_NAME));
0123: r2_1KeysByName.put(IKeyLookup.ESC_NAME, lookup
0124: .formalKeyLookupInteger(IKeyLookup.ESC_NAME));
0125: r2_1KeysByName.put(IKeyLookup.DELETE_NAME, lookup
0126: .formalKeyLookupInteger(IKeyLookup.DELETE_NAME));
0127: r2_1KeysByName.put(IKeyLookup.SPACE_NAME, lookup
0128: .formalKeyLookupInteger(IKeyLookup.SPACE_NAME));
0129: r2_1KeysByName.put(IKeyLookup.ARROW_UP_NAME, lookup
0130: .formalKeyLookupInteger(IKeyLookup.ARROW_UP_NAME));
0131: r2_1KeysByName.put(IKeyLookup.ARROW_DOWN_NAME, lookup
0132: .formalKeyLookupInteger(IKeyLookup.ARROW_DOWN_NAME));
0133: r2_1KeysByName.put(IKeyLookup.ARROW_LEFT_NAME, lookup
0134: .formalKeyLookupInteger(IKeyLookup.ARROW_LEFT_NAME));
0135: r2_1KeysByName.put(IKeyLookup.ARROW_RIGHT_NAME, lookup
0136: .formalKeyLookupInteger(IKeyLookup.ARROW_RIGHT_NAME));
0137: r2_1KeysByName.put(IKeyLookup.PAGE_UP_NAME, lookup
0138: .formalKeyLookupInteger(IKeyLookup.PAGE_UP_NAME));
0139: r2_1KeysByName.put(IKeyLookup.PAGE_DOWN_NAME, lookup
0140: .formalKeyLookupInteger(IKeyLookup.PAGE_DOWN_NAME));
0141: r2_1KeysByName.put(IKeyLookup.HOME_NAME, lookup
0142: .formalKeyLookupInteger(IKeyLookup.HOME_NAME));
0143: r2_1KeysByName.put(IKeyLookup.END_NAME, lookup
0144: .formalKeyLookupInteger(IKeyLookup.END_NAME));
0145: r2_1KeysByName.put(IKeyLookup.INSERT_NAME, lookup
0146: .formalKeyLookupInteger(IKeyLookup.INSERT_NAME));
0147: r2_1KeysByName.put(IKeyLookup.F1_NAME, lookup
0148: .formalKeyLookupInteger(IKeyLookup.F1_NAME));
0149: r2_1KeysByName.put(IKeyLookup.F2_NAME, lookup
0150: .formalKeyLookupInteger(IKeyLookup.F2_NAME));
0151: r2_1KeysByName.put(IKeyLookup.F3_NAME, lookup
0152: .formalKeyLookupInteger(IKeyLookup.F3_NAME));
0153: r2_1KeysByName.put(IKeyLookup.F4_NAME, lookup
0154: .formalKeyLookupInteger(IKeyLookup.F4_NAME));
0155: r2_1KeysByName.put(IKeyLookup.F5_NAME, lookup
0156: .formalKeyLookupInteger(IKeyLookup.F5_NAME));
0157: r2_1KeysByName.put(IKeyLookup.F6_NAME, lookup
0158: .formalKeyLookupInteger(IKeyLookup.F6_NAME));
0159: r2_1KeysByName.put(IKeyLookup.F7_NAME, lookup
0160: .formalKeyLookupInteger(IKeyLookup.F7_NAME));
0161: r2_1KeysByName.put(IKeyLookup.F8_NAME, lookup
0162: .formalKeyLookupInteger(IKeyLookup.F8_NAME));
0163: r2_1KeysByName.put(IKeyLookup.F9_NAME, lookup
0164: .formalKeyLookupInteger(IKeyLookup.F9_NAME));
0165: r2_1KeysByName.put(IKeyLookup.F10_NAME, lookup
0166: .formalKeyLookupInteger(IKeyLookup.F10_NAME));
0167: r2_1KeysByName.put(IKeyLookup.F11_NAME, lookup
0168: .formalKeyLookupInteger(IKeyLookup.F11_NAME));
0169: r2_1KeysByName.put(IKeyLookup.F12_NAME, lookup
0170: .formalKeyLookupInteger(IKeyLookup.F12_NAME));
0171: }
0172:
0173: /**
0174: * Converts a 2.1.x style key sequence (as parsed from the
0175: * <code>string</code> attribute of the <code>keyBinding</code>) to a
0176: * 3.1 key sequence.
0177: *
0178: * @param r21KeySequence
0179: * The sequence of 2.1.x key strokes that should be converted
0180: * into a 3.1 key sequence; never <code>null</code>.
0181: * @return A 3.1 key sequence; never <code>null</code>.
0182: */
0183: private static final KeySequence convert2_1Sequence(
0184: int[] r21KeySequence) {
0185: final int r21KeySequenceLength = r21KeySequence.length;
0186: final KeyStroke[] keyStrokes = new KeyStroke[r21KeySequenceLength];
0187: for (int i = 0; i < r21KeySequenceLength; i++) {
0188: keyStrokes[i] = convert2_1Stroke(r21KeySequence[i]);
0189: }
0190:
0191: return KeySequence.getInstance(keyStrokes);
0192: }
0193:
0194: /**
0195: * Converts a 2.1.x style key stroke (as parsed from the <code>string</code>
0196: * attribute of the <code>keyBinding</code> to a 3.1 key stroke.
0197: *
0198: * @param r21Stroke
0199: * The 2.1.x stroke to convert; must never be <code>null</code>.
0200: * @return A 3.1 key stroke; never <code>null</code>.
0201: */
0202: private static final KeyStroke convert2_1Stroke(final int r21Stroke) {
0203: return SWTKeySupport.convertAcceleratorToKeyStroke(r21Stroke);
0204: }
0205:
0206: /**
0207: * Returns the default scheme identifier for the currently running
0208: * application.
0209: *
0210: * @return The default scheme identifier (<code>String</code>); never
0211: * <code>null</code>, but may be empty or point to an undefined
0212: * scheme.
0213: */
0214: static final String getDefaultSchemeId() {
0215: final IPreferenceStore store = PlatformUI.getPreferenceStore();
0216: return store
0217: .getDefaultString(IWorkbenchPreferenceConstants.KEY_CONFIGURATION_ID);
0218: }
0219:
0220: /**
0221: * Parses a 2.1.x <code>string</code> attribute of the
0222: * <code>keyBinding</code> element.
0223: *
0224: * @param string
0225: * The string to parse; must not be <code>null</code>.
0226: * @return An array of integer values -- each integer representing a single
0227: * key stroke. This array may be empty, but it is never
0228: * <code>null</code>.
0229: */
0230: private static final int[] parse2_1Sequence(final String string) {
0231: final StringTokenizer stringTokenizer = new StringTokenizer(
0232: string);
0233: final int length = stringTokenizer.countTokens();
0234: final int[] strokes = new int[length];
0235:
0236: for (int i = 0; i < length; i++) {
0237: strokes[i] = parse2_1Stroke(stringTokenizer.nextToken());
0238: }
0239:
0240: return strokes;
0241: }
0242:
0243: /**
0244: * Parses a single 2.1.x key stroke string, as provided by
0245: * <code>parse2_1Sequence</code>.
0246: *
0247: * @param string
0248: * The string to parse; must not be <code>null</code>.
0249: * @return An single integer value representing this key stroke.
0250: */
0251: private static final int parse2_1Stroke(final String string) {
0252: final StringTokenizer stringTokenizer = new StringTokenizer(
0253: string, KeyStroke.KEY_DELIMITER, true);
0254:
0255: // Copy out the tokens so we have random access.
0256: final int size = stringTokenizer.countTokens();
0257: final String[] tokens = new String[size];
0258: for (int i = 0; stringTokenizer.hasMoreTokens(); i++) {
0259: tokens[i] = stringTokenizer.nextToken();
0260: }
0261:
0262: int value = 0;
0263: if (size % 2 == 1) {
0264: String token = tokens[size - 1];
0265: final Integer integer = (Integer) r2_1KeysByName.get(token
0266: .toUpperCase());
0267:
0268: if (integer != null) {
0269: value = integer.intValue();
0270: } else if (token.length() == 1) {
0271: value = token.toUpperCase().charAt(0);
0272: }
0273:
0274: if (value != 0) {
0275: for (int i = 0; i < size - 1; i++) {
0276: token = tokens[i];
0277:
0278: if (i % 2 == 0) {
0279: if (token
0280: .equalsIgnoreCase(IKeyLookup.CTRL_NAME)) {
0281: if ((value & SWT.CTRL) != 0) {
0282: return 0;
0283: }
0284:
0285: value |= SWT.CTRL;
0286:
0287: } else if (token
0288: .equalsIgnoreCase(IKeyLookup.ALT_NAME)) {
0289: if ((value & SWT.ALT) != 0) {
0290: return 0;
0291: }
0292:
0293: value |= SWT.ALT;
0294:
0295: } else if (token
0296: .equalsIgnoreCase(IKeyLookup.SHIFT_NAME)) {
0297: if ((value & SWT.SHIFT) != 0) {
0298: return 0;
0299: }
0300:
0301: value |= SWT.SHIFT;
0302:
0303: } else if (token
0304: .equalsIgnoreCase(IKeyLookup.COMMAND_NAME)) {
0305: if ((value & SWT.COMMAND) != 0) {
0306: return 0;
0307: }
0308:
0309: value |= SWT.COMMAND;
0310:
0311: } else {
0312: return 0;
0313:
0314: }
0315:
0316: } else if (!KeyStroke.KEY_DELIMITER.equals(token)) {
0317: return 0;
0318: }
0319: }
0320: }
0321: }
0322:
0323: return value;
0324: }
0325:
0326: /**
0327: * <p>
0328: * Reads the registry and the preference store, and determines the
0329: * identifier for the scheme that should be active. There is a complicated
0330: * order of priorities for this. The registry will only be read if there is
0331: * no user preference, and the default active scheme id is different than
0332: * the default default active scheme id.
0333: * </p>
0334: * <ol>
0335: * <li>A non-default preference.</li>
0336: * <li>The legacy preference XML memento.</li>
0337: * <li>A default preference value that is different than the default
0338: * default active scheme id.</li>
0339: * <li>The registry.</li>
0340: * <li>The default default active scheme id.</li>
0341: * </ol>
0342: *
0343: * @param configurationElements
0344: * The configuration elements from the commands extension point;
0345: * must not be <code>null</code>.
0346: * @param configurationElementCount
0347: * The number of configuration elements that are really in the
0348: * array.
0349: * @param preferences
0350: * The memento wrapping the commands preference key; may be
0351: * <code>null</code>.
0352: * @param bindingManager
0353: * The binding manager that should be updated with the active
0354: * scheme. This binding manager must already have its schemes
0355: * defined. This value must not be <code>null</code>.
0356: */
0357: private static final void readActiveScheme(
0358: final IConfigurationElement[] configurationElements,
0359: final int configurationElementCount,
0360: final IMemento preferences,
0361: final BindingManager bindingManager) {
0362: // A non-default preference.
0363: final IPreferenceStore store = PlatformUI.getPreferenceStore();
0364: final String defaultActiveSchemeId = store
0365: .getDefaultString(IWorkbenchPreferenceConstants.KEY_CONFIGURATION_ID);
0366: final String preferenceActiveSchemeId = store
0367: .getString(IWorkbenchPreferenceConstants.KEY_CONFIGURATION_ID);
0368: if ((preferenceActiveSchemeId != null)
0369: && (!preferenceActiveSchemeId
0370: .equals(defaultActiveSchemeId))) {
0371: try {
0372: bindingManager.setActiveScheme(bindingManager
0373: .getScheme(preferenceActiveSchemeId));
0374: return;
0375: } catch (final NotDefinedException e) {
0376: // Let's keep looking....
0377: }
0378: }
0379:
0380: // A legacy preference XML memento.
0381: if (preferences != null) {
0382: final IMemento[] preferenceMementos = preferences
0383: .getChildren(TAG_ACTIVE_KEY_CONFIGURATION);
0384: int preferenceMementoCount = preferenceMementos.length;
0385: for (int i = preferenceMementoCount - 1; i >= 0; i--) {
0386: final IMemento memento = preferenceMementos[i];
0387: String id = memento.getString(ATT_KEY_CONFIGURATION_ID);
0388: if (id != null) {
0389: try {
0390: bindingManager.setActiveScheme(bindingManager
0391: .getScheme(id));
0392: return;
0393: } catch (final NotDefinedException e) {
0394: // Let's keep looking....
0395: }
0396: }
0397: }
0398: }
0399:
0400: // A default preference value that is different than the default.
0401: if ((defaultActiveSchemeId != null && defaultActiveSchemeId
0402: .length() > 0)
0403: && (!defaultActiveSchemeId
0404: .equals(IBindingService.DEFAULT_DEFAULT_ACTIVE_SCHEME_ID))) {
0405: try {
0406: bindingManager.setActiveScheme(bindingManager
0407: .getScheme(defaultActiveSchemeId));
0408: return;
0409: } catch (final NotDefinedException e) {
0410: // Let's keep looking....
0411: }
0412: }
0413:
0414: // The registry.
0415: for (int i = configurationElementCount - 1; i >= 0; i--) {
0416: final IConfigurationElement configurationElement = configurationElements[i];
0417:
0418: String id = configurationElement
0419: .getAttribute(ATT_KEY_CONFIGURATION_ID);
0420: if (id != null) {
0421: try {
0422: bindingManager.setActiveScheme(bindingManager
0423: .getScheme(id));
0424: return;
0425: } catch (final NotDefinedException e) {
0426: // Let's keep looking....
0427: }
0428: }
0429:
0430: id = configurationElement.getAttribute(ATT_VALUE);
0431: if (id != null) {
0432: try {
0433: bindingManager.setActiveScheme(bindingManager
0434: .getScheme(id));
0435: return;
0436: } catch (final NotDefinedException e) {
0437: // Let's keep looking....
0438: }
0439: }
0440: }
0441:
0442: // The default default active scheme id.
0443: try {
0444: bindingManager
0445: .setActiveScheme(bindingManager
0446: .getScheme(IBindingService.DEFAULT_DEFAULT_ACTIVE_SCHEME_ID));
0447: } catch (final NotDefinedException e) {
0448: //this is bad - the default default scheme should always exist
0449: throw new Error(
0450: "The default default active scheme id is not defined."); //$NON-NLS-1$
0451: }
0452: }
0453:
0454: /**
0455: * Reads all of the binding definitions from the preferences.
0456: *
0457: * @param preferences
0458: * The memento for the commands preferences key.
0459: * @param bindingManager
0460: * The binding manager to which the bindings should be added;
0461: * must not be <code>null</code>.
0462: * @param commandService
0463: * The command service for the workbench; must not be
0464: * <code>null</code>.
0465: */
0466: private static final void readBindingsFromPreferences(
0467: final IMemento preferences,
0468: final BindingManager bindingManager,
0469: final ICommandService commandService) {
0470: List warningsToLog = new ArrayList(1);
0471:
0472: if (preferences != null) {
0473: final IMemento[] preferenceMementos = preferences
0474: .getChildren(TAG_KEY_BINDING);
0475: int preferenceMementoCount = preferenceMementos.length;
0476: for (int i = preferenceMementoCount - 1; i >= 0; i--) {
0477: final IMemento memento = preferenceMementos[i];
0478:
0479: // Read out the command id.
0480: String commandId = readOptional(memento, ATT_COMMAND_ID);
0481: if (commandId == null) {
0482: commandId = readOptional(memento, ATT_COMMAND);
0483: }
0484: final Command command;
0485: if (commandId != null) {
0486: command = commandService.getCommand(commandId);
0487: } else {
0488: command = null;
0489: }
0490:
0491: // Read out the scheme id.
0492: String schemeId = readOptional(memento,
0493: ATT_KEY_CONFIGURATION_ID);
0494: if (schemeId == null) {
0495: schemeId = readRequired(memento, ATT_CONFIGURATION,
0496: warningsToLog,
0497: "Key bindings need a scheme or key configuration"); //$NON-NLS-1$
0498: if (schemeId == null) {
0499: continue;
0500: }
0501: }
0502:
0503: // Read out the context id.
0504: String contextId = readOptional(memento, ATT_CONTEXT_ID);
0505: if (contextId == null) {
0506: contextId = readOptional(memento, ATT_SCOPE);
0507: }
0508: if (LEGACY_DEFAULT_SCOPE.equals(contextId)) {
0509: contextId = null;
0510: }
0511: if (contextId == null) {
0512: contextId = IContextIds.CONTEXT_ID_WINDOW;
0513: }
0514:
0515: // Read out the key sequence.
0516: String keySequenceText = readOptional(memento,
0517: ATT_KEY_SEQUENCE);
0518: KeySequence keySequence = null;
0519: if (keySequenceText == null) {
0520: keySequenceText = readRequired(memento, ATT_STRING,
0521: warningsToLog,
0522: "Key bindings need a key sequence or string"); //$NON-NLS-1$
0523: if (keySequenceText == null) {
0524: continue;
0525: }
0526:
0527: // The key sequence is in the old-style format.
0528: keySequence = convert2_1Sequence(parse2_1Sequence(keySequenceText));
0529:
0530: } else {
0531: // The key sequence is in the new-style format.
0532: try {
0533: keySequence = KeySequence
0534: .getInstance(keySequenceText);
0535: } catch (final ParseException e) {
0536: addWarning(
0537: warningsToLog,
0538: "Could not parse", null, //$NON-NLS-1$
0539: commandId,
0540: "keySequence", keySequenceText); //$NON-NLS-1$
0541: continue;
0542: }
0543: if (keySequence.isEmpty()
0544: || !keySequence.isComplete()) {
0545: addWarning(
0546: warningsToLog,
0547: "Key bindings cannot use an empty or incomplete key sequence", //$NON-NLS-1$
0548: null, commandId,
0549: "keySequence", keySequence //$NON-NLS-1$
0550: .toString());
0551: continue;
0552: }
0553:
0554: }
0555:
0556: // Read out the locale and platform.
0557: final String locale = readOptional(memento, ATT_LOCALE);
0558: final String platform = readOptional(memento,
0559: ATT_PLATFORM);
0560:
0561: // Read out the parameters
0562: final ParameterizedCommand parameterizedCommand;
0563: if (command == null) {
0564: parameterizedCommand = null;
0565: } else {
0566: parameterizedCommand = readParameters(memento,
0567: warningsToLog, command);
0568: }
0569:
0570: final Binding binding = new KeyBinding(keySequence,
0571: parameterizedCommand, schemeId, contextId,
0572: locale, platform, null, Binding.USER);
0573: bindingManager.addBinding(binding);
0574: }
0575: }
0576:
0577: // If there were any warnings, then log them now.
0578: logWarnings(warningsToLog,
0579: "Warnings while parsing the key bindings from the preference store"); //$NON-NLS-1$
0580: }
0581:
0582: /**
0583: * Reads all of the binding definitions from the commands extension point.
0584: *
0585: * @param configurationElements
0586: * The configuration elements in the commands extension point;
0587: * must not be <code>null</code>, but may be empty.
0588: * @param configurationElementCount
0589: * The number of configuration elements that are really in the
0590: * array.
0591: * @param bindingManager
0592: * The binding manager to which the bindings should be added;
0593: * must not be <code>null</code>.
0594: * @param commandService
0595: * The command service for the workbench; must not be
0596: * <code>null</code>.
0597: */
0598: private static final void readBindingsFromRegistry(
0599: final IConfigurationElement[] configurationElements,
0600: final int configurationElementCount,
0601: final BindingManager bindingManager,
0602: final ICommandService commandService) {
0603: final Collection bindings = new ArrayList(
0604: configurationElementCount);
0605: final List warningsToLog = new ArrayList(1);
0606:
0607: for (int i = 0; i < configurationElementCount; i++) {
0608: final IConfigurationElement configurationElement = configurationElements[i];
0609:
0610: /*
0611: * Read out the command id. Doing this before determining if the key
0612: * binding is actually valid is a bit wasteful. However, it is
0613: * helpful to have the command identifier when logging syntax
0614: * errors.
0615: */
0616: String commandId = configurationElement
0617: .getAttribute(ATT_COMMAND_ID);
0618: if ((commandId == null) || (commandId.length() == 0)) {
0619: commandId = configurationElement
0620: .getAttribute(ATT_COMMAND);
0621: }
0622: if ((commandId != null) && (commandId.length() == 0)) {
0623: commandId = null;
0624: }
0625: final Command command;
0626: if (commandId != null) {
0627: command = commandService.getCommand(commandId);
0628: if (!command.isDefined()) {
0629: // Reference to an undefined command. This is invalid.
0630: addWarning(warningsToLog,
0631: "Cannot bind to an undefined command", //$NON-NLS-1$
0632: configurationElement, commandId);
0633: continue;
0634: }
0635: } else {
0636: command = null;
0637: }
0638:
0639: // Read out the scheme id.
0640: String schemeId = configurationElement
0641: .getAttribute(ATT_SCHEME_ID);
0642: if ((schemeId == null) || (schemeId.length() == 0)) {
0643: schemeId = configurationElement
0644: .getAttribute(ATT_KEY_CONFIGURATION_ID);
0645: if ((schemeId == null) || (schemeId.length() == 0)) {
0646: schemeId = configurationElement
0647: .getAttribute(ATT_CONFIGURATION);
0648: if ((schemeId == null) || (schemeId.length() == 0)) {
0649: // The scheme id should never be null. This is invalid.
0650: addWarning(warningsToLog,
0651: "Key bindings need a scheme", //$NON-NLS-1$
0652: configurationElement, commandId);
0653: continue;
0654: }
0655: }
0656: }
0657:
0658: // Read out the context id.
0659: String contextId = configurationElement
0660: .getAttribute(ATT_CONTEXT_ID);
0661: if (LEGACY_DEFAULT_SCOPE.equals(contextId)) {
0662: contextId = null;
0663: } else if ((contextId == null) || (contextId.length() == 0)) {
0664: contextId = configurationElement
0665: .getAttribute(ATT_SCOPE);
0666: if (LEGACY_DEFAULT_SCOPE.equals(contextId)) {
0667: contextId = null;
0668: }
0669: }
0670: if ((contextId == null) || (contextId.length() == 0)) {
0671: contextId = IContextIds.CONTEXT_ID_WINDOW;
0672: }
0673:
0674: // Read out the key sequence.
0675: KeySequence keySequence = null;
0676: String keySequenceText = configurationElement
0677: .getAttribute(ATT_SEQUENCE);
0678: if ((keySequenceText == null)
0679: || (keySequenceText.length() == 0)) {
0680: keySequenceText = configurationElement
0681: .getAttribute(ATT_KEY_SEQUENCE);
0682: }
0683: if ((keySequenceText == null)
0684: || (keySequenceText.length() == 0)) {
0685: keySequenceText = configurationElement
0686: .getAttribute(ATT_STRING);
0687: if ((keySequenceText == null)
0688: || (keySequenceText.length() == 0)) {
0689: // The key sequence should never be null. This is pointless
0690: addWarning(
0691: warningsToLog,
0692: "Defining a key binding with no key sequence has no effect", //$NON-NLS-1$
0693: configurationElement, commandId);
0694: continue;
0695: }
0696:
0697: // The key sequence is in the old-style format.
0698: try {
0699: keySequence = convert2_1Sequence(parse2_1Sequence(keySequenceText));
0700: } catch (final IllegalArgumentException e) {
0701: addWarning(
0702: warningsToLog,
0703: "Could not parse key sequence", //$NON-NLS-1$
0704: configurationElement, commandId,
0705: "keySequence", //$NON-NLS-1$
0706: keySequenceText);
0707: continue;
0708: }
0709:
0710: } else {
0711: // The key sequence is in the new-style format.
0712: try {
0713: keySequence = KeySequence
0714: .getInstance(keySequenceText);
0715: } catch (final ParseException e) {
0716: addWarning(
0717: warningsToLog,
0718: "Could not parse key sequence", //$NON-NLS-1$
0719: configurationElement, commandId,
0720: "keySequence", //$NON-NLS-1$
0721: keySequenceText);
0722: continue;
0723: }
0724: if (keySequence.isEmpty() || !keySequence.isComplete()) {
0725: addWarning(
0726: warningsToLog,
0727: "Key bindings should not have an empty or incomplete key sequence", //$NON-NLS-1$
0728: configurationElement, commandId,
0729: "keySequence", //$NON-NLS-1$
0730: keySequence.toString());
0731: continue;
0732: }
0733:
0734: }
0735:
0736: // Read out the locale and platform.
0737: String locale = configurationElement
0738: .getAttribute(ATT_LOCALE);
0739: if ((locale != null) && (locale.length() == 0)) {
0740: locale = null;
0741: }
0742: String platform = configurationElement
0743: .getAttribute(ATT_PLATFORM);
0744: if ((platform != null) && (platform.length() == 0)) {
0745: platform = null;
0746: }
0747:
0748: // Read out the parameters, if any.
0749: final ParameterizedCommand parameterizedCommand;
0750: if (command == null) {
0751: parameterizedCommand = null;
0752: } else {
0753: parameterizedCommand = readParameters(
0754: configurationElement, warningsToLog, command);
0755: }
0756:
0757: final Binding binding = new KeyBinding(keySequence,
0758: parameterizedCommand, schemeId, contextId, locale,
0759: platform, null, Binding.SYSTEM);
0760: bindings.add(binding);
0761: }
0762:
0763: final Binding[] bindingArray = (Binding[]) bindings
0764: .toArray(new Binding[bindings.size()]);
0765: bindingManager.setBindings(bindingArray);
0766:
0767: logWarnings(
0768: warningsToLog,
0769: "Warnings while parsing the key bindings from the 'org.eclipse.ui.commands' extension point"); //$NON-NLS-1$
0770: }
0771:
0772: /**
0773: * Reads all of the scheme definitions from the registry.
0774: *
0775: * @param configurationElements
0776: * The configuration elements in the commands extension point;
0777: * must not be <code>null</code>, but may be empty.
0778: * @param configurationElementCount
0779: * The number of configuration elements that are really in the
0780: * array.
0781: * @param bindingManager
0782: * The binding manager to which the schemes should be added; must
0783: * not be <code>null</code>.
0784: */
0785: private static final void readSchemesFromRegistry(
0786: final IConfigurationElement[] configurationElements,
0787: final int configurationElementCount,
0788: final BindingManager bindingManager) {
0789: // Undefine all the previous handle objects.
0790: final HandleObject[] handleObjects = bindingManager
0791: .getDefinedSchemes();
0792: if (handleObjects != null) {
0793: for (int i = 0; i < handleObjects.length; i++) {
0794: handleObjects[i].undefine();
0795: }
0796: }
0797:
0798: final List warningsToLog = new ArrayList(1);
0799:
0800: for (int i = 0; i < configurationElementCount; i++) {
0801: final IConfigurationElement configurationElement = configurationElements[i];
0802:
0803: // Read out the attributes.
0804: final String id = readRequired(configurationElement,
0805: ATT_ID, warningsToLog, "Schemes need an id"); //$NON-NLS-1$
0806: if (id == null) {
0807: continue;
0808: }
0809: final String name = readRequired(configurationElement,
0810: ATT_NAME, warningsToLog,
0811: "A scheme needs a name", id); //$NON-NLS-1$
0812: if (name == null) {
0813: continue;
0814: }
0815: final String description = readOptional(
0816: configurationElement, ATT_DESCRIPTION);
0817:
0818: String parentId = configurationElement
0819: .getAttribute(ATT_PARENT_ID);
0820: if ((parentId != null) && (parentId.length() == 0)) {
0821: parentId = configurationElement
0822: .getAttribute(ATT_PARENT);
0823: if ((parentId != null) && (parentId.length() == 0)) {
0824: parentId = null;
0825: }
0826: }
0827:
0828: // Define the scheme.
0829: final Scheme scheme = bindingManager.getScheme(id);
0830: scheme.define(name, description, parentId);
0831: }
0832:
0833: logWarnings(
0834: warningsToLog,
0835: "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$
0836: }
0837:
0838: /**
0839: * Writes the given active scheme and bindings to the preference store. Only
0840: * bindings that are of the <code>Binding.USER</code> type will be
0841: * written; the others will be ignored.
0842: *
0843: * @param activeScheme
0844: * The scheme which should be persisted; may be <code>null</code>.
0845: * @param bindings
0846: * The bindings which should be persisted; may be
0847: * <code>null</code>
0848: * @throws IOException
0849: * If something happens while trying to write to the workbench
0850: * preference store.
0851: */
0852: static final void write(final Scheme activeScheme,
0853: final Binding[] bindings) throws IOException {
0854: // Print out debugging information, if requested.
0855: if (DEBUG) {
0856: Tracing.printTrace("BINDINGS", "Persisting active scheme '" //$NON-NLS-1$ //$NON-NLS-2$
0857: + activeScheme.getId() + '\'');
0858: Tracing.printTrace("BINDINGS", "Persisting bindings"); //$NON-NLS-1$ //$NON-NLS-2$
0859: }
0860:
0861: // Write the simple preference key to the UI preference store.
0862: writeActiveScheme(activeScheme);
0863:
0864: // Build the XML block for writing the bindings and active scheme.
0865: final XMLMemento xmlMemento = XMLMemento
0866: .createWriteRoot(EXTENSION_COMMANDS);
0867: if (activeScheme != null) {
0868: writeActiveSchemeToPreferences(xmlMemento, activeScheme);
0869: }
0870: if (bindings != null) {
0871: final int bindingsLength = bindings.length;
0872: for (int i = 0; i < bindingsLength; i++) {
0873: final Binding binding = bindings[i];
0874: if (binding.getType() == Binding.USER) {
0875: writeBindingToPreferences(xmlMemento, binding);
0876: }
0877: }
0878: }
0879:
0880: // Write the XML block to the workbench preference store.
0881: final IPreferenceStore preferenceStore = WorkbenchPlugin
0882: .getDefault().getPreferenceStore();
0883: final Writer writer = new StringWriter();
0884: try {
0885: xmlMemento.save(writer);
0886: preferenceStore.setValue(EXTENSION_COMMANDS, writer
0887: .toString());
0888: } finally {
0889: writer.close();
0890: }
0891: }
0892:
0893: /**
0894: * Writes the active scheme to its own preference key. This key is used by
0895: * RCP applications as part of their plug-in customization.
0896: *
0897: * @param scheme
0898: * The scheme to write to the preference store. If the scheme is
0899: * <code>null</code>, then it is removed.
0900: */
0901: private static final void writeActiveScheme(final Scheme scheme) {
0902: final IPreferenceStore store = PlatformUI.getPreferenceStore();
0903: final String schemeId = (scheme == null) ? null : scheme
0904: .getId();
0905: final String defaultSchemeId = store
0906: .getDefaultString(IWorkbenchPreferenceConstants.KEY_CONFIGURATION_ID);
0907: if ((defaultSchemeId == null) ? (scheme != null)
0908: : (!defaultSchemeId.equals(schemeId))) {
0909: store.setValue(
0910: IWorkbenchPreferenceConstants.KEY_CONFIGURATION_ID,
0911: scheme.getId());
0912: } else {
0913: store
0914: .setToDefault(IWorkbenchPreferenceConstants.KEY_CONFIGURATION_ID);
0915: }
0916: }
0917:
0918: /**
0919: * Writes the active scheme to the memento. If the scheme is
0920: * <code>null</code>, then all schemes in the memento are removed.
0921: *
0922: * @param memento
0923: * The memento to which the scheme should be written; must not be
0924: * <code>null</code>.
0925: * @param scheme
0926: * The scheme that should be written; must not be
0927: * <code>null</code>.
0928: */
0929: private static final void writeActiveSchemeToPreferences(
0930: final IMemento memento, final Scheme scheme) {
0931: // Add this active scheme, if it is not the default.
0932: final IPreferenceStore store = PlatformUI.getPreferenceStore();
0933: final String schemeId = scheme.getId();
0934: final String defaultSchemeId = store
0935: .getDefaultString(IWorkbenchPreferenceConstants.KEY_CONFIGURATION_ID);
0936: if ((defaultSchemeId == null) ? (schemeId != null)
0937: : (!defaultSchemeId.equals(schemeId))) {
0938: final IMemento child = memento
0939: .createChild(TAG_ACTIVE_KEY_CONFIGURATION);
0940: child.putString(ATT_KEY_CONFIGURATION_ID, schemeId);
0941: }
0942: }
0943:
0944: /**
0945: * Writes the binding to the memento. This creates a new child element on
0946: * the memento, and places the properties of the binding as its attributes.
0947: *
0948: * @param parent
0949: * The parent memento for the binding element; must not be
0950: * <code>null</code>.
0951: * @param binding
0952: * The binding to write; must not be <code>null</code>.
0953: */
0954: private static final void writeBindingToPreferences(
0955: final IMemento parent, final Binding binding) {
0956: final IMemento element = parent.createChild(TAG_KEY_BINDING);
0957: element.putString(ATT_CONTEXT_ID, binding.getContextId());
0958: final ParameterizedCommand parameterizedCommand = binding
0959: .getParameterizedCommand();
0960: final String commandId = (parameterizedCommand == null) ? null
0961: : parameterizedCommand.getId();
0962: element.putString(ATT_COMMAND_ID, commandId);
0963: element.putString(ATT_KEY_CONFIGURATION_ID, binding
0964: .getSchemeId());
0965: element.putString(ATT_KEY_SEQUENCE, binding
0966: .getTriggerSequence().toString());
0967: element.putString(ATT_LOCALE, binding.getLocale());
0968: element.putString(ATT_PLATFORM, binding.getPlatform());
0969: if (parameterizedCommand != null) {
0970: final Map parameterizations = parameterizedCommand
0971: .getParameterMap();
0972: final Iterator parameterizationItr = parameterizations
0973: .entrySet().iterator();
0974: while (parameterizationItr.hasNext()) {
0975: final Map.Entry entry = (Map.Entry) parameterizationItr
0976: .next();
0977: final String id = (String) entry.getKey();
0978: final String value = (String) entry.getValue();
0979: final IMemento parameterElement = element
0980: .createChild(TAG_PARAMETER);
0981: parameterElement.putString(ATT_ID, id);
0982: parameterElement.putString(ATT_VALUE, value);
0983: }
0984: }
0985: }
0986:
0987: /**
0988: * The binding manager which should be populated with the values from the
0989: * registry and preference store; must not be <code>null</code>.
0990: */
0991: private final BindingManager bindingManager;
0992:
0993: /**
0994: * The command service for the workbench; must not be <code>null</code>.
0995: */
0996: private final ICommandService commandService;
0997:
0998: /**
0999: * Constructs a new instance of <code>BindingPersistence</code>.
1000: *
1001: * @param bindingManager
1002: * The binding manager which should be populated with the values
1003: * from the registry and preference store; must not be
1004: * <code>null</code>.
1005: * @param commandService
1006: * The command service for the workbench; must not be
1007: * <code>null</code>.
1008: */
1009: BindingPersistence(final BindingManager bindingManager,
1010: final ICommandService commandService) {
1011: this .bindingManager = bindingManager;
1012: this .commandService = commandService;
1013: }
1014:
1015: protected final boolean isChangeImportant(
1016: final IRegistryChangeEvent event) {
1017: final IExtensionDelta[] acceleratorConfigurationDeltas = event
1018: .getExtensionDeltas(
1019: PlatformUI.PLUGIN_ID,
1020: IWorkbenchRegistryConstants.PL_ACCELERATOR_CONFIGURATIONS);
1021: if (acceleratorConfigurationDeltas.length == 0) {
1022: final IExtensionDelta[] bindingDeltas = event
1023: .getExtensionDeltas(PlatformUI.PLUGIN_ID,
1024: IWorkbenchRegistryConstants.PL_BINDINGS);
1025: if (bindingDeltas.length == 0) {
1026: final IExtensionDelta[] commandDeltas = event
1027: .getExtensionDeltas(PlatformUI.PLUGIN_ID,
1028: IWorkbenchRegistryConstants.PL_COMMANDS);
1029: if (commandDeltas.length == 0) {
1030: final IExtensionDelta[] acceleratorScopeDeltas = event
1031: .getExtensionDeltas(
1032: PlatformUI.PLUGIN_ID,
1033: IWorkbenchRegistryConstants.PL_ACCELERATOR_SCOPES);
1034: if (acceleratorScopeDeltas.length == 0) {
1035: final IExtensionDelta[] contextDeltas = event
1036: .getExtensionDeltas(
1037: PlatformUI.PLUGIN_ID,
1038: IWorkbenchRegistryConstants.PL_CONTEXTS);
1039: if (contextDeltas.length == 0) {
1040: final IExtensionDelta[] actionDefinitionDeltas = event
1041: .getExtensionDeltas(
1042: PlatformUI.PLUGIN_ID,
1043: IWorkbenchRegistryConstants.PL_ACTION_DEFINITIONS);
1044: if (actionDefinitionDeltas.length == 0) {
1045: return false;
1046: }
1047: }
1048: }
1049: }
1050: }
1051: }
1052:
1053: return true;
1054: }
1055:
1056: protected final boolean isChangeImportant(
1057: final PropertyChangeEvent event) {
1058: return EXTENSION_COMMANDS.equals(event.getProperty());
1059: }
1060:
1061: /**
1062: * Reads all of the binding information from the registry and from the
1063: * preference store.
1064: */
1065: protected final void read() {
1066: super .read();
1067:
1068: // Create the extension registry mementos.
1069: final IExtensionRegistry registry = Platform
1070: .getExtensionRegistry();
1071: int activeSchemeElementCount = 0;
1072: int bindingDefinitionCount = 0;
1073: int schemeDefinitionCount = 0;
1074: final IConfigurationElement[][] indexedConfigurationElements = new IConfigurationElement[3][];
1075:
1076: // Sort the bindings extension point based on element name.
1077: final IConfigurationElement[] bindingsExtensionPoint = registry
1078: .getConfigurationElementsFor(EXTENSION_BINDINGS);
1079: for (int i = 0; i < bindingsExtensionPoint.length; i++) {
1080: final IConfigurationElement configurationElement = bindingsExtensionPoint[i];
1081: final String name = configurationElement.getName();
1082:
1083: // Check if it is a binding definition.
1084: if (TAG_KEY.equals(name)) {
1085: addElementToIndexedArray(configurationElement,
1086: indexedConfigurationElements,
1087: INDEX_BINDING_DEFINITIONS,
1088: bindingDefinitionCount++);
1089: } else
1090: // Check to see if it is a scheme definition.
1091: if (TAG_SCHEME.equals(name)) {
1092: addElementToIndexedArray(configurationElement,
1093: indexedConfigurationElements,
1094: INDEX_SCHEME_DEFINITIONS,
1095: schemeDefinitionCount++);
1096: }
1097:
1098: }
1099:
1100: // Sort the commands extension point based on element name.
1101: final IConfigurationElement[] commandsExtensionPoint = registry
1102: .getConfigurationElementsFor(EXTENSION_COMMANDS);
1103: for (int i = 0; i < commandsExtensionPoint.length; i++) {
1104: final IConfigurationElement configurationElement = commandsExtensionPoint[i];
1105: final String name = configurationElement.getName();
1106:
1107: // Check if it is a binding definition.
1108: if (TAG_KEY_BINDING.equals(name)) {
1109: addElementToIndexedArray(configurationElement,
1110: indexedConfigurationElements,
1111: INDEX_BINDING_DEFINITIONS,
1112: bindingDefinitionCount++);
1113:
1114: // Check if it is a scheme defintion.
1115: } else if (TAG_KEY_CONFIGURATION.equals(name)) {
1116: addElementToIndexedArray(configurationElement,
1117: indexedConfigurationElements,
1118: INDEX_SCHEME_DEFINITIONS,
1119: schemeDefinitionCount++);
1120:
1121: // Check if it is an active scheme identifier.
1122: } else if (TAG_ACTIVE_KEY_CONFIGURATION.equals(name)) {
1123: addElementToIndexedArray(configurationElement,
1124: indexedConfigurationElements,
1125: INDEX_ACTIVE_SCHEME, activeSchemeElementCount++);
1126: }
1127: }
1128:
1129: /*
1130: * Sort the accelerator configuration extension point into the scheme
1131: * definitions.
1132: */
1133: final IConfigurationElement[] acceleratorConfigurationsExtensionPoint = registry
1134: .getConfigurationElementsFor(EXTENSION_ACCELERATOR_CONFIGURATIONS);
1135: for (int i = 0; i < acceleratorConfigurationsExtensionPoint.length; i++) {
1136: final IConfigurationElement configurationElement = acceleratorConfigurationsExtensionPoint[i];
1137: final String name = configurationElement.getName();
1138:
1139: // Check if the name matches the accelerator configuration element
1140: if (TAG_ACCELERATOR_CONFIGURATION.equals(name)) {
1141: addElementToIndexedArray(configurationElement,
1142: indexedConfigurationElements,
1143: INDEX_SCHEME_DEFINITIONS,
1144: schemeDefinitionCount++);
1145: }
1146: }
1147:
1148: // Create the preference memento.
1149: final IPreferenceStore store = WorkbenchPlugin.getDefault()
1150: .getPreferenceStore();
1151: final String preferenceString = store
1152: .getString(EXTENSION_COMMANDS);
1153: IMemento preferenceMemento = null;
1154: if ((preferenceString != null)
1155: && (preferenceString.length() > 0)) {
1156: final Reader reader = new StringReader(preferenceString);
1157: try {
1158: preferenceMemento = XMLMemento.createReadRoot(reader);
1159: } catch (final WorkbenchException e) {
1160: // Could not initialize the preference memento.
1161: }
1162: }
1163:
1164: // Read the scheme definitions.
1165: readSchemesFromRegistry(
1166: indexedConfigurationElements[INDEX_SCHEME_DEFINITIONS],
1167: schemeDefinitionCount, bindingManager);
1168: readActiveScheme(
1169: indexedConfigurationElements[INDEX_ACTIVE_SCHEME],
1170: activeSchemeElementCount, preferenceMemento,
1171: bindingManager);
1172: readBindingsFromRegistry(
1173: indexedConfigurationElements[INDEX_BINDING_DEFINITIONS],
1174: bindingDefinitionCount, bindingManager, commandService);
1175: readBindingsFromPreferences(preferenceMemento, bindingManager,
1176: commandService);
1177: }
1178: }
|