]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.browsing.ui/src/org/simantics/browsing/ui/NodeContextPath.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.browsing.ui / src / org / simantics / browsing / ui / NodeContextPath.java
1 /*******************************************************************************
2  * Copyright (c) 2005, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package org.simantics.browsing.ui;
12
13 import java.util.Arrays;
14
15 import org.eclipse.core.runtime.Assert;
16 import org.eclipse.core.runtime.IAdaptable;
17
18 /**
19  * A {@link GraphExplorer} tree {@link NodeContext} path denotes a model element
20  * in a tree viewer. Tree path objects have value semantics. A model element is
21  * represented by a path of elements in the tree from the root element to the
22  * leaf element.
23  * <p>
24  * Clients may instantiate this class. Not intended to be subclassed.
25  * </p>
26  * 
27  * <p>
28  * Directly imitated for the Simantics browsing framework from JFace
29  * <code>org.eclipse.jface.viewer.TreePath</code> class.
30  * </p>
31  * 
32  * @author Tuukka Lehtonen
33  */
34 public final class NodeContextPath implements IAdaptable {
35
36     public static final NodeContextPath[] NONE  = {};
37
38     /**
39      * Constant for representing an empty tree path.
40      */
41     public static final NodeContextPath   EMPTY = new NodeContextPath(NodeContext.NONE);
42
43     private final NodeContext[]           segments;
44
45     private int                           hash;
46
47     /**
48      * Constructs a path identifying a leaf node in a tree.
49      * 
50      * @param segments
51      *            path of elements to a leaf node in a tree, starting with the
52      *            root element
53      */
54     public NodeContextPath(NodeContext... segments) {
55         Assert.isNotNull(segments);
56         for (int i = 0; i < segments.length; i++) {
57             Assert.isNotNull(segments[i]);
58         }
59         this.segments = segments;
60     }
61
62     /**
63      * Returns the element at the specified index in this path.
64      * 
65      * @param index
66      *            index of element to return
67      * @return element at the specified index
68      */
69     public NodeContext getSegment(int index) {
70         return segments[index];
71     }
72
73     /**
74      * Returns the number of elements in this path.
75      * 
76      * @return the number of elements in this path
77      */
78     public int getSegmentCount() {
79         return segments.length;
80     }
81
82     /**
83      * Returns the first element in this path, or <code>null</code> if this
84      * path has no segments.
85      * 
86      * @return the first element in this path
87      */
88     public NodeContext getFirstSegment() {
89         if (segments.length == 0) {
90             return null;
91         }
92         return segments[0];
93     }
94
95     /**
96      * Returns the last element in this path, or <code>null</code> if this
97      * path has no segments.
98      * 
99      * @return the last element in this path
100      */
101     public NodeContext getLastSegment() {
102         if (segments.length == 0) {
103             return null;
104         }
105         return segments[segments.length - 1];
106     }
107
108     /**
109      * @return all the segments in the path as an array
110      */
111     public NodeContext[] getSegments() {
112         if (segments.length == 0)
113             return segments;
114         return Arrays.copyOf(segments, segments.length);
115     }
116
117     /*
118      * (non-Javadoc)
119      * 
120      * @see java.lang.Object#equals(java.lang.Object)
121      */
122     public boolean equals(Object other) {
123         if (!(other instanceof NodeContextPath)) {
124             return false;
125         }
126         return equals((NodeContextPath) other);
127     }
128
129     /**
130      * (non-Javadoc)
131      * 
132      * @see java.lang.Object#hashCode()
133      */
134     public int hashCode() {
135         if (hash == 0) {
136             hash = hashCode0();
137         }
138         return hash;
139     }
140
141     /**
142      * Returns a hash code computed from the hash codes of the segments, using
143      * the given comparer to compute the hash codes of the segments.
144      * 
145      * @param comparer
146      *            comparer to use or <code>null</code> if the segments' hash
147      *            codes should be computed by calling their hashCode() methods.
148      * @return the computed hash code
149      */
150     private int hashCode0() {
151         int result = 0;
152         for (int i = 0; i < segments.length; i++) {
153             result += segments[i].hashCode();
154         }
155         return result;
156     }
157
158     /**
159      * Returns whether this path is equivalent to the given path using the
160      * specified comparer to compare individual elements.
161      * 
162      * @param otherPath
163      *            tree path to compare to
164      * @param comparer
165      *            comparator to use or <code>null</code> if segments should be
166      *            compared using equals()
167      * @return whether the paths are equal
168      */
169     public boolean equals(NodeContextPath otherPath) {
170         if (otherPath == null) {
171             return false;
172         }
173         if (segments.length != otherPath.segments.length) {
174             return false;
175         }
176         for (int i = 0; i < segments.length; i++) {
177             if (!segments[i].equals(otherPath.segments[i])) {
178                 return false;
179             }
180         }
181         return true;
182     }
183
184     /**
185      * Returns whether this path starts with the same segments as the given
186      * path, using the given comparer to compare segments.
187      * 
188      * @param treePath
189      *            path to compare to
190      * @param comparer
191      *            the comparer to use, or <code>null</code> if equals() should
192      *            be used to compare segments
193      * @return whether the given path is a prefix of this path, or the same as
194      *         this path
195      */
196     public boolean startsWith(NodeContextPath treePath) {
197         int thisSegmentCount = getSegmentCount();
198         int otherSegmentCount = treePath.getSegmentCount();
199         if (otherSegmentCount == thisSegmentCount) {
200             return equals(treePath);
201         }
202         if (otherSegmentCount > thisSegmentCount) {
203             return false;
204         }
205         for (int i = 0; i < otherSegmentCount; i++) {
206             Object otherSegment = treePath.getSegment(i);
207             if (!otherSegment.equals(segments[i])) {
208                 return false;
209             }
210         }
211         return true;
212     }
213
214     /**
215      * Returns a copy of this tree path with one segment removed from the end,
216      * or <code>null</code> if this tree path has no segments.
217      * @return a tree path
218      */
219     public NodeContextPath getParentPath() {
220         int segmentCount = getSegmentCount();
221         if (segmentCount < 1) {
222             return null;
223         } else if (segmentCount == 1) {
224             return EMPTY;
225         }
226         NodeContext[] parentSegments = new NodeContext[segmentCount - 1];
227         System.arraycopy(segments, 0, parentSegments, 0, segmentCount - 1);
228         return new NodeContextPath(parentSegments);
229     }
230
231     /**
232      * Returns a copy of this tree path with the given segment added at the end.
233      * @param newSegment 
234      * @return a tree path
235      */
236     public NodeContextPath createChildPath(NodeContext newSegment) {
237         int segmentCount = getSegmentCount();
238         NodeContext[] childSegments = new NodeContext[segmentCount + 1];
239         if(segmentCount>0) {
240             System.arraycopy(segments, 0, childSegments, 0, segmentCount);
241         }
242         childSegments[segmentCount] = newSegment;
243         return new NodeContextPath(childSegments);
244     }
245
246     @SuppressWarnings("unchecked")
247     @Override
248     public <T> T getAdapter(Class<T> adapter) {
249         if (NodeContext.class == adapter)
250             return (T) getLastSegment();
251         NodeContext last = getLastSegment();
252         if (last instanceof IAdaptable)
253             return ((IAdaptable) last).getAdapter(adapter);
254         return null;
255     }
256
257 }