1 /*******************************************************************************
2 * Copyright (c) 2012, 2016 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
;
19 import static org
.eclipse
.tracecompass
.statesystem
.core
.ITmfStateSystem
.INVALID_ATTRIBUTE
;
20 import static org
.eclipse
.tracecompass
.statesystem
.core
.ITmfStateSystem
.ROOT_ATTRIBUTE
;
22 import java
.io
.BufferedInputStream
;
24 import java
.io
.FileInputStream
;
25 import java
.io
.FileOutputStream
;
26 import java
.io
.IOException
;
27 import java
.io
.ObjectInputStream
;
28 import java
.io
.ObjectOutputStream
;
29 import java
.io
.PrintWriter
;
30 import java
.nio
.channels
.FileChannel
;
31 import java
.util
.ArrayList
;
32 import java
.util
.List
;
34 import org
.eclipse
.jdt
.annotation
.NonNull
;
35 import org
.eclipse
.tracecompass
.statesystem
.core
.ITmfStateSystem
;
36 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.AttributeNotFoundException
;
39 * The Attribute Tree is the /proc-like filesystem used to organize attributes.
40 * Each node of this tree is both like a file and a directory in the
46 public final class AttributeTree
{
48 /* "Magic number" for attribute tree files or file sections */
49 private static final int ATTRIB_TREE_MAGIC_NUMBER
= 0x06EC3671;
51 private final StateSystem ss
;
52 private final List
<Attribute
> attributeList
;
53 private final Attribute attributeTreeRoot
;
56 * Standard constructor, create a new empty Attribute Tree
59 * The StateSystem to which this AT is attached
61 public AttributeTree(StateSystem ss
) {
63 this.attributeList
= new ArrayList
<>();
64 this.attributeTreeRoot
= new Attribute(null, "root", ROOT_ATTRIBUTE
); //$NON-NLS-1$
68 * "Existing file" constructor. Builds an attribute tree from a
69 * "mapping file" or mapping section previously saved somewhere.
72 * StateSystem to which this AT is attached
74 * File stream where to read the AT information. Make sure it's
75 * sought at the right place!
77 * If there is a problem reading from the file stream
79 public AttributeTree(StateSystem ss
, FileInputStream fis
) throws IOException
{
81 ObjectInputStream ois
= new ObjectInputStream(new BufferedInputStream(fis
));
83 /* Read the header of the Attribute Tree file (or file section) */
84 int res
= ois
.readInt(); /* Magic number */
85 if (res
!= ATTRIB_TREE_MAGIC_NUMBER
) {
86 throw new IOException("The attribute tree file section is either invalid or corrupted."); //$NON-NLS-1$
90 ArrayList
<String
[]> attribList
;
92 @SuppressWarnings("unchecked")
93 ArrayList
<String
[]> list
= (ArrayList
<String
[]>) ois
.readObject();
95 } catch (ClassNotFoundException e
) {
96 throw new IOException("Unrecognizable attribute list"); //$NON-NLS-1$
100 * Now we have 'list', the ArrayList of String arrays representing all
101 * the attributes. Simply create attributes the normal way from them.
103 for (String
[] attrib
: attribList
) {
104 this.getQuarkAndAdd(ROOT_ATTRIBUTE
, attrib
);
109 * Tell the Attribute Tree to write itself somewhere in a file.
112 * The file to write to
114 * The position (in bytes) in the file where to write
116 public synchronized void writeSelf(File file
, long pos
) {
117 try (FileOutputStream fos
= new FileOutputStream(file
, true);
118 FileChannel fc
= fos
.getChannel();) {
120 try (ObjectOutputStream oos
= new ObjectOutputStream(fos
)) {
122 /* Write the almost-magic number */
123 oos
.writeInt(ATTRIB_TREE_MAGIC_NUMBER
);
125 /* Compute the serialized list of attributes and write it */
126 List
<String
[]> list
= new ArrayList
<>(attributeList
.size());
127 for (Attribute entry
: this.attributeList
) {
128 list
.add(entry
.getFullAttribute());
130 oos
.writeObject(list
);
132 } catch (IOException e
) {
139 * Return the number of attributes this system as seen so far. Note that
140 * this also equals the integer value (quark) the next added attribute will
143 * @return The current number of attributes in the tree
145 public synchronized int getNbAttributes() {
146 return attributeList
.size();
150 * Get the quark for a given attribute path. No new attribute will be
151 * created : if the specified path does not exist, return
152 * {@link ITmfStateSystem#INVALID_ATTRIBUTE}.
154 * @param startingNodeQuark
155 * The quark of the attribute from which relative queries will
156 * start. Use {@link ITmfStateSystem#ROOT_ATTRIBUTE} to start at
159 * The path to the attribute, relative to the starting node.
160 * @return The quark of the specified attribute, or
161 * {@link ITmfStateSystem#INVALID_ATTRIBUTE} if that attribute does
164 public synchronized int getQuarkDontAdd(int startingNodeQuark
, String
... subPath
) {
165 assert (startingNodeQuark
>= ROOT_ATTRIBUTE
);
169 /* If subPath is empty, simply return the starting quark */
170 if (subPath
== null || subPath
.length
== 0) {
171 return startingNodeQuark
;
174 /* Get the "starting node" */
175 if (startingNodeQuark
== ROOT_ATTRIBUTE
) {
176 prevNode
= attributeTreeRoot
;
178 prevNode
= attributeList
.get(startingNodeQuark
);
181 return prevNode
.getSubAttributeQuark(subPath
);
185 * Get the quark of a given attribute path. If that specified path does not
186 * exist, it will be created (and the quark that was just created will be
189 * @param startingNodeQuark
190 * The quark of the attribute from which relative queries will
191 * start. Use {@link ITmfStateSystem#ROOT_ATTRIBUTE} to start at
194 * The path to the attribute, relative to the starting node.
195 * @return The quark of the attribute represented by the path
197 public synchronized int getQuarkAndAdd(int startingNodeQuark
, String
... subPath
) {
198 // FIXME synchronized here is probably quite costly... maybe only locking
199 // the "for" would be enough?
200 assert (subPath
!= null && subPath
.length
> 0);
201 assert (startingNodeQuark
>= ROOT_ATTRIBUTE
);
203 Attribute nextNode
= null;
206 /* Get the "starting node" */
207 if (startingNodeQuark
== ROOT_ATTRIBUTE
) {
208 prevNode
= attributeTreeRoot
;
210 prevNode
= attributeList
.get(startingNodeQuark
);
213 int knownQuark
= prevNode
.getSubAttributeQuark(subPath
);
214 if (knownQuark
== INVALID_ATTRIBUTE
) {
216 * The attribute was not in the table previously, and we want to add
219 for (String curDirectory
: subPath
) {
220 nextNode
= prevNode
.getSubAttributeNode(curDirectory
);
221 if (nextNode
== null) {
222 /* This is where we need to start adding */
223 nextNode
= new Attribute(prevNode
, checkNotNull(curDirectory
), attributeList
.size());
224 prevNode
.addSubAttribute(nextNode
);
225 attributeList
.add(nextNode
);
226 ss
.addEmptyAttribute();
230 return attributeList
.size() - 1;
233 * The attribute was already existing, return the quark of that
240 * Returns the sub-attributes of the quark passed in parameter
242 * @param attributeQuark
243 * The quark of the attribute to print the sub-attributes of.
245 * Should the query be recursive or not? If false, only children
246 * one level deep will be returned. If true, all descendants will
247 * be returned (depth-first search)
248 * @return The list of quarks representing the children attributes
249 * @throws AttributeNotFoundException
250 * If 'attributeQuark' is invalid, or if there is no attribute
253 public synchronized @NonNull List
<@NonNull Integer
> getSubAttributes(int attributeQuark
, boolean recursive
)
254 throws AttributeNotFoundException
{
255 List
<@NonNull Integer
> listOfChildren
= new ArrayList
<>();
256 Attribute startingAttribute
;
258 /* Check if the quark is valid */
259 if (attributeQuark
< ROOT_ATTRIBUTE
|| attributeQuark
>= attributeList
.size()) {
260 throw new AttributeNotFoundException(ss
.getSSID() + " Quark:" + attributeQuark
); //$NON-NLS-1$
263 /* Set up the node from which we'll start the search */
264 if (attributeQuark
== ROOT_ATTRIBUTE
) {
265 startingAttribute
= attributeTreeRoot
;
267 startingAttribute
= attributeList
.get(attributeQuark
);
270 /* Iterate through the sub-attributes and add them to the list */
271 addSubAttributes(listOfChildren
, startingAttribute
, recursive
);
273 return listOfChildren
;
277 * Returns the parent quark of the attribute. The root attribute has no
278 * parent and will return {@link ITmfStateSystem#ROOT_ATTRIBUTE}.
281 * The quark of the attribute
282 * @return Quark of the parent attribute or
283 * {@link ITmfStateSystem#ROOT_ATTRIBUTE} for the root attribute
285 public synchronized int getParentAttributeQuark(int quark
) {
286 if (quark
== ROOT_ATTRIBUTE
) {
289 return attributeList
.get(quark
).getParentAttributeQuark();
292 private void addSubAttributes(List
<Integer
> list
, Attribute curAttribute
,
294 for (Attribute childNode
: curAttribute
.getSubAttributes()) {
295 list
.add(childNode
.getQuark());
297 addSubAttributes(list
, childNode
, true);
303 * Get then base name of an attribute specified by a quark.
306 * The quark of the attribute
307 * @return The (base) name of the attribute
309 public synchronized @NonNull String
getAttributeName(int quark
) {
310 return attributeList
.get(quark
).getName();
314 * Get the full path name of an attribute specified by a quark.
317 * The quark of the attribute
318 * @return The full path name of the attribute
320 public synchronized @NonNull String
getFullAttributeName(int quark
) {
321 return attributeList
.get(quark
).getFullAttributeName();
325 * Get the full path name (as an array of path elements) of an attribute
326 * specified by a quark.
329 * The quark of the attribute
330 * @return The path elements of the full path
332 public synchronized String
@NonNull [] getFullAttributePathArray(int quark
) {
333 return attributeList
.get(quark
).getFullAttribute();
337 * Debug-print all the attributes in the tree.
340 * The writer where to print the output
342 public synchronized void debugPrint(PrintWriter writer
) {
343 attributeTreeRoot
.debugPrint(writer
);