Commit | Line | Data |
---|---|---|
a52fde77 | 1 | /******************************************************************************* |
60ae41e1 | 2 | * Copyright (c) 2012, 2014 Ericsson |
a52fde77 AM |
3 | * Copyright (c) 2010, 2011 École Polytechnique de Montréal |
4 | * Copyright (c) 2010, 2011 Alexandre Montplaisir <alexandre.montplaisir@gmail.com> | |
cb42195c | 5 | * |
a52fde77 AM |
6 | * All rights reserved. This program and the accompanying materials are |
7 | * made available under the terms of the Eclipse Public License v1.0 which | |
8 | * accompanies this distribution, and is available at | |
9 | * http://www.eclipse.org/legal/epl-v10.html | |
cb42195c | 10 | * |
a52fde77 AM |
11 | *******************************************************************************/ |
12 | ||
18ab1d18 | 13 | package org.eclipse.linuxtools.internal.tmf.core.statesystem; |
a52fde77 AM |
14 | |
15 | import java.io.PrintWriter; | |
cb42195c | 16 | import java.util.ArrayList; |
a52fde77 AM |
17 | import java.util.Collections; |
18 | import java.util.HashMap; | |
19 | import java.util.LinkedList; | |
20 | import java.util.List; | |
cb42195c | 21 | import java.util.Map; |
a52fde77 AM |
22 | |
23 | /** | |
24 | * An Attribute is a "node" in the Attribute Tree. It represents a smallest | |
25 | * unit of the model which can be in a particular state at a given time. | |
cb42195c | 26 | * |
a52fde77 AM |
27 | * It is abstract, as different implementations can provide different ways to |
28 | * access sub-attributes | |
cb42195c | 29 | * |
a52fde77 | 30 | * @author alexmont |
cb42195c | 31 | * |
a52fde77 | 32 | */ |
a6917276 | 33 | public abstract class Attribute { |
a52fde77 AM |
34 | |
35 | private final Attribute parent; | |
36 | private final String name; | |
37 | private final int quark; | |
a6917276 AM |
38 | |
39 | /** The list of sub-attributes */ | |
cb42195c | 40 | protected final List<Attribute> subAttributes; |
a52fde77 AM |
41 | |
42 | /** | |
43 | * Constructor | |
a6917276 AM |
44 | * |
45 | * @param parent | |
46 | * The parent attribute of this one. Can be 'null' to represent | |
47 | * this attribute is the root node of the tree. | |
48 | * @param name | |
49 | * Base name of this attribute | |
50 | * @param quark | |
51 | * The integer representation of this attribute | |
a52fde77 | 52 | */ |
a6917276 | 53 | public Attribute(Attribute parent, String name, int quark) { |
a52fde77 AM |
54 | this.parent = parent; |
55 | this.quark = quark; | |
56 | this.name = name; | |
a4524c1b | 57 | this.subAttributes = new ArrayList<>(); |
a52fde77 AM |
58 | } |
59 | ||
a6917276 AM |
60 | // ------------------------------------------------------------------------ |
61 | // Accessors | |
62 | // ------------------------------------------------------------------------ | |
63 | ||
a52fde77 | 64 | /** |
a6917276 AM |
65 | * Get the quark (integer representation) of this attribute. |
66 | * | |
67 | * @return The quark of this attribute | |
a52fde77 | 68 | */ |
a6917276 | 69 | public int getQuark() { |
a52fde77 AM |
70 | return quark; |
71 | } | |
72 | ||
a6917276 AM |
73 | /** |
74 | * Get the name of this attribute. | |
75 | * | |
76 | * @return The name of this attribute | |
77 | */ | |
78 | public String getName() { | |
79 | return name; | |
a52fde77 AM |
80 | } |
81 | ||
a6917276 AM |
82 | /** |
83 | * Get the list of child attributes below this one. This is a read-only | |
84 | * view. | |
85 | * | |
86 | * @return The list of child attributes. | |
87 | */ | |
88 | public List<Attribute> getSubAttributes() { | |
cb42195c | 89 | return Collections.unmodifiableList(subAttributes); |
a52fde77 AM |
90 | } |
91 | ||
a52fde77 AM |
92 | /** |
93 | * Get the matching quark for a given path-of-strings | |
cb42195c | 94 | * |
a52fde77 AM |
95 | * @param path |
96 | * The path we are looking for, *relative to this node*. | |
97 | * @return The matching quark, or -1 if that attribute does not exist. | |
98 | */ | |
a6917276 | 99 | public int getSubAttributeQuark(String... path) { |
a52fde77 AM |
100 | return this.getSubAttributeQuark(path, 0); |
101 | } | |
102 | ||
103 | /** | |
104 | * Other method to search through the attribute tree, but instead of | |
105 | * returning the matching quark we return the AttributeTreeNode object | |
106 | * itself. It can then be used as new "root node" for faster queries on the | |
107 | * tree. | |
cb42195c | 108 | * |
a52fde77 AM |
109 | * @param path |
110 | * The target path, *relative to this node* | |
111 | * @return The Node object matching the last element in the path, or "null" | |
112 | * if that attribute does not exist. | |
113 | */ | |
a6917276 | 114 | public Attribute getSubAttributeNode(String... path) { |
a52fde77 AM |
115 | return this.getSubAttributeNode(path, 0); |
116 | } | |
117 | ||
118 | /** | |
119 | * "Inner" part of the previous public method, which is used recursively. To | |
120 | * avoid having to copy sub-arrays to pass down, we just track where we are | |
121 | * at with the index parameter. It uses getSubAttributeNode(), whose | |
122 | * implementation is left to the derived classes. | |
123 | */ | |
124 | private int getSubAttributeQuark(String[] path, int index) { | |
125 | Attribute targetNode = this.getSubAttributeNode(path, index); | |
126 | if (targetNode == null) { | |
127 | return -1; | |
128 | } | |
129 | return targetNode.getQuark(); | |
130 | } | |
131 | ||
0fdd2c45 FG |
132 | /** |
133 | * Get the parent attribute of this attribute | |
134 | * | |
135 | * @return The parent attribute | |
136 | */ | |
137 | public Attribute getParentAttribute() { | |
138 | return this.parent; | |
139 | } | |
140 | ||
141 | /** | |
142 | * Get the parent quark of this attribute | |
143 | * | |
144 | * @return The quark of the parent attribute | |
145 | */ | |
146 | public int getParentAttributeQuark() { | |
147 | return this.parent.getQuark(); | |
148 | } | |
149 | ||
a52fde77 | 150 | /* The methods how to access children are left to derived classes */ |
a6917276 AM |
151 | |
152 | /** | |
153 | * Add a sub-attribute to this attribute | |
154 | * | |
155 | * @param newSubAttribute The new attribute to add | |
156 | */ | |
157 | protected abstract void addSubAttribute(Attribute newSubAttribute); | |
158 | ||
159 | /** | |
160 | * Get a sub-attribute from this node's sub-attributes | |
161 | * | |
162 | * @param path | |
163 | * The *full* path to the attribute | |
164 | * @param index | |
165 | * The index in 'path' where this attribute is located | |
166 | * (indicating where to start searching). | |
167 | * @return The requested attribute | |
168 | */ | |
169 | protected abstract Attribute getSubAttributeNode(String[] path, int index); | |
a52fde77 AM |
170 | |
171 | /** | |
172 | * Return a String array composed of the full (absolute) path representing | |
173 | * this attribute | |
cb42195c | 174 | * |
a52fde77 AM |
175 | * @return |
176 | */ | |
a6917276 | 177 | private String[] getFullAttribute() { |
a4524c1b | 178 | LinkedList<String> list = new LinkedList<>(); |
a52fde77 AM |
179 | Attribute curNode = this; |
180 | ||
181 | /* Add recursive parents to the list, but stop at the root node */ | |
a6917276 | 182 | while (curNode.parent != null) { |
e2af45f9 | 183 | list.addFirst(curNode.getName()); |
a6917276 | 184 | curNode = curNode.parent; |
a52fde77 AM |
185 | } |
186 | ||
a52fde77 AM |
187 | return list.toArray(new String[0]); |
188 | } | |
189 | ||
190 | /** | |
191 | * Return the absolute path of this attribute, as a single slash-separated | |
192 | * String. | |
cb42195c | 193 | * |
a6917276 | 194 | * @return The full name of this attribute |
a52fde77 | 195 | */ |
a6917276 | 196 | public String getFullAttributeName() { |
ab604305 AM |
197 | String[] array = this.getFullAttribute(); |
198 | StringBuffer buf = new StringBuffer(); | |
a52fde77 | 199 | |
a52fde77 | 200 | for (int i = 0; i < array.length - 1; i++) { |
ab604305 AM |
201 | buf.append(array[i]); |
202 | buf.append('/'); | |
a52fde77 | 203 | } |
ab604305 AM |
204 | buf.append(array[array.length - 1]); |
205 | return buf.toString(); | |
a52fde77 AM |
206 | } |
207 | ||
208 | @Override | |
209 | public String toString() { | |
210 | return getFullAttributeName() + " (" + quark + ')'; //$NON-NLS-1$ | |
211 | } | |
212 | ||
213 | private int curDepth; | |
214 | ||
215 | private void attributeNodeToString(PrintWriter writer, Attribute currentNode) { | |
216 | int j; | |
217 | ||
218 | writer.println(currentNode.getName() + " (" + currentNode.quark + ')'); //$NON-NLS-1$ | |
219 | curDepth++; | |
220 | ||
cb42195c | 221 | for (Attribute nextNode : currentNode.getSubAttributes()) { |
a52fde77 AM |
222 | /* Skip printing 'null' entries */ |
223 | if (nextNode == null) { | |
224 | continue; | |
225 | } | |
226 | for (j = 0; j < curDepth - 1; j++) { | |
227 | writer.print(" "); //$NON-NLS-1$ | |
228 | } | |
229 | writer.print(" "); //$NON-NLS-1$ | |
230 | attributeNodeToString(writer, nextNode); | |
231 | } | |
232 | curDepth--; | |
233 | return; | |
234 | } | |
235 | ||
a6917276 AM |
236 | /** |
237 | * Debugging method to print the contents of this attribute | |
238 | * | |
239 | * @param writer | |
240 | * PrintWriter where to write the information | |
241 | */ | |
242 | public void debugPrint(PrintWriter writer) { | |
a52fde77 AM |
243 | /* Only used for debugging, shouldn't be externalized */ |
244 | writer.println("------------------------------"); //$NON-NLS-1$ | |
245 | writer.println("Attribute tree: (quark)\n"); //$NON-NLS-1$ | |
246 | curDepth = 0; | |
247 | attributeNodeToString(writer, this); | |
248 | writer.print('\n'); | |
249 | } | |
250 | } | |
251 | ||
252 | /** | |
253 | * This is the basic implementation, where sub-attributes names can be composed | |
254 | * of any alphanumeric characters, and are stored as Strings. A HashMap is used | |
255 | * to access them. | |
cb42195c | 256 | * |
a52fde77 | 257 | * @author alexmont |
cb42195c | 258 | * |
a52fde77 AM |
259 | */ |
260 | final class AlphaNumAttribute extends Attribute { | |
261 | ||
a6917276 | 262 | private final Map<String, Integer> subAttributesMap; |
a52fde77 | 263 | |
a6917276 | 264 | public AlphaNumAttribute(Attribute parent, String name, int quark) { |
a52fde77 | 265 | super(parent, name, quark); |
a4524c1b | 266 | this.subAttributesMap = new HashMap<>(); |
a52fde77 AM |
267 | } |
268 | ||
269 | @Override | |
a6917276 | 270 | protected synchronized void addSubAttribute(Attribute newSubAttribute) { |
a52fde77 AM |
271 | assert (newSubAttribute != null); |
272 | assert (newSubAttribute.getName() != null); | |
273 | /* This should catch buggy state changing statements */ | |
274 | assert (!newSubAttribute.getName().equals(this.getName())); | |
275 | ||
276 | subAttributesMap.put(newSubAttribute.getName(), subAttributes.size()); | |
277 | subAttributes.add(newSubAttribute); | |
278 | } | |
279 | ||
280 | @Override | |
281 | protected synchronized Attribute getSubAttributeNode(String[] path, | |
282 | int index) { | |
283 | Integer indexOfNextNode = subAttributesMap.get(path[index]); | |
284 | Attribute nextNode; | |
285 | ||
286 | if (indexOfNextNode == null) { | |
287 | /* We don't have the expected child => the attribute does not exist */ | |
288 | return null; | |
289 | } | |
290 | if (index == path.length - 1) { | |
291 | /* It's our job to process this request */ | |
292 | return subAttributes.get(indexOfNextNode); | |
293 | } | |
294 | ||
295 | nextNode = subAttributes.get(indexOfNextNode); | |
296 | return nextNode.getSubAttributeNode(path, index + 1); | |
297 | } | |
298 | } |