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
.statesystem
.core
;
15 import java
.io
.BufferedInputStream
;
16 import java
.io
.DataInputStream
;
18 import java
.io
.FileInputStream
;
19 import java
.io
.IOException
;
20 import java
.io
.PrintWriter
;
21 import java
.io
.RandomAccessFile
;
22 import java
.util
.ArrayList
;
23 import java
.util
.Arrays
;
24 import java
.util
.Collections
;
25 import java
.util
.List
;
27 import org
.eclipse
.linuxtools
.statesystem
.core
.exceptions
.AttributeNotFoundException
;
30 * The Attribute Tree is the /proc-like filesystem used to organize attributes.
31 * Each node of this tree is both like a file and a directory in the
37 public final class AttributeTree
{
39 /* "Magic number" for attribute tree files or file sections */
40 private static final int ATTRIB_TREE_MAGIC_NUMBER
= 0x06EC3671;
42 private final StateSystem ss
;
43 private final List
<Attribute
> attributeList
;
44 private final Attribute attributeTreeRoot
;
47 * Standard constructor, create a new empty Attribute Tree
50 * The StateSystem to which this AT is attached
52 public AttributeTree(StateSystem ss
) {
54 this.attributeList
= Collections
.synchronizedList(new ArrayList
<Attribute
>());
55 this.attributeTreeRoot
= new Attribute(null, "root", -1); //$NON-NLS-1$
59 * "Existing file" constructor. Builds an attribute tree from a
60 * "mapping file" or mapping section previously saved somewhere.
63 * StateSystem to which this AT is attached
65 * File stream where to read the AT information. Make sure it's
66 * sought at the right place!
68 * If there is a problem reading from the file stream
70 public AttributeTree(StateSystem ss
, FileInputStream fis
) throws IOException
{
72 DataInputStream in
= new DataInputStream(new BufferedInputStream(fis
));
74 /* Message for exceptions, shouldn't be externalized */
75 final String errorMessage
= "The attribute tree file section is either invalid or corrupted."; //$NON-NLS-1$
77 ArrayList
<String
[]> list
= new ArrayList
<>();
80 String
[] curStringArray
;
81 int res
, remain
, size
;
85 /* Read the header of the Attribute Tree file (or file section) */
86 res
= in
.readInt(); /* Magic number */
87 if (res
!= ATTRIB_TREE_MAGIC_NUMBER
) {
88 throw new IOException(errorMessage
);
91 /* Expected size of the section */
92 expectedSize
= in
.readInt();
93 if (expectedSize
< 12) {
94 throw new IOException(errorMessage
);
97 /* How many entries we have to read */
98 remain
= in
.readInt();
101 /* Read each entry */
102 for (; remain
> 0; remain
--) {
103 /* Read the first byte = the size of the entry */
104 size
= in
.readByte();
105 curByteArray
= new byte[size
];
106 res
= in
.read(curByteArray
);
108 throw new IOException(errorMessage
);
112 * Go buffer -> byteArray -> String -> String[] -> insert in list.
115 curFullString
= new String(curByteArray
);
116 curStringArray
= curFullString
.split("/"); //$NON-NLS-1$
117 list
.add(curStringArray
);
119 /* Read the 0'ed confirmation byte */
122 throw new IOException(errorMessage
);
124 total
+= curByteArray
.length
+ 2;
127 if (total
!= expectedSize
) {
128 throw new IOException(errorMessage
);
132 * Now we have 'list', the ArrayList of String arrays representing all
133 * the attributes. Simply create attributes the normal way from them.
135 for (String
[] attrib
: list
) {
136 this.getQuarkAndAdd(-1, attrib
);
141 * Tell the Attribute Tree to write itself somewhere in a file.
144 * The file to write to
146 * The position (in bytes) in the file where to write
147 * @return The total number of bytes written.
149 public int writeSelf(File file
, long pos
) {
153 try (RandomAccessFile raf
= new RandomAccessFile(file
, "rw");) { //$NON-NLS-1$
156 /* Write the almost-magic number */
157 raf
.writeInt(ATTRIB_TREE_MAGIC_NUMBER
);
159 /* Placeholder for the total size of the section... */
162 /* Write the number of entries */
163 raf
.writeInt(this.attributeList
.size());
166 /* Write the attributes themselves */
167 for (Attribute entry
: this.attributeList
) {
168 curByteArray
= entry
.getFullAttributeName().getBytes();
169 if (curByteArray
.length
> Byte
.MAX_VALUE
) {
170 throw new IOException("Attribute with name \"" //$NON-NLS-1$
171 + Arrays
.toString(curByteArray
) + "\" is too long."); //$NON-NLS-1$
173 /* Write the first byte = size of the array */
174 raf
.writeByte((byte) curByteArray
.length
);
176 /* Write the array itself */
177 raf
.write(curByteArray
);
179 /* Write the 0'ed byte */
180 raf
.writeByte((byte) 0);
182 total
+= curByteArray
.length
+ 2;
185 /* Now go back and write the actual size of this section */
189 } catch (IOException e
) {
196 * Return the number of attributes this system as seen so far. Note that
197 * this also equals the integer value (quark) the next added attribute will
200 * @return The current number of attributes in the tree
202 public int getNbAttributes() {
203 return attributeList
.size();
207 * Get the quark for a given attribute path. No new attribute will be
208 * created : if the specified path does not exist, throw an error.
210 * @param startingNodeQuark
211 * The quark of the attribute from which relative queries will
212 * start. Use '-1' to start at the root node.
214 * The path to the attribute, relative to the starting node.
215 * @return The quark of the specified attribute
216 * @throws AttributeNotFoundException
217 * If the specified path was not found
219 public int getQuarkDontAdd(int startingNodeQuark
, String
... subPath
)
220 throws AttributeNotFoundException
{
221 assert (startingNodeQuark
>= -1);
225 /* If subPath is empty, simply return the starting quark */
226 if (subPath
== null || subPath
.length
== 0) {
227 return startingNodeQuark
;
230 /* Get the "starting node" */
231 if (startingNodeQuark
== -1) {
232 prevNode
= attributeTreeRoot
;
234 prevNode
= attributeList
.get(startingNodeQuark
);
237 int knownQuark
= prevNode
.getSubAttributeQuark(subPath
);
238 if (knownQuark
== -1) {
240 * The attribute doesn't exist, but we have been specified to NOT
241 * add any new attributes.
243 throw new AttributeNotFoundException();
246 * The attribute was already existing, return the quark of that
253 * Get the quark of a given attribute path. If that specified path does not
254 * exist, it will be created (and the quark that was just created will be
257 * @param startingNodeQuark
258 * The quark of the attribute from which relative queries will
259 * start. Use '-1' to start at the root node.
261 * The path to the attribute, relative to the starting node.
262 * @return The quark of the attribute represented by the path
264 public synchronized int getQuarkAndAdd(int startingNodeQuark
, String
... subPath
) {
265 // FIXME synchronized here is probably quite costly... maybe only locking
266 // the "for" would be enough?
267 assert (subPath
!= null && subPath
.length
> 0);
268 assert (startingNodeQuark
>= -1);
270 Attribute nextNode
= null;
273 /* Get the "starting node" */
274 if (startingNodeQuark
== -1) {
275 prevNode
= attributeTreeRoot
;
277 prevNode
= attributeList
.get(startingNodeQuark
);
280 int knownQuark
= prevNode
.getSubAttributeQuark(subPath
);
281 if (knownQuark
== -1) {
283 * The attribute was not in the table previously, and we want to add
286 for (String curDirectory
: subPath
) {
287 nextNode
= prevNode
.getSubAttributeNode(curDirectory
);
288 if (nextNode
== null) {
289 /* This is where we need to start adding */
290 nextNode
= new Attribute(prevNode
, curDirectory
, attributeList
.size());
291 prevNode
.addSubAttribute(nextNode
);
292 attributeList
.add(nextNode
);
293 ss
.addEmptyAttribute();
297 return attributeList
.size() - 1;
300 * The attribute was already existing, return the quark of that
307 * Returns the sub-attributes of the quark passed in parameter
309 * @param attributeQuark
310 * The quark of the attribute to print the sub-attributes of.
312 * Should the query be recursive or not? If false, only children
313 * one level deep will be returned. If true, all descendants will
314 * be returned (depth-first search)
315 * @return The list of quarks representing the children attributes
316 * @throws AttributeNotFoundException
317 * If 'attributeQuark' is invalid, or if there is no attrbiute
320 public List
<Integer
> getSubAttributes(int attributeQuark
, boolean recursive
)
321 throws AttributeNotFoundException
{
322 List
<Integer
> listOfChildren
= new ArrayList
<>();
323 Attribute startingAttribute
;
325 /* Check if the quark is valid */
326 if (attributeQuark
< -1 || attributeQuark
>= attributeList
.size()) {
327 throw new AttributeNotFoundException();
330 /* Set up the node from which we'll start the search */
331 if (attributeQuark
== -1) {
332 startingAttribute
= attributeTreeRoot
;
334 startingAttribute
= attributeList
.get(attributeQuark
);
337 /* Iterate through the sub-attributes and add them to the list */
338 addSubAttributes(listOfChildren
, startingAttribute
, recursive
);
340 return listOfChildren
;
344 * Returns the parent quark of the attribute. The root attribute has no
345 * parent and will return <code>-1</code>
348 * The quark of the attribute
349 * @return Quark of the parent attribute or <code>-1</code> for the root
352 public int getParentAttributeQuark(int quark
) {
356 return attributeList
.get(quark
).getParentAttributeQuark();
359 private void addSubAttributes(List
<Integer
> list
, Attribute curAttribute
,
361 for (Attribute childNode
: curAttribute
.getSubAttributes()) {
362 list
.add(childNode
.getQuark());
364 addSubAttributes(list
, childNode
, true);
370 * Get then base name of an attribute specified by a quark.
373 * The quark of the attribute
374 * @return The (base) name of the attribute
376 public String
getAttributeName(int quark
) {
377 return attributeList
.get(quark
).getName();
381 * Get the full path name of an attribute specified by a quark.
384 * The quark of the attribute
385 * @return The full path name of the attribute
387 public String
getFullAttributeName(int quark
) {
388 if (quark
>= attributeList
.size() || quark
< 0) {
391 return attributeList
.get(quark
).getFullAttributeName();
395 * Debug-print all the attributes in the tree.
398 * The writer where to print the output
400 public void debugPrint(PrintWriter writer
) {
401 attributeTreeRoot
.debugPrint(writer
);