001: /*******************************************************************************
002: * Copyright (c) 2005, 2007 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.jface.viewers;
011:
012: import java.util.ArrayList;
013: import java.util.List;
014:
015: import org.eclipse.core.runtime.Assert;
016:
017: /**
018: * A concrete implementation of the <code>ITreeSelection</code> interface,
019: * suitable for instantiating.
020: * <p>
021: * This class is not intended to be subclassed.
022: * </p>
023: *
024: * @since 3.2
025: */
026: public class TreeSelection extends StructuredSelection implements
027: ITreeSelection {
028:
029: /* Implementation note. This class extends StructuredSelection because many pre-existing
030: * JFace viewer clients assumed that the only implementation of IStructuredSelection
031: * was StructuredSelection. By extending StructuredSelection rather than implementing
032: * ITreeSelection directly, we avoid this problem.
033: * For more details, see Bug 121939 [Viewers] TreeSelection should subclass StructuredSelection.
034: */
035:
036: private TreePath[] paths = null;
037: private CustomHashtable element2TreePaths = null;
038:
039: /**
040: * The canonical empty selection. This selection should be used instead of
041: * <code>null</code>.
042: */
043: public static final TreeSelection EMPTY = new TreeSelection();
044:
045: private static final TreePath[] EMPTY_TREE_PATHS = new TreePath[0];
046:
047: private static class InitializeData {
048: List selection;
049: TreePath[] paths;
050: CustomHashtable element2TreePaths;
051:
052: private InitializeData(TreePath[] paths,
053: IElementComparer comparer) {
054: this .paths = new TreePath[paths.length];
055: System.arraycopy(paths, 0, this .paths, 0, paths.length);
056: element2TreePaths = new CustomHashtable(comparer);
057: int size = paths.length;
058: selection = new ArrayList(size);
059: for (int i = 0; i < size; i++) {
060: Object lastSegment = paths[i].getLastSegment();
061: Object mapped = element2TreePaths.get(lastSegment);
062: if (mapped == null) {
063: selection.add(lastSegment);
064: element2TreePaths.put(lastSegment, paths[i]);
065: } else if (mapped instanceof List) {
066: ((List) mapped).add(paths[i]);
067: } else {
068: List newMapped = new ArrayList(2);
069: newMapped.add(mapped);
070: newMapped.add(paths[i]);
071: element2TreePaths.put(lastSegment, newMapped);
072: }
073: }
074: }
075: }
076:
077: /**
078: * Constructs a selection based on the elements identified by the given tree
079: * paths.
080: *
081: * @param paths
082: * tree paths
083: */
084: public TreeSelection(TreePath[] paths) {
085: this (new InitializeData(paths, null));
086: }
087:
088: /**
089: * Constructs a selection based on the elements identified by the given tree
090: * paths.
091: *
092: * @param paths
093: * tree paths
094: * @param comparer
095: * the comparer, or <code>null</code> if default equals is to be used
096: */
097: public TreeSelection(TreePath[] paths, IElementComparer comparer) {
098: this (new InitializeData(paths, comparer));
099: }
100:
101: /**
102: * Constructs a selection based on the elements identified by the given tree
103: * path.
104: *
105: * @param treePath
106: * tree path, or <code>null</code> for an empty selection
107: */
108: public TreeSelection(TreePath treePath) {
109: this (treePath != null ? new TreePath[] { treePath }
110: : EMPTY_TREE_PATHS, null);
111: }
112:
113: /**
114: * Constructs a selection based on the elements identified by the given tree
115: * path.
116: *
117: * @param treePath
118: * tree path, or <code>null</code> for an empty selection
119: * @param comparer
120: * the comparer, or <code>null</code> if default equals is to be used
121: */
122: public TreeSelection(TreePath treePath, IElementComparer comparer) {
123: this (treePath != null ? new TreePath[] { treePath }
124: : EMPTY_TREE_PATHS, comparer);
125: }
126:
127: /**
128: * Creates a new tree selection based on the initialization data.
129: *
130: * @param data the data
131: */
132: private TreeSelection(InitializeData data) {
133: super (data.selection);
134: paths = data.paths;
135: element2TreePaths = data.element2TreePaths;
136: }
137:
138: /**
139: * Creates a new empty selection. See also the static field
140: * <code>EMPTY</code> which contains an empty selection singleton.
141: * <p>
142: * Note that TreeSelection.EMPTY is not equals() to StructuredViewer.EMPTY.
143: * </p>
144: *
145: * @see #EMPTY
146: */
147: public TreeSelection() {
148: super ();
149: }
150:
151: /**
152: * Returns the element comparer passed in when the tree selection
153: * has been created or <code>null</code> if no comparer has been
154: * provided.
155: *
156: * @return the element comparer or <code>null</code>
157: *
158: * @since 3.2
159: */
160: public IElementComparer getElementComparer() {
161: if (element2TreePaths == null)
162: return null;
163: return element2TreePaths.getComparer();
164: }
165:
166: public boolean equals(Object obj) {
167: if (!(obj instanceof TreeSelection)) {
168: // Fall back to super implementation, see bug 135837.
169: return super .equals(obj);
170: }
171: TreeSelection selection = (TreeSelection) obj;
172: int size = getPaths().length;
173: if (selection.getPaths().length == size) {
174: IElementComparer comparerOrNull = (getElementComparer() == selection
175: .getElementComparer()) ? getElementComparer()
176: : null;
177: if (size > 0) {
178: for (int i = 0; i < paths.length; i++) {
179: if (!paths[i].equals(selection.paths[i],
180: comparerOrNull)) {
181: return false;
182: }
183: }
184: }
185: return true;
186: }
187: return false;
188: }
189:
190: public int hashCode() {
191: int code = getClass().hashCode();
192: if (paths != null) {
193: for (int i = 0; i < paths.length; i++) {
194: code = code * 17
195: + paths[i].hashCode(getElementComparer());
196: }
197: }
198: return code;
199: }
200:
201: public TreePath[] getPaths() {
202: return paths == null ? EMPTY_TREE_PATHS : (TreePath[]) paths
203: .clone();
204: }
205:
206: public TreePath[] getPathsFor(Object element) {
207: Object value = element2TreePaths == null ? null
208: : element2TreePaths.get(element);
209: if (value == null) {
210: return EMPTY_TREE_PATHS;
211: } else if (value instanceof TreePath) {
212: return new TreePath[] { (TreePath) value };
213: } else if (value instanceof List) {
214: List l = (List) value;
215: return (TreePath[]) l.toArray(new TreePath[l.size()]);
216: } else {
217: // should not happen:
218: Assert.isTrue(false, "Unhandled case"); //$NON-NLS-1$
219: return null;
220: }
221: }
222: }
|