1 /*******************************************************************************
2 * Copyright (c) 2012 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
;
24 * The Attribute Tree is the /proc-like filesystem used to organize attributes.
25 * Each node of this tree is both like a file and a directory in the
31 final class AttributeTree
{
33 /* "Magic number" for attribute tree files or file sections */
34 private final static int ATTRIB_TREE_MAGIC_NUMBER
= 0x06EC3671;
36 private final StateSystem ss
;
37 private final List
<Attribute
> attributeList
;
38 private final Attribute attributeTreeRoot
;
41 * Standard constructor, create a new empty Attribute Tree
44 * The StateSystem to which this AT is attached
46 AttributeTree(StateSystem ss
) {
48 this.attributeList
= Collections
.synchronizedList(new ArrayList
<Attribute
>());
49 this.attributeTreeRoot
= new AlphaNumAttribute(null, "root", -1); //$NON-NLS-1$
53 * "Existing file" constructor Builds a attribute tree from a "mapping file"
54 * or mapping section previously saved somewhere.
57 * StateSystem to which this AT is attached
59 * File stream where to read the AT information. Make sure it's
60 * seeked at the right place!
63 AttributeTree(StateSystem ss
, FileInputStream fis
) throws IOException
{
65 DataInputStream in
= new DataInputStream(new BufferedInputStream(fis
));
67 /* Message for exceptions, shouldn't be externalized */
68 final String errorMessage
= "The attribute tree file section is either invalid or corrupted."; //$NON-NLS-1$
70 ArrayList
<String
[]> list
= new ArrayList
<String
[]>();
73 String
[] curStringArray
;
74 int res
, remain
, size
;
78 /* Read the header of the Attribute Tree file (or file section) */
79 res
= in
.readInt(); /* Magic number */
80 if (res
!= ATTRIB_TREE_MAGIC_NUMBER
) {
81 throw new IOException(errorMessage
);
84 /* Expected size of the section */
85 expectedSize
= in
.readInt();
86 if (expectedSize
<= 12) {
87 throw new IOException(errorMessage
);
90 /* How many entries we have to read */
91 remain
= in
.readInt();
95 for (; remain
> 0; remain
--) {
96 /* Read the first byte = the size of the entry */
98 curByteArray
= new byte[size
];
99 res
= in
.read(curByteArray
);
101 throw new IOException(errorMessage
);
105 * Go buffer -> byteArray -> String -> String[] -> insert in list.
108 curFullString
= new String(curByteArray
);
109 curStringArray
= curFullString
.split("/"); //$NON-NLS-1$
110 list
.add(curStringArray
);
112 /* Read the 0'ed confirmation byte */
115 throw new IOException(errorMessage
);
117 total
+= curByteArray
.length
+ 2;
120 if (total
!= expectedSize
) {
121 throw new IOException(errorMessage
);
125 * Now we have 'list', the ArrayList of String arrays representing all
126 * the attributes. Simply create attributes the normal way from them.
128 for (String
[] attrib
: list
) {
129 this.getQuarkAndAdd(-1, attrib
);
134 * Tell the Attribute Tree to write itself somewhere. The passed
135 * FileOutputStream defines where (which file/position).
138 * Where to write. Make sure it's seeked at the right position
140 * @return The total number of bytes written.
142 int writeSelf(File file
, long pos
) {
143 RandomAccessFile raf
= null;
148 raf
= new RandomAccessFile(file
, "rw"); //$NON-NLS-1$
151 /* Write the almost-magic number */
152 raf
.writeInt(ATTRIB_TREE_MAGIC_NUMBER
);
154 /* Placeholder for the total size of the section... */
157 /* Write the number of entries */
158 raf
.writeInt(this.attributeList
.size());
161 /* Write the attributes themselves */
162 for (Attribute entry
: this.attributeList
) {
163 curByteArray
= entry
.getFullAttributeName().getBytes();
164 if (curByteArray
.length
> Byte
.MAX_VALUE
) {
165 throw new IOException("Attribute with name \"" //$NON-NLS-1$
166 + Arrays
.toString(curByteArray
) + "\" is too long."); //$NON-NLS-1$
168 /* Write the first byte = size of the array */
169 raf
.writeByte((byte) curByteArray
.length
);
171 /* Write the array itself */
172 raf
.write(curByteArray
);
174 /* Write the 0'ed byte */
175 raf
.writeByte((byte) 0);
177 total
+= curByteArray
.length
+ 2;
180 /* Now go back and write the actual size of this section */
184 } catch (IOException e
) {
190 } catch (IOException e
) {
199 * Return the number of attributes this system as seen so far. Note that
200 * this also equals the integer value (quark) the next added attribute will
205 int getNbAttributes() {
206 return attributeList
.size();
210 * This is the version to specifically add missing attributes.
212 * If 'numericalNode' is true, all the new attributes created will be of
213 * type 'NumericalNode' instead of 'AlphaNumNode'. Be careful with this, if
214 * you do not want ALL added attributes to be numerical, call this function
215 * first with 'false' to create the parent nodes, then call it again to make
216 * sure only the final node is numerical.
218 * @throws AttributeNotFoundException
220 int getQuarkDontAdd(int startingNodeQuark
, String
... subPath
)
221 throws AttributeNotFoundException
{
222 assert (startingNodeQuark
>= -1);
226 /* If subPath is empty, simply return the starting quark */
227 if (subPath
== null || subPath
.length
== 0) {
228 return startingNodeQuark
;
231 /* Get the "starting node" */
232 if (startingNodeQuark
== -1) {
233 prevNode
= attributeTreeRoot
;
235 prevNode
= attributeList
.get(startingNodeQuark
);
238 int knownQuark
= prevNode
.getSubAttributeQuark(subPath
);
239 if (knownQuark
== -1) {
241 * The attribute doesn't exist, but we have been specified to NOT
242 * add any new attributes.
244 throw new AttributeNotFoundException();
247 * The attribute was already existing, return the quark of that
253 // FIXME synchronized here is probably quite costly... maybe only locking
254 // the "for" would be enough?
255 synchronized int getQuarkAndAdd(int startingNodeQuark
, String
... subPath
) {
256 assert (subPath
!= null && subPath
.length
> 0);
257 assert (startingNodeQuark
>= -1);
259 Attribute nextNode
= null;
262 /* Get the "starting node" */
263 if (startingNodeQuark
== -1) {
264 prevNode
= attributeTreeRoot
;
266 prevNode
= attributeList
.get(startingNodeQuark
);
269 int knownQuark
= prevNode
.getSubAttributeQuark(subPath
);
270 if (knownQuark
== -1) {
272 * The attribute was not in the table previously, and we want to add
275 for (String curDirectory
: subPath
) {
276 nextNode
= prevNode
.getSubAttributeNode(curDirectory
);
277 if (nextNode
== null) {
278 /* This is where we need to start adding */
279 nextNode
= new AlphaNumAttribute(prevNode
, curDirectory
,
280 attributeList
.size());
281 prevNode
.addSubAttribute(nextNode
);
282 attributeList
.add(nextNode
);
283 ss
.addEmptyAttribute();
287 return attributeList
.size() - 1;
290 * The attribute was already existing, return the quark of that
296 int getSubAttributesCount(int quark
) {
297 return attributeList
.get(quark
).getSubAttributesList().size();
301 * Returns the sub-attributes of the quark passed in parameter
303 * @param attributeQuark
306 * @throws AttributeNotFoundException
308 List
<Integer
> getSubAttributes(int attributeQuark
, boolean recursive
)
309 throws AttributeNotFoundException
{
310 List
<Integer
> listOfChildren
= new ArrayList
<Integer
>();
311 Attribute startingAttribute
;
313 /* Check if the quark is valid */
314 if (attributeQuark
< -1 || attributeQuark
>= attributeList
.size()) {
315 throw new AttributeNotFoundException();
318 /* Set up the node from which we'll start the search */
319 if (attributeQuark
== -1) {
320 startingAttribute
= attributeTreeRoot
;
322 startingAttribute
= attributeList
.get(attributeQuark
);
325 /* Iterate through the sub-attributes and add them to the list */
326 addSubAttributes(listOfChildren
, startingAttribute
, recursive
);
328 return listOfChildren
;
331 private void addSubAttributes(List
<Integer
> list
, Attribute curAttribute
,
333 for (Attribute childNode
: curAttribute
.getSubAttributesList()) {
334 list
.add(childNode
.getQuark());
336 addSubAttributes(list
, childNode
, true);
341 String
getAttributeName(int quark
) {
342 return attributeList
.get(quark
).getName();
345 String
getFullAttributeName(int quark
) {
346 if (quark
>= attributeList
.size() || quark
< 0) {
349 return attributeList
.get(quark
).getFullAttributeName();
352 void debugPrint(PrintWriter writer
) {
353 attributeTreeRoot
.debugPrint(writer
);