1 /*******************************************************************************
2 * Copyright (c) 2012, 2014 Ericsson
3 * Copyright (c) 2010, 2011 École Polytechnique de Montréal
4 * Copyright (c) 2010, 2011 Alexandre Montplaisir <alexandre.montplaisir@gmail.com>
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
11 *******************************************************************************/
13 package org
.eclipse
.linuxtools
.internal
.tmf
.core
.statesystem
;
16 import java
.util
.ArrayList
;
17 import java
.util
.Arrays
;
18 import java
.util
.Collections
;
19 import java
.util
.List
;
21 import org
.eclipse
.linuxtools
.tmf
.core
.exceptions
.AttributeNotFoundException
;
22 import org
.eclipse
.linuxtools
.tmf
.core
.exceptions
.StateValueTypeException
;
23 import org
.eclipse
.linuxtools
.tmf
.core
.exceptions
.TimeRangeException
;
24 import org
.eclipse
.linuxtools
.tmf
.core
.statevalue
.TmfStateValue
;
27 * The Attribute Tree is the /proc-like filesystem used to organize attributes.
28 * Each node of this tree is both like a file and a directory in the
34 public final class AttributeTree
{
36 /* "Magic number" for attribute tree files or file sections */
37 private static final int ATTRIB_TREE_MAGIC_NUMBER
= 0x06EC3671;
39 private final StateSystem ss
;
40 private final List
<Attribute
> attributeList
;
41 private final Attribute attributeTreeRoot
;
44 * Standard constructor, create a new empty Attribute Tree
47 * The StateSystem to which this AT is attached
49 public AttributeTree(StateSystem ss
) {
51 this.attributeList
= Collections
.synchronizedList(new ArrayList
<Attribute
>());
52 this.attributeTreeRoot
= new Attribute(null, "root", -1); //$NON-NLS-1$
56 * "Existing file" constructor. Builds an attribute tree from a
57 * "mapping file" or mapping section previously saved somewhere.
60 * StateSystem to which this AT is attached
62 * File stream where to read the AT information. Make sure it's
63 * sought at the right place!
65 * If there is a problem reading from the file stream
67 public AttributeTree(StateSystem ss
, FileInputStream fis
) throws IOException
{
69 DataInputStream in
= new DataInputStream(new BufferedInputStream(fis
));
71 /* Message for exceptions, shouldn't be externalized */
72 final String errorMessage
= "The attribute tree file section is either invalid or corrupted."; //$NON-NLS-1$
74 ArrayList
<String
[]> list
= new ArrayList
<>();
77 String
[] curStringArray
;
78 int res
, remain
, size
;
82 /* Read the header of the Attribute Tree file (or file section) */
83 res
= in
.readInt(); /* Magic number */
84 if (res
!= ATTRIB_TREE_MAGIC_NUMBER
) {
85 throw new IOException(errorMessage
);
88 /* Expected size of the section */
89 expectedSize
= in
.readInt();
90 if (expectedSize
< 12) {
91 throw new IOException(errorMessage
);
94 /* How many entries we have to read */
95 remain
= in
.readInt();
99 for (; remain
> 0; remain
--) {
100 /* Read the first byte = the size of the entry */
101 size
= in
.readByte();
102 curByteArray
= new byte[size
];
103 res
= in
.read(curByteArray
);
105 throw new IOException(errorMessage
);
109 * Go buffer -> byteArray -> String -> String[] -> insert in list.
112 curFullString
= new String(curByteArray
);
113 curStringArray
= curFullString
.split("/"); //$NON-NLS-1$
114 list
.add(curStringArray
);
116 /* Read the 0'ed confirmation byte */
119 throw new IOException(errorMessage
);
121 total
+= curByteArray
.length
+ 2;
124 if (total
!= expectedSize
) {
125 throw new IOException(errorMessage
);
129 * Now we have 'list', the ArrayList of String arrays representing all
130 * the attributes. Simply create attributes the normal way from them.
132 for (String
[] attrib
: list
) {
133 this.getQuarkAndAdd(-1, attrib
);
138 * Tell the Attribute Tree to write itself somewhere in a file.
141 * The file to write to
143 * The position (in bytes) in the file where to write
144 * @return The total number of bytes written.
146 public int writeSelf(File file
, long pos
) {
150 try (RandomAccessFile raf
= new RandomAccessFile(file
, "rw");) { //$NON-NLS-1$
153 /* Write the almost-magic number */
154 raf
.writeInt(ATTRIB_TREE_MAGIC_NUMBER
);
156 /* Placeholder for the total size of the section... */
159 /* Write the number of entries */
160 raf
.writeInt(this.attributeList
.size());
163 /* Write the attributes themselves */
164 for (Attribute entry
: this.attributeList
) {
165 curByteArray
= entry
.getFullAttributeName().getBytes();
166 if (curByteArray
.length
> Byte
.MAX_VALUE
) {
167 throw new IOException("Attribute with name \"" //$NON-NLS-1$
168 + Arrays
.toString(curByteArray
) + "\" is too long."); //$NON-NLS-1$
170 /* Write the first byte = size of the array */
171 raf
.writeByte((byte) curByteArray
.length
);
173 /* Write the array itself */
174 raf
.write(curByteArray
);
176 /* Write the 0'ed byte */
177 raf
.writeByte((byte) 0);
179 total
+= curByteArray
.length
+ 2;
182 /* Now go back and write the actual size of this section */
186 } catch (IOException e
) {
193 * Return the number of attributes this system as seen so far. Note that
194 * this also equals the integer value (quark) the next added attribute will
197 * @return The current number of attributes in the tree
199 public int getNbAttributes() {
200 return attributeList
.size();
204 * Get the quark for a given attribute path. No new attribute will be
205 * created : if the specified path does not exist, throw an error.
207 * @param startingNodeQuark
208 * The quark of the attribute from which relative queries will
209 * start. Use '-1' to start at the root node.
211 * The path to the attribute, relative to the starting node.
212 * @return The quark of the specified attribute
213 * @throws AttributeNotFoundException
214 * If the specified path was not found
216 public int getQuarkDontAdd(int startingNodeQuark
, String
... subPath
)
217 throws AttributeNotFoundException
{
218 assert (startingNodeQuark
>= -1);
222 /* If subPath is empty, simply return the starting quark */
223 if (subPath
== null || subPath
.length
== 0) {
224 return startingNodeQuark
;
227 /* Get the "starting node" */
228 if (startingNodeQuark
== -1) {
229 prevNode
= attributeTreeRoot
;
231 prevNode
= attributeList
.get(startingNodeQuark
);
234 int knownQuark
= prevNode
.getSubAttributeQuark(subPath
);
235 if (knownQuark
== -1) {
237 * The attribute doesn't exist, but we have been specified to NOT
238 * add any new attributes.
240 throw new AttributeNotFoundException();
243 * The attribute was already existing, return the quark of that
250 * Get the quark of a given attribute path. If that specified path does not
251 * exist, it will be created (and the quark that was just created will be
254 * @param startingNodeQuark
255 * The quark of the attribute from which relative queries will
256 * start. Use '-1' to start at the root node.
258 * The path to the attribute, relative to the starting node.
259 * @return The quark of the attribute represented by the path
261 public synchronized int getQuarkAndAdd(int startingNodeQuark
, String
... subPath
) {
262 // FIXME synchronized here is probably quite costly... maybe only locking
263 // the "for" would be enough?
264 assert (subPath
!= null && subPath
.length
> 0);
265 assert (startingNodeQuark
>= -1);
267 Attribute nextNode
= null;
270 /* Get the "starting node" */
271 if (startingNodeQuark
== -1) {
272 prevNode
= attributeTreeRoot
;
274 prevNode
= attributeList
.get(startingNodeQuark
);
277 int knownQuark
= prevNode
.getSubAttributeQuark(subPath
);
278 if (knownQuark
== -1) {
280 * The attribute was not in the table previously, and we want to add
283 for (String curDirectory
: subPath
) {
284 nextNode
= prevNode
.getSubAttributeNode(curDirectory
);
285 if (nextNode
== null) {
286 /* This is where we need to start adding */
287 nextNode
= new Attribute(prevNode
, curDirectory
, attributeList
.size());
288 prevNode
.addSubAttribute(nextNode
);
289 attributeList
.add(nextNode
);
290 ss
.addEmptyAttribute();
295 * Insert an initial null value for this attribute in the state
296 * system (in case the state provider doesn't set one).
298 final int newAttrib
= attributeList
.size() - 1;
300 ss
.modifyAttribute(ss
.getStartTime(), TmfStateValue
.nullValue(), newAttrib
);
301 } catch (TimeRangeException e
) {
302 /* Should not happen, we're inserting at ss's start time */
303 throw new IllegalStateException(e
);
304 } catch (AttributeNotFoundException e
) {
305 /* Should not happen, we just created this attribute! */
306 throw new IllegalStateException(e
);
307 } catch (StateValueTypeException e
) {
308 /* Should not happen, there is no existing state value, and the
309 * one we insert is a null value anyway. */
310 throw new IllegalStateException(e
);
316 * The attribute was already existing, return the quark of that
323 * Returns the sub-attributes of the quark passed in parameter
325 * @param attributeQuark
326 * The quark of the attribute to print the sub-attributes of.
328 * Should the query be recursive or not? If false, only children
329 * one level deep will be returned. If true, all descendants will
330 * be returned (depth-first search)
331 * @return The list of quarks representing the children attributes
332 * @throws AttributeNotFoundException
333 * If 'attributeQuark' is invalid, or if there is no attrbiute
336 public List
<Integer
> getSubAttributes(int attributeQuark
, boolean recursive
)
337 throws AttributeNotFoundException
{
338 List
<Integer
> listOfChildren
= new ArrayList
<>();
339 Attribute startingAttribute
;
341 /* Check if the quark is valid */
342 if (attributeQuark
< -1 || attributeQuark
>= attributeList
.size()) {
343 throw new AttributeNotFoundException();
346 /* Set up the node from which we'll start the search */
347 if (attributeQuark
== -1) {
348 startingAttribute
= attributeTreeRoot
;
350 startingAttribute
= attributeList
.get(attributeQuark
);
353 /* Iterate through the sub-attributes and add them to the list */
354 addSubAttributes(listOfChildren
, startingAttribute
, recursive
);
356 return listOfChildren
;
360 * Returns the parent quark of the attribute. The root attribute has no
361 * parent and will return <code>-1</code>
364 * The quark of the attribute
365 * @return Quark of the parent attribute or <code>-1</code> for the root
368 public int getParentAttributeQuark(int quark
) {
372 return attributeList
.get(quark
).getParentAttributeQuark();
375 private void addSubAttributes(List
<Integer
> list
, Attribute curAttribute
,
377 for (Attribute childNode
: curAttribute
.getSubAttributes()) {
378 list
.add(childNode
.getQuark());
380 addSubAttributes(list
, childNode
, true);
386 * Get then base name of an attribute specified by a quark.
389 * The quark of the attribute
390 * @return The (base) name of the attribute
392 public String
getAttributeName(int quark
) {
393 return attributeList
.get(quark
).getName();
397 * Get the full path name of an attribute specified by a quark.
400 * The quark of the attribute
401 * @return The full path name of the attribute
403 public String
getFullAttributeName(int quark
) {
404 if (quark
>= attributeList
.size() || quark
< 0) {
407 return attributeList
.get(quark
).getFullAttributeName();
411 * Debug-print all the attributes in the tree.
414 * The writer where to print the output
416 public void debugPrint(PrintWriter writer
) {
417 attributeTreeRoot
.debugPrint(writer
);