1 /*******************************************************************************
2 * Copyright (c) 2012, 2015 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
12 * Alexandre Montplaisir - Initial API and implementation
13 * Patrick Tasse - Add message to exceptions
14 *******************************************************************************/
16 package org
.eclipse
.tracecompass
.internal
.statesystem
.core
;
18 import static org
.eclipse
.tracecompass
.common
.core
.NonNullUtils
.checkNotNull
;
20 import java
.io
.BufferedInputStream
;
21 import java
.io
.DataInputStream
;
23 import java
.io
.FileInputStream
;
24 import java
.io
.IOException
;
25 import java
.io
.PrintWriter
;
26 import java
.io
.RandomAccessFile
;
27 import java
.util
.ArrayList
;
28 import java
.util
.Arrays
;
29 import java
.util
.Collections
;
30 import java
.util
.List
;
32 import org
.eclipse
.jdt
.annotation
.NonNull
;
33 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.AttributeNotFoundException
;
36 * The Attribute Tree is the /proc-like filesystem used to organize attributes.
37 * Each node of this tree is both like a file and a directory in the
43 public final class AttributeTree
{
45 /* "Magic number" for attribute tree files or file sections */
46 private static final int ATTRIB_TREE_MAGIC_NUMBER
= 0x06EC3671;
48 private final StateSystem ss
;
49 private final List
<Attribute
> attributeList
;
50 private final Attribute attributeTreeRoot
;
53 * Standard constructor, create a new empty Attribute Tree
56 * The StateSystem to which this AT is attached
58 public AttributeTree(StateSystem ss
) {
60 this.attributeList
= Collections
.synchronizedList(new ArrayList
<Attribute
>());
61 this.attributeTreeRoot
= new Attribute(null, "root", -1); //$NON-NLS-1$
65 * "Existing file" constructor. Builds an attribute tree from a
66 * "mapping file" or mapping section previously saved somewhere.
69 * StateSystem to which this AT is attached
71 * File stream where to read the AT information. Make sure it's
72 * sought at the right place!
74 * If there is a problem reading from the file stream
76 public AttributeTree(StateSystem ss
, FileInputStream fis
) throws IOException
{
78 DataInputStream in
= new DataInputStream(new BufferedInputStream(fis
));
80 /* Message for exceptions, shouldn't be externalized */
81 final String errorMessage
= "The attribute tree file section is either invalid or corrupted."; //$NON-NLS-1$
83 ArrayList
<String
[]> list
= new ArrayList
<>();
86 String
[] curStringArray
;
87 int res
, remain
, size
;
91 /* Read the header of the Attribute Tree file (or file section) */
92 res
= in
.readInt(); /* Magic number */
93 if (res
!= ATTRIB_TREE_MAGIC_NUMBER
) {
94 throw new IOException(errorMessage
);
97 /* Expected size of the section */
98 expectedSize
= in
.readInt();
99 if (expectedSize
< 12) {
100 throw new IOException(errorMessage
);
103 /* How many entries we have to read */
104 remain
= in
.readInt();
107 /* Read each entry */
108 for (; remain
> 0; remain
--) {
109 /* Read the first byte = the size of the entry */
110 size
= in
.readByte();
111 curByteArray
= new byte[size
];
112 res
= in
.read(curByteArray
);
114 throw new IOException(errorMessage
);
118 * Go buffer -> byteArray -> String -> String[] -> insert in list.
121 curFullString
= new String(curByteArray
);
122 curStringArray
= curFullString
.split("/"); //$NON-NLS-1$
123 list
.add(curStringArray
);
125 /* Read the 0'ed confirmation byte */
128 throw new IOException(errorMessage
);
130 total
+= curByteArray
.length
+ 2;
133 if (total
!= expectedSize
) {
134 throw new IOException(errorMessage
);
138 * Now we have 'list', the ArrayList of String arrays representing all
139 * the attributes. Simply create attributes the normal way from them.
141 for (String
[] attrib
: list
) {
142 this.getQuarkAndAdd(-1, attrib
);
147 * Tell the Attribute Tree to write itself somewhere in a file.
150 * The file to write to
152 * The position (in bytes) in the file where to write
153 * @return The total number of bytes written.
155 public int writeSelf(File file
, long pos
) {
159 try (RandomAccessFile raf
= new RandomAccessFile(file
, "rw");) { //$NON-NLS-1$
162 /* Write the almost-magic number */
163 raf
.writeInt(ATTRIB_TREE_MAGIC_NUMBER
);
165 /* Placeholder for the total size of the section... */
168 /* Write the number of entries */
169 raf
.writeInt(this.attributeList
.size());
172 /* Write the attributes themselves */
173 for (Attribute entry
: this.attributeList
) {
174 curByteArray
= entry
.getFullAttributeName().getBytes();
175 if (curByteArray
.length
> Byte
.MAX_VALUE
) {
176 throw new IOException("Attribute with name \"" //$NON-NLS-1$
177 + Arrays
.toString(curByteArray
) + "\" is too long."); //$NON-NLS-1$
179 /* Write the first byte = size of the array */
180 raf
.writeByte((byte) curByteArray
.length
);
182 /* Write the array itself */
183 raf
.write(curByteArray
);
185 /* Write the 0'ed byte */
186 raf
.writeByte((byte) 0);
188 total
+= curByteArray
.length
+ 2;
191 /* Now go back and write the actual size of this section */
195 } catch (IOException e
) {
202 * Return the number of attributes this system as seen so far. Note that
203 * this also equals the integer value (quark) the next added attribute will
206 * @return The current number of attributes in the tree
208 public int getNbAttributes() {
209 return attributeList
.size();
213 * Get the quark for a given attribute path. No new attribute will be
214 * created : if the specified path does not exist, throw an error.
216 * @param startingNodeQuark
217 * The quark of the attribute from which relative queries will
218 * start. Use '-1' to start at the root node.
220 * The path to the attribute, relative to the starting node.
221 * @return The quark of the specified attribute
222 * @throws AttributeNotFoundException
223 * If the specified path was not found
225 public int getQuarkDontAdd(int startingNodeQuark
, String
... subPath
)
226 throws AttributeNotFoundException
{
227 assert (startingNodeQuark
>= -1);
231 /* If subPath is empty, simply return the starting quark */
232 if (subPath
== null || subPath
.length
== 0) {
233 return startingNodeQuark
;
236 /* Get the "starting node" */
237 if (startingNodeQuark
== -1) {
238 prevNode
= attributeTreeRoot
;
240 prevNode
= attributeList
.get(startingNodeQuark
);
243 int knownQuark
= prevNode
.getSubAttributeQuark(subPath
);
244 if (knownQuark
== -1) {
246 * The attribute doesn't exist, but we have been specified to NOT
247 * add any new attributes.
249 throw new AttributeNotFoundException(ss
.getSSID() + " Quark:" + startingNodeQuark
+ ", SubPath:" + Arrays
.toString(subPath
)); //$NON-NLS-1$ //$NON-NLS-2$
252 * The attribute was already existing, return the quark of that
259 * Get the quark of a given attribute path. If that specified path does not
260 * exist, it will be created (and the quark that was just created will be
263 * @param startingNodeQuark
264 * The quark of the attribute from which relative queries will
265 * start. Use '-1' to start at the root node.
267 * The path to the attribute, relative to the starting node.
268 * @return The quark of the attribute represented by the path
270 public synchronized int getQuarkAndAdd(int startingNodeQuark
, String
... subPath
) {
271 // FIXME synchronized here is probably quite costly... maybe only locking
272 // the "for" would be enough?
273 assert (subPath
!= null && subPath
.length
> 0);
274 assert (startingNodeQuark
>= -1);
276 Attribute nextNode
= null;
279 /* Get the "starting node" */
280 if (startingNodeQuark
== -1) {
281 prevNode
= attributeTreeRoot
;
283 prevNode
= attributeList
.get(startingNodeQuark
);
286 int knownQuark
= prevNode
.getSubAttributeQuark(subPath
);
287 if (knownQuark
== -1) {
289 * The attribute was not in the table previously, and we want to add
292 for (String curDirectory
: subPath
) {
293 nextNode
= prevNode
.getSubAttributeNode(curDirectory
);
294 if (nextNode
== null) {
295 /* This is where we need to start adding */
296 nextNode
= new Attribute(prevNode
, checkNotNull(curDirectory
), attributeList
.size());
297 prevNode
.addSubAttribute(nextNode
);
298 attributeList
.add(nextNode
);
299 ss
.addEmptyAttribute();
303 return attributeList
.size() - 1;
306 * The attribute was already existing, return the quark of that
313 * Returns the sub-attributes of the quark passed in parameter
315 * @param attributeQuark
316 * The quark of the attribute to print the sub-attributes of.
318 * Should the query be recursive or not? If false, only children
319 * one level deep will be returned. If true, all descendants will
320 * be returned (depth-first search)
321 * @return The list of quarks representing the children attributes
322 * @throws AttributeNotFoundException
323 * If 'attributeQuark' is invalid, or if there is no attrbiute
326 public @NonNull List
<Integer
> getSubAttributes(int attributeQuark
, boolean recursive
)
327 throws AttributeNotFoundException
{
328 List
<Integer
> listOfChildren
= new ArrayList
<>();
329 Attribute startingAttribute
;
331 /* Check if the quark is valid */
332 if (attributeQuark
< -1 || attributeQuark
>= attributeList
.size()) {
333 throw new AttributeNotFoundException(ss
.getSSID() + " Quark:" + attributeQuark
); //$NON-NLS-1$
336 /* Set up the node from which we'll start the search */
337 if (attributeQuark
== -1) {
338 startingAttribute
= attributeTreeRoot
;
340 startingAttribute
= attributeList
.get(attributeQuark
);
343 /* Iterate through the sub-attributes and add them to the list */
344 addSubAttributes(listOfChildren
, startingAttribute
, recursive
);
346 return listOfChildren
;
350 * Returns the parent quark of the attribute. The root attribute has no
351 * parent and will return <code>-1</code>
354 * The quark of the attribute
355 * @return Quark of the parent attribute or <code>-1</code> for the root
358 public int getParentAttributeQuark(int quark
) {
362 return attributeList
.get(quark
).getParentAttributeQuark();
365 private void addSubAttributes(List
<Integer
> list
, Attribute curAttribute
,
367 for (Attribute childNode
: curAttribute
.getSubAttributes()) {
368 list
.add(childNode
.getQuark());
370 addSubAttributes(list
, childNode
, true);
376 * Get then base name of an attribute specified by a quark.
379 * The quark of the attribute
380 * @return The (base) name of the attribute
382 public @NonNull String
getAttributeName(int quark
) {
383 return attributeList
.get(quark
).getName();
387 * Get the full path name of an attribute specified by a quark.
390 * The quark of the attribute
391 * @return The full path name of the attribute
393 public @NonNull String
getFullAttributeName(int quark
) {
394 return attributeList
.get(quark
).getFullAttributeName();
398 * Get the full path name (as an array of path elements) of an attribute
399 * specified by a quark.
402 * The quark of the attribute
403 * @return The path elements of the full path
405 public @NonNull String
[] getFullAttributePathArray(int quark
) {
406 return attributeList
.get(quark
).getFullAttribute();
410 * Debug-print all the attributes in the tree.
413 * The writer where to print the output
415 public void debugPrint(PrintWriter writer
) {
416 attributeTreeRoot
.debugPrint(writer
);