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
.tmf
.core
.statesystem
;
16 import java
.util
.ArrayList
;
17 import java
.util
.Arrays
;
18 import java
.util
.Collections
;
19 import java
.util
.List
;
22 * The Attribute Tree is the /proc-like filesystem used to organize attributes.
23 * Each node of this tree is both like a file and a directory in the
29 final class AttributeTree
{
31 /* "Magic number" for attribute tree files or file sections */
32 private final static int ATTRIB_TREE_MAGIC_NUMBER
= 0x06EC3671;
34 private final StateSystem ss
;
35 private final List
<Attribute
> attributeList
;
36 private final Attribute attributeTreeRoot
;
39 * Standard constructor, create a new empty Attribute Tree
42 * The StateSystem to which this AT is attached
44 AttributeTree(StateSystem ss
) {
46 this.attributeList
= Collections
.synchronizedList(new ArrayList
<Attribute
>());
47 this.attributeTreeRoot
= new AlphaNumAttribute(null, "root", -1); //$NON-NLS-1$
51 * "Existing file" constructor Builds a attribute tree from a "mapping file"
52 * or mapping section previously saved somewhere.
55 * StateSystem to which this AT is attached
57 * File stream where to read the AT information. Make sure it's
58 * seeked at the right place!
61 AttributeTree(StateSystem ss
, FileInputStream fis
) throws IOException
{
63 DataInputStream in
= new DataInputStream(new BufferedInputStream(fis
));
65 /* Message for exceptions, shouldn't be externalized */
66 final String errorMessage
= "The attribute tree file section is either invalid or corrupted."; //$NON-NLS-1$
68 ArrayList
<String
[]> list
= new ArrayList
<String
[]>();
71 String
[] curStringArray
;
72 int res
, remain
, size
;
76 /* Read the header of the Attribute Tree file (or file section) */
77 res
= in
.readInt(); /* Magic number */
78 if (res
!= ATTRIB_TREE_MAGIC_NUMBER
) {
79 throw new IOException(errorMessage
);
82 /* Expected size of the section */
83 expectedSize
= in
.readInt();
84 if (expectedSize
<= 12) {
85 throw new IOException(errorMessage
);
88 /* How many entries we have to read */
89 remain
= in
.readInt();
93 for (; remain
> 0; remain
--) {
94 /* Read the first byte = the size of the entry */
96 curByteArray
= new byte[size
];
97 in
.read(curByteArray
);
100 * Go buffer -> byteArray -> String -> String[] -> insert in list.
103 curFullString
= new String(curByteArray
);
104 curStringArray
= curFullString
.split("/"); //$NON-NLS-1$
105 list
.add(curStringArray
);
107 /* Read the 0'ed confirmation byte */
110 throw new IOException(errorMessage
);
112 total
+= curByteArray
.length
+ 2;
115 if (total
!= expectedSize
) {
116 throw new IOException(errorMessage
);
120 * Now we have 'list', the ArrayList of String arrays representing all
121 * the attributes. Simply create attributes the normal way from them.
123 for (String
[] attrib
: list
) {
124 this.getQuarkAndAdd(-1, attrib
);
129 * Tell the Attribute Tree to write itself somewhere. The passed
130 * FileOutputStream defines where (which file/position).
133 * Where to write. Make sure it's seeked at the right position
135 * @return The total number of bytes written.
137 int writeSelf(File file
, long pos
) {
138 RandomAccessFile raf
;
143 raf
= new RandomAccessFile(file
, "rw"); //$NON-NLS-1$
146 /* Write the almost-magic number */
147 raf
.writeInt(ATTRIB_TREE_MAGIC_NUMBER
);
149 /* Placeholder for the total size of the section... */
152 /* Write the number of entries */
153 raf
.writeInt(this.attributeList
.size());
156 /* Write the attributes themselves */
157 for (Attribute entry
: this.attributeList
) {
158 curByteArray
= entry
.getFullAttributeName().getBytes();
159 if (curByteArray
.length
> Byte
.MAX_VALUE
) {
160 throw new IOException("Attribute with name \"" //$NON-NLS-1$
161 + Arrays
.toString(curByteArray
) + "\" is too long."); //$NON-NLS-1$
163 /* Write the first byte = size of the array */
164 raf
.writeByte((byte) curByteArray
.length
);
166 /* Write the array itself */
167 raf
.write(curByteArray
);
169 /* Write the 0'ed byte */
170 raf
.writeByte((byte) 0);
172 total
+= curByteArray
.length
+ 2;
175 /* Now go back and write the actual size of this section */
180 } catch (IOException e
) {
187 * Return the number of attributes this system as seen so far. Note that
188 * this also equals the integer value (quark) the next added attribute will
193 int getNbAttributes() {
194 return attributeList
.size();
198 * This is the version to specifically add missing attributes.
200 * If 'numericalNode' is true, all the new attributes created will be of
201 * type 'NumericalNode' instead of 'AlphaNumNode'. Be careful with this, if
202 * you do not want ALL added attributes to be numerical, call this function
203 * first with 'false' to create the parent nodes, then call it again to make
204 * sure only the final node is numerical.
206 * @throws AttributeNotFoundException
208 int getQuarkDontAdd(int startingNodeQuark
, String
... subPath
)
209 throws AttributeNotFoundException
{
210 assert (subPath
!= null && subPath
.length
> 0);
211 assert (startingNodeQuark
>= -1);
215 /* Get the "starting node" */
216 if (startingNodeQuark
== -1) {
217 prevNode
= attributeTreeRoot
;
219 prevNode
= attributeList
.get(startingNodeQuark
);
222 int knownQuark
= prevNode
.getSubAttributeQuark(subPath
);
223 if (knownQuark
== -1) {
225 * The attribute doesn't exist, but we have been specified to NOT
226 * add any new attributes.
228 throw new AttributeNotFoundException();
231 * The attribute was already existing, return the quark of that
237 // FIXME synchronized here is probably quite costly... maybe only locking
238 // the "for" would be enough?
239 synchronized int getQuarkAndAdd(int startingNodeQuark
, String
... subPath
) {
240 assert (subPath
!= null && subPath
.length
> 0);
241 assert (startingNodeQuark
>= -1);
243 Attribute nextNode
= null;
246 /* Get the "starting node" */
247 if (startingNodeQuark
== -1) {
248 prevNode
= attributeTreeRoot
;
250 prevNode
= attributeList
.get(startingNodeQuark
);
253 int knownQuark
= prevNode
.getSubAttributeQuark(subPath
);
254 if (knownQuark
== -1) {
256 * The attribute was not in the table previously, and we want to add
259 for (String curDirectory
: subPath
) {
260 nextNode
= prevNode
.getSubAttributeNode(curDirectory
);
261 if (nextNode
== null) {
262 /* This is where we need to start adding */
263 nextNode
= new AlphaNumAttribute(prevNode
, curDirectory
,
264 attributeList
.size());
265 prevNode
.addSubAttribute(nextNode
);
266 attributeList
.add(nextNode
);
267 ss
.transState
.addEmptyEntry();
271 return attributeList
.size() - 1;
274 * The attribute was already existing, return the quark of that
280 int getSubAttributesCount(int quark
) {
281 return attributeList
.get(quark
).getSubAttributesList().size();
284 ArrayList
<Integer
> getSubAttributes(int attributeQuark
) {
285 ArrayList
<Integer
> listOfChildren
= new ArrayList
<Integer
>();
287 for (Attribute childNode
: attributeList
.get(attributeQuark
).getSubAttributesList()) {
288 listOfChildren
.add(childNode
.getQuark());
290 return listOfChildren
;
293 String
getFullAttributeName(int quark
) {
294 if (quark
>= attributeList
.size() || quark
< 0) {
297 return attributeList
.get(quark
).getFullAttributeName();
300 void debugPrint(PrintWriter writer
) {
301 attributeTreeRoot
.debugPrint(writer
);