001: /*******************************************************************************
002: * Copyright (c) 2000, 2006 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.ui.keys;
011:
012: import java.util.ArrayList;
013: import java.util.Arrays;
014: import java.util.Collections;
015: import java.util.List;
016: import java.util.StringTokenizer;
017:
018: import org.eclipse.ui.internal.util.Util;
019:
020: /**
021: * <p>
022: * A <code>KeySequence</code> is defined as a list of zero or more
023: * <code>KeyStrokes</code>, with the stipulation that all
024: * <code>KeyStroke</code> objects must be complete, save for the last one,
025: * whose completeness is optional. A <code>KeySequence</code> is said to be
026: * complete if all of its <code>KeyStroke</code> objects are complete.
027: * </p>
028: * <p>
029: * All <code>KeySequence</code> objects have a formal string representation
030: * available via the <code>toString()</code> method. There are a number of
031: * methods to get instances of <code>KeySequence</code> objects, including one
032: * which can parse this formal string representation.
033: * </p>
034: * <p>
035: * All <code>KeySequence</code> objects, via the <code>format()</code>
036: * method, provide a version of their formal string representation translated by
037: * platform and locale, suitable for display to a user.
038: * </p>
039: * <p>
040: * <code>KeySequence</code> objects are immutable. Clients are not permitted
041: * to extend this class.
042: * </p>
043: *
044: * @deprecated Please use org.eclipse.jface.bindings.keys.KeySequence
045: * @since 3.0
046: */
047: public final class KeySequence implements Comparable {
048:
049: /**
050: * The delimiter between multiple key strokes in a single key sequence --
051: * expressed in the formal key stroke grammar. This is not to be displayed
052: * to the user. It is only intended as an internal representation.
053: */
054: public final static String KEY_STROKE_DELIMITER = "\u0020"; //$NON-NLS-1$
055:
056: /**
057: * An empty key sequence instance for use by everyone.
058: */
059: private final static KeySequence EMPTY_KEY_SEQUENCE = new KeySequence(
060: Collections.EMPTY_LIST);
061:
062: /**
063: * An internal constant used only in this object's hash code algorithm.
064: */
065: private final static int HASH_FACTOR = 89;
066:
067: /**
068: * An internal constant used only in this object's hash code algorithm.
069: */
070: private final static int HASH_INITIAL = KeySequence.class.getName()
071: .hashCode();
072:
073: /**
074: * The set of delimiters for <code>KeyStroke</code> objects allowed
075: * during parsing of the formal string representation.
076: */
077: public final static String KEY_STROKE_DELIMITERS = KEY_STROKE_DELIMITER
078: + "\b\r\u007F\u001B\f\n\0\t\u000B"; //$NON-NLS-1$
079:
080: /**
081: * Gets an instance of <code>KeySequence</code>.
082: *
083: * @return a key sequence. This key sequence will have no key strokes.
084: * Guaranteed not to be <code>null</code>.
085: */
086: public static KeySequence getInstance() {
087: return EMPTY_KEY_SEQUENCE;
088: }
089:
090: /**
091: * Gets an instance of <code>KeySequence</code> given a key sequence and
092: * a key stroke.
093: *
094: * @param keySequence
095: * a key sequence. Must not be <code>null</code>.
096: * @param keyStroke
097: * a key stroke. Must not be <code>null</code>.
098: * @return a key sequence that is equal to the given key sequence with the
099: * given key stroke appended to the end. Guaranteed not to be
100: * <code>null</code>.
101: */
102: public static KeySequence getInstance(KeySequence keySequence,
103: KeyStroke keyStroke) {
104: if (keySequence == null || keyStroke == null) {
105: throw new NullPointerException();
106: }
107:
108: List keyStrokes = new ArrayList(keySequence.getKeyStrokes());
109: keyStrokes.add(keyStroke);
110: return new KeySequence(keyStrokes);
111: }
112:
113: /**
114: * Gets an instance of <code>KeySequence</code> given a single key
115: * stroke.
116: *
117: * @param keyStroke
118: * a single key stroke. Must not be <code>null</code>.
119: * @return a key sequence. Guaranteed not to be <code>null</code>.
120: */
121: public static KeySequence getInstance(KeyStroke keyStroke) {
122: return new KeySequence(Collections.singletonList(keyStroke));
123: }
124:
125: /**
126: * Gets an instance of <code>KeySequence</code> given an array of key
127: * strokes.
128: *
129: * @param keyStrokes
130: * the array of key strokes. This array may be empty, but it
131: * must not be <code>null</code>. This array must not contain
132: * <code>null</code> elements.
133: * @return a key sequence. Guaranteed not to be <code>null</code>.
134: */
135: public static KeySequence getInstance(KeyStroke[] keyStrokes) {
136: return new KeySequence(Arrays.asList(keyStrokes));
137: }
138:
139: /**
140: * Gets an instance of <code>KeySequence</code> given a list of key
141: * strokes.
142: *
143: * @param keyStrokes
144: * the list of key strokes. This list may be empty, but it must
145: * not be <code>null</code>. If this list is not empty, it
146: * must only contain instances of <code>KeyStroke</code>.
147: * @return a key sequence. Guaranteed not to be <code>null</code>.
148: */
149: public static KeySequence getInstance(List keyStrokes) {
150: return new KeySequence(keyStrokes);
151: }
152:
153: /**
154: * Gets an instance of <code>KeySequence</code> given a new-style key
155: * sequence.
156: *
157: * @param newKeySequence
158: * The new-style key sequence to convert into a legacy key
159: * sequence; must not be <code>null</code>.
160: * @return a key sequence; never <code>null</code>.
161: */
162: public static final KeySequence getInstance(
163: final org.eclipse.jface.bindings.keys.KeySequence newKeySequence) {
164: final org.eclipse.jface.bindings.keys.KeyStroke[] newKeyStrokes = newKeySequence
165: .getKeyStrokes();
166: final int newKeyStrokesCount = newKeyStrokes.length;
167: final List legacyKeyStrokes = new ArrayList(newKeyStrokesCount);
168:
169: for (int i = 0; i < newKeyStrokesCount; i++) {
170: final org.eclipse.jface.bindings.keys.KeyStroke newKeyStroke = newKeyStrokes[i];
171: legacyKeyStrokes.add(SWTKeySupport
172: .convertAcceleratorToKeyStroke(newKeyStroke
173: .getModifierKeys()
174: | newKeyStroke.getNaturalKey()));
175: }
176:
177: return new KeySequence(legacyKeyStrokes);
178: }
179:
180: /**
181: * Gets an instance of <code>KeySequence</code> by parsing a given a
182: * formal string representation.
183: *
184: * @param string
185: * the formal string representation to parse.
186: * @return a key sequence. Guaranteed not to be <code>null</code>.
187: * @throws ParseException
188: * if the given formal string representation could not be
189: * parsed to a valid key sequence.
190: */
191: public static KeySequence getInstance(String string)
192: throws ParseException {
193: if (string == null) {
194: throw new NullPointerException();
195: }
196:
197: List keyStrokes = new ArrayList();
198: StringTokenizer stringTokenizer = new StringTokenizer(string,
199: KEY_STROKE_DELIMITERS);
200:
201: while (stringTokenizer.hasMoreTokens()) {
202: keyStrokes.add(KeyStroke.getInstance(stringTokenizer
203: .nextToken()));
204: }
205:
206: try {
207: return new KeySequence(keyStrokes);
208: } catch (Throwable t) {
209: throw new ParseException(
210: "Could not construct key sequence with these key strokes: " //$NON-NLS-1$
211: + keyStrokes);
212: }
213: }
214:
215: /**
216: * The cached hash code for this object. Because <code>KeySequence</code>
217: * objects are immutable, their hash codes need only to be computed once.
218: * After the first call to <code>hashCode()</code>, the computed value
219: * is cached here for all subsequent calls.
220: */
221: private transient int hashCode;
222:
223: /**
224: * A flag to determine if the <code>hashCode</code> field has already
225: * been computed.
226: */
227: private transient boolean hashCodeComputed;
228:
229: /**
230: * The list of key strokes for this key sequence.
231: */
232: private List keyStrokes;
233:
234: /**
235: * Constructs an instance of <code>KeySequence</code> given a list of key
236: * strokes.
237: *
238: * @param keyStrokes
239: * the list of key strokes. This list may be empty, but it must
240: * not be <code>null</code>. If this list is not empty, it
241: * must only contain instances of <code>KeyStroke</code>.
242: */
243: private KeySequence(List keyStrokes) {
244: this .keyStrokes = Util.safeCopy(keyStrokes, KeyStroke.class);
245:
246: for (int i = 0; i < this .keyStrokes.size() - 1; i++) {
247: KeyStroke keyStroke = (KeyStroke) this .keyStrokes.get(i);
248:
249: if (!keyStroke.isComplete()) {
250: throw new IllegalArgumentException();
251: }
252: }
253: }
254:
255: /**
256: * @see java.lang.Object#equals(java.lang.Object)
257: */
258: public int compareTo(Object object) {
259: KeySequence castedObject = (KeySequence) object;
260: int compareTo = Util.compare(keyStrokes,
261: castedObject.keyStrokes);
262: return compareTo;
263: }
264:
265: /**
266: * Returns whether or not this key sequence ends with the given key
267: * sequence.
268: *
269: * @param keySequence
270: * a key sequence. Must not be <code>null</code>.
271: * @param equals
272: * whether or not an identical key sequence should be considered
273: * as a possible match.
274: * @return <code>true</code>, iff the given key sequence ends with this
275: * key sequence.
276: */
277: public boolean endsWith(KeySequence keySequence, boolean equals) {
278: if (keySequence == null) {
279: throw new NullPointerException();
280: }
281:
282: return Util
283: .endsWith(keyStrokes, keySequence.keyStrokes, equals);
284: }
285:
286: /**
287: * @see java.lang.Object#equals(java.lang.Object)
288: */
289: public boolean equals(Object object) {
290: if (!(object instanceof KeySequence)) {
291: return false;
292: }
293:
294: return keyStrokes.equals(((KeySequence) object).keyStrokes);
295: }
296:
297: /**
298: * Formats this key sequence into the current default look.
299: *
300: * @return A string representation for this key sequence using the default
301: * look; never <code>null</code>.
302: */
303: public String format() {
304: return KeyFormatterFactory.getDefault().format(this );
305: }
306:
307: /**
308: * Returns the list of key strokes for this key sequence.
309: *
310: * @return the list of key strokes keys. This list may be empty, but is
311: * guaranteed not to be <code>null</code>. If this list is not
312: * empty, it is guaranteed to only contain instances of <code>KeyStroke</code>.
313: */
314: public List getKeyStrokes() {
315: return keyStrokes;
316: }
317:
318: /**
319: * @see java.lang.Object#hashCode()
320: */
321: public int hashCode() {
322: if (!hashCodeComputed) {
323: hashCode = HASH_INITIAL;
324: hashCode = hashCode * HASH_FACTOR + keyStrokes.hashCode();
325: hashCodeComputed = true;
326: }
327:
328: return hashCode;
329: }
330:
331: /**
332: * Returns whether or not this key sequence is complete. Key sequences are
333: * complete iff all of their key strokes are complete.
334: *
335: * @return <code>true</code>, iff the key sequence is complete.
336: */
337: public boolean isComplete() {
338: return keyStrokes.isEmpty()
339: || ((KeyStroke) keyStrokes.get(keyStrokes.size() - 1))
340: .isComplete();
341: }
342:
343: /**
344: * Returns whether or not this key sequence is empty. Key sequences are
345: * complete iff they have no key strokes.
346: *
347: * @return <code>true</code>, iff the key sequence is empty.
348: */
349: public boolean isEmpty() {
350: return keyStrokes.isEmpty();
351: }
352:
353: /**
354: * Returns whether or not this key sequence starts with the given key
355: * sequence.
356: *
357: * @param keySequence
358: * a key sequence. Must not be <code>null</code>.
359: * @param equals
360: * whether or not an identical key sequence should be considered
361: * as a possible match.
362: * @return <code>true</code>, iff the given key sequence starts with
363: * this key sequence.
364: */
365: public boolean startsWith(KeySequence keySequence, boolean equals) {
366: if (keySequence == null) {
367: throw new NullPointerException();
368: }
369:
370: return Util.startsWith(keyStrokes, keySequence.keyStrokes,
371: equals);
372: }
373:
374: /**
375: * Returns the formal string representation for this key sequence.
376: *
377: * @return The formal string representation for this key sequence.
378: * Guaranteed not to be <code>null</code>.
379: * @see java.lang.Object#toString()
380: */
381: public String toString() {
382: return KeyFormatterFactory.getFormalKeyFormatter().format(this);
383: }
384: }
|