|
1 /******************************************************************************* |
|
2 * Copyright (c) 2000, 2005 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.eclipse.core.internal.dtree; |
|
12 |
|
13 import org.eclipse.core.runtime.IPath; |
|
14 |
|
15 /** |
|
16 * A <code>DataTree</code> represents the complete information of a source tree. |
|
17 * The tree contains all information within its branches, and has no relation to any |
|
18 * other source trees (no parent and no children). |
|
19 */ |
|
20 public class DataTree extends AbstractDataTree { |
|
21 |
|
22 /** |
|
23 * the root node of the tree |
|
24 */ |
|
25 private DataTreeNode rootNode; |
|
26 |
|
27 /** |
|
28 * Creates a new empty tree |
|
29 */ |
|
30 public DataTree() { |
|
31 this.empty(); |
|
32 } |
|
33 |
|
34 /** |
|
35 * Returns a copy of the node subtree rooted at the given key. |
|
36 * |
|
37 * @param key |
|
38 * Key of subtree to copy |
|
39 */ |
|
40 public AbstractDataTreeNode copyCompleteSubtree(IPath key) { |
|
41 DataTreeNode node = findNodeAt(key); |
|
42 if (node == null) { |
|
43 handleNotFound(key); |
|
44 } |
|
45 return copyHierarchy(node); |
|
46 } |
|
47 |
|
48 /** |
|
49 * Returns a deep copy of a node and all its children. |
|
50 * |
|
51 * @param node |
|
52 * Node to be copied. |
|
53 */ |
|
54 DataTreeNode copyHierarchy(DataTreeNode node) { |
|
55 DataTreeNode newNode; |
|
56 int size = node.size(); |
|
57 if (size == 0) { |
|
58 newNode = new DataTreeNode(node.getName(), node.getData()); |
|
59 } else { |
|
60 AbstractDataTreeNode[] children = node.getChildren(); |
|
61 DataTreeNode[] newChildren = new DataTreeNode[size]; |
|
62 for (int i = size; --i >= 0;) { |
|
63 newChildren[i] = this.copyHierarchy((DataTreeNode) children[i]); |
|
64 } |
|
65 newNode = new DataTreeNode(node.getName(), node.getData(), newChildren); |
|
66 } |
|
67 |
|
68 return newNode; |
|
69 } |
|
70 |
|
71 /** |
|
72 * Creates a new child in the tree. |
|
73 * @see AbstractDataTree#createChild(IPath, String) |
|
74 */ |
|
75 public void createChild(IPath parentKey, String localName) { |
|
76 createChild(parentKey, localName, null); |
|
77 } |
|
78 |
|
79 /** |
|
80 * Creates a new child in the tree. |
|
81 * @see AbstractDataTree#createChild(IPath, String, Object) |
|
82 */ |
|
83 public void createChild(IPath parentKey, String localName, Object data) { |
|
84 DataTreeNode node = findNodeAt(parentKey); |
|
85 if (node == null) |
|
86 handleNotFound(parentKey); |
|
87 if (this.isImmutable()) |
|
88 handleImmutableTree(); |
|
89 /* If node already exists, replace */ |
|
90 if (node.includesChild(localName)) { |
|
91 node.replaceChild(localName, new DataTreeNode(localName, data)); |
|
92 } else { |
|
93 this.replaceNode(parentKey, node.copyWithNewChild(localName, new DataTreeNode(localName, data))); |
|
94 } |
|
95 } |
|
96 |
|
97 /** |
|
98 * Creates and returns an instance of the receiver. This is an |
|
99 * implementation of the factory method creational pattern for allowing |
|
100 * abstract methods to create instances |
|
101 */ |
|
102 protected AbstractDataTree createInstance() { |
|
103 return new DataTree(); |
|
104 } |
|
105 |
|
106 /** |
|
107 * Creates or replaces a subtree in the tree. The parent node must exist. |
|
108 * |
|
109 * @param key |
|
110 * Key of parent node whose subtree we want to create/replace. |
|
111 * @param subtree |
|
112 * New node to insert into tree. |
|
113 */ |
|
114 public void createSubtree(IPath key, AbstractDataTreeNode subtree) { |
|
115 |
|
116 // Copy it since destructive mods are allowed on the original |
|
117 // and shouldn't affect this tree. |
|
118 DataTreeNode newNode = copyHierarchy((DataTreeNode) subtree); |
|
119 |
|
120 if (this.isImmutable()) { |
|
121 handleImmutableTree(); |
|
122 } |
|
123 |
|
124 if (key.isRoot()) { |
|
125 setRootNode(newNode); |
|
126 } else { |
|
127 String localName = key.lastSegment(); |
|
128 newNode.setName(localName); // Another mod, but it's OK we've already copied |
|
129 |
|
130 IPath parentKey = key.removeLastSegments(1); |
|
131 |
|
132 DataTreeNode node = findNodeAt(parentKey); |
|
133 if (node == null) { |
|
134 handleNotFound(parentKey); |
|
135 } |
|
136 |
|
137 /* If node already exists, replace */ |
|
138 if (node.includesChild(localName)) { |
|
139 node.replaceChild(localName, newNode); |
|
140 } |
|
141 |
|
142 this.replaceNode(parentKey, node.copyWithNewChild(localName, newNode)); |
|
143 } |
|
144 } |
|
145 |
|
146 /** |
|
147 * Deletes a child of the tree. |
|
148 * @see AbstractDataTree#deleteChild(IPath, String) |
|
149 */ |
|
150 public void deleteChild(IPath parentKey, String localName) { |
|
151 if (this.isImmutable()) |
|
152 handleImmutableTree(); |
|
153 DataTreeNode node = findNodeAt(parentKey); |
|
154 if (node == null || (!node.includesChild(localName))) { |
|
155 handleNotFound(node == null ? parentKey : parentKey.append(localName)); |
|
156 } else { |
|
157 this.replaceNode(parentKey, node.copyWithoutChild(localName)); |
|
158 } |
|
159 } |
|
160 |
|
161 /** |
|
162 * Initializes the receiver. |
|
163 * @see AbstractDataTree#empty() |
|
164 */ |
|
165 public void empty() { |
|
166 this.setRootNode(new DataTreeNode(null, null)); |
|
167 } |
|
168 |
|
169 /** |
|
170 * Returns the specified node if it is present, otherwise returns null. |
|
171 * |
|
172 * @param key |
|
173 * Key of node to return |
|
174 */ |
|
175 public DataTreeNode findNodeAt(IPath key) { |
|
176 AbstractDataTreeNode node = this.getRootNode(); |
|
177 int keyLength = key.segmentCount(); |
|
178 for (int i = 0; i < keyLength; i++) { |
|
179 try { |
|
180 node = node.childAt(key.segment(i)); |
|
181 } catch (ObjectNotFoundException notFound) { |
|
182 return null; |
|
183 } |
|
184 } |
|
185 return (DataTreeNode) node; |
|
186 } |
|
187 |
|
188 /** |
|
189 * Returns the data at the specified node. |
|
190 * |
|
191 * @param key |
|
192 * Node whose data to return. |
|
193 */ |
|
194 public Object getData(IPath key) { |
|
195 DataTreeNode node = findNodeAt(key); |
|
196 if (node == null) { |
|
197 handleNotFound(key); |
|
198 return null; |
|
199 } |
|
200 return node.getData(); |
|
201 } |
|
202 |
|
203 /** |
|
204 * Returns the names of the children of a node. |
|
205 * @see AbstractDataTree#getNamesOfChildren(IPath) |
|
206 */ |
|
207 public String[] getNamesOfChildren(IPath parentKey) { |
|
208 DataTreeNode parentNode; |
|
209 parentNode = findNodeAt(parentKey); |
|
210 if (parentNode == null) { |
|
211 handleNotFound(parentKey); |
|
212 return null; |
|
213 } |
|
214 return parentNode.namesOfChildren(); |
|
215 } |
|
216 |
|
217 /** |
|
218 * Returns the root node of the tree |
|
219 */ |
|
220 AbstractDataTreeNode getRootNode() { |
|
221 return rootNode; |
|
222 } |
|
223 |
|
224 /** |
|
225 * Returns true if the receiver includes a node with |
|
226 * the given key, false otherwise. |
|
227 */ |
|
228 public boolean includes(IPath key) { |
|
229 return (findNodeAt(key) != null); |
|
230 } |
|
231 |
|
232 /** |
|
233 * Returns an object containing: |
|
234 * - a flag indicating whether the specified node was found |
|
235 * - the data for the node, if it was found |
|
236 * @param key |
|
237 * key of node for which we want to retrieve data. |
|
238 */ |
|
239 public DataTreeLookup lookup(IPath key) { |
|
240 DataTreeNode node = this.findNodeAt(key); |
|
241 if (node == null) |
|
242 return DataTreeLookup.newLookup(key, false, null); |
|
243 return DataTreeLookup.newLookup(key, true, node.getData()); |
|
244 } |
|
245 |
|
246 /** |
|
247 * Replaces the node at the specified key with the given node |
|
248 */ |
|
249 protected void replaceNode(IPath key, DataTreeNode node) { |
|
250 DataTreeNode found; |
|
251 if (key.isRoot()) { |
|
252 this.setRootNode(node); |
|
253 } else { |
|
254 found = this.findNodeAt(key.removeLastSegments(1)); |
|
255 found.replaceChild(key.lastSegment(), node); |
|
256 } |
|
257 } |
|
258 |
|
259 /** |
|
260 * Sets the data at the specified node. |
|
261 * @see AbstractDataTree#setData(IPath, Object) |
|
262 */ |
|
263 public void setData(IPath key, Object data) { |
|
264 DataTreeNode node = this.findNodeAt(key); |
|
265 if (this.isImmutable()) |
|
266 handleImmutableTree(); |
|
267 if (node == null) { |
|
268 handleNotFound(key); |
|
269 } else { |
|
270 node.setData(data); |
|
271 } |
|
272 } |
|
273 |
|
274 /** |
|
275 * Sets the root node of the tree |
|
276 * @see AbstractDataTree#setRootNode(AbstractDataTreeNode) |
|
277 */ |
|
278 void setRootNode(DataTreeNode aNode) { |
|
279 rootNode = aNode; |
|
280 } |
|
281 } |