1 /*******************************************************************************
2 * Copyright (c) 2015, 2016 EfficiOS Inc. and others
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
8 *******************************************************************************/
10 package org
.eclipse
.tracecompass
.internal
.provisional
.analysis
.lami
.core
.types
;
12 import static org
.eclipse
.tracecompass
.common
.core
.NonNullUtils
.checkNotNull
;
15 import java
.util
.function
.Function
;
17 import org
.eclipse
.jdt
.annotation
.Nullable
;
18 import org
.eclipse
.tracecompass
.internal
.provisional
.analysis
.lami
.core
.LamiStrings
;
19 import org
.json
.JSONException
;
20 import org
.json
.JSONObject
;
22 import com
.google
.common
.collect
.ImmutableMap
;
25 * Base class for data types allowed in LAMI analysis scripts JSON output.
27 * @author Alexandre Montplaisir
28 * @author Philippe Proulx
30 public abstract class LamiData
{
33 * Enum of all the valid data types
35 @SuppressWarnings("javadoc")
36 public enum DataType
{
38 /* Generic JSON types */
39 STRING("string", "Value", false, null, LamiString
.class), //$NON-NLS-1$ //$NON-NLS-2$
40 INT("int", "Value", true, null, LamiInteger
.class), //$NON-NLS-1$ //$NON-NLS-2$
41 FLOAT("float", "Value", true, null, LamiNumber
.class), //$NON-NLS-1$ //$NON-NLS-2$
42 NUMBER("number", "Value", true, null, LamiNumber
.class), //$NON-NLS-1$ //$NON-NLS-2$
43 BOOL("bool", "Value", false, null, LamiBoolean
.class), //$NON-NLS-1$ //$NON-NLS-2$
45 /* Lami-specific data types */
46 RATIO("ratio", "Ratio", true, "%", LamiRatio
.class), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
47 TIMESTAMP("timestamp", "Timestamp", true, "ns", LamiTimestamp
.class), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
48 TIME_RANGE("time-range", "Time range", true, null, LamiTimeRange
.class), //$NON-NLS-1$ //$NON-NLS-2$
49 DURATION("duration", "Duration", true, "ns", LamiDuration
.class), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
50 SIZE("size", "Size", true, Messages
.LamiData_UnitBytes
, LamiSize
.class), //$NON-NLS-1$ //$NON-NLS-2$
51 BITRATE("bitrate", "Bitrate", true, Messages
.LamiData_UnitBitsPerSecond
, LamiBitrate
.class), //$NON-NLS-1$ //$NON-NLS-2$
52 SYSCALL("syscall", "System call", false, null, LamiSystemCall
.class), //$NON-NLS-1$ //$NON-NLS-2$
53 PROCESS("process", "Process", false, null, LamiProcess
.class), //$NON-NLS-1$ //$NON-NLS-2$
54 PATH("path", "Path", false, null, LamiPath
.class), //$NON-NLS-1$ //$NON-NLS-2$
55 FD("fd", "File descriptor", false, null, LamiFileDescriptor
.class), //$NON-NLS-1$ //$NON-NLS-2$
56 IRQ("irq", "IRQ", false, null, LamiIRQ
.class), //$NON-NLS-1$ //$NON-NLS-2$
57 CPU("cpu", "CPU", false, null, LamiCPU
.class), //$NON-NLS-1$ //$NON-NLS-2$
58 DISK("disk", "Disk", false, null, LamiDisk
.class), //$NON-NLS-1$ //$NON-NLS-2$
59 PART("part", "Disk partition", false, null, LamiDiskPartition
.class), //$NON-NLS-1$ //$NON-NLS-2$
60 NETIF("netif", "Network interface", false, null, LamiNetworkInterface
.class), //$NON-NLS-1$ //$NON-NLS-2$
61 UNKNOWN("unknown", "Value", false, null, LamiUnknown
.class), //$NON-NLS-1$ //$NON-NLS-2$
62 MIXED("mixed", "Value", false, null, null); //$NON-NLS-1$ //$NON-NLS-2$
64 private final String fName
;
65 private final String fTitle
;
66 private final boolean fIsContinuous
;
67 private final @Nullable String fUnits
;
68 private final @Nullable Class
<?
> fClass
;
70 private DataType(String name
, String title
, boolean isContinous
, @Nullable String units
, @Nullable Class
<?
> cls
) {
73 fIsContinuous
= isContinous
;
79 * Indicates if this data type represents a continuous numerical value.
81 * For example, time or bitrates are continuous values, but CPU or IRQ
82 * numbers are not (you can't have CPU 1.5!)
84 * @return If this aspect is continuous
86 public boolean isContinuous() {
91 * Get the units of this data type, if any.
93 * @return The units, or <code>null</code> if there are no units
95 public @Nullable String
getUnits() {
100 * The default title for columns containing these units.
102 * @return The data type's column title
104 public String
getTitle() {
109 * Get the data type from its JSON string representation.
113 * @return The corresponding data type
115 public static DataType
fromString(String value
) {
116 for (DataType type
: DataType
.values()) {
117 if (type
.fName
.equals(value
)) {
121 throw new IllegalArgumentException("Unrecognized type: " + value
); //$NON-NLS-1$
125 * Get the date type enum element from its implementation Class.
128 * The data type class
129 * @return The data type
131 public static @Nullable DataType
fromClass(Class
<?
extends LamiData
> cls
) {
132 for (DataType type
: DataType
.values()) {
133 if (cls
.equals(type
.fClass
)) {
143 public abstract @Nullable String
toString();
145 // ------------------------------------------------------------------------
146 // Convenience methods
147 // ------------------------------------------------------------------------
150 * Convenience method to get the "value" field from a JSON object. Many LAMI
151 * types have a "value" field.
155 * @return The read value
156 * @throws JSONException
157 * If the object does not actually have a "value" field.
159 private static final long getJSONObjectLongValue(JSONObject obj
) throws JSONException
{
160 return obj
.getLong(LamiStrings
.VALUE
);
164 * Convenience method to get the "name" field from a JSON object. Many LAMI
165 * types have a "nam" field.
169 * @return The read name
170 * @throws JSONException
171 * If the object does not actually have a "name" field.
173 private static final String
getJSONObjectStringName(JSONObject obj
) throws JSONException
{
174 return checkNotNull(obj
.getString(LamiStrings
.NAME
));
177 // ------------------------------------------------------------------------
178 // "Factory" methods and helpers
179 // ------------------------------------------------------------------------
182 * Factory method to build a new LamiData object from either a
183 * {@link JSONObject} or a standard Java {@link Object} representing a
188 * @return The corresponding LamiData object
189 * @throws JSONException
190 * If the object type is not supported
192 public static LamiData
createFromObject(Object obj
) throws JSONException
{
193 if (obj
instanceof JSONObject
) {
194 return createFromJsonObject((JSONObject
) obj
);
195 } else if (obj
.equals(JSONObject
.NULL
)) {
196 return LamiEmpty
.INSTANCE
;
198 return createFromPrimitiveObject(obj
);
203 private static interface CheckedJSONExceptionFunction
<T
, R
> {
204 R
apply(T t
) throws JSONException
;
208 * Map returning the Functions to build new LAMI objects for JSON primitive
211 private static final Map
<Class
<?
>, Function
<Object
, LamiData
>> PRIMITIVE_TYPE_GENERATOR
;
213 ImmutableMap
.Builder
<Class
<?
>, Function
<Object
, LamiData
>> primitiveTypeGenBuilder
= ImmutableMap
.builder();
214 primitiveTypeGenBuilder
.put(Boolean
.class, (o
) -> LamiBoolean
.instance((Boolean
) o
));
215 primitiveTypeGenBuilder
.put(Integer
.class, (o
) -> new LamiInteger(((Integer
) o
).longValue()));
216 primitiveTypeGenBuilder
.put(Long
.class, (o
) -> new LamiInteger((Long
) o
));
217 primitiveTypeGenBuilder
.put(Double
.class, (o
) -> new LamiNumber((Double
) o
));
218 primitiveTypeGenBuilder
.put(String
.class, (o
) -> new LamiString((String
) o
));
219 PRIMITIVE_TYPE_GENERATOR
= primitiveTypeGenBuilder
.build();
223 * Map returning the Functions to build new LAMI objects for LAMI-specific
226 private static final Map
<String
, CheckedJSONExceptionFunction
<JSONObject
, LamiData
>> COMPLEX_TYPE_GENERATOR
;
228 ImmutableMap
.Builder
<String
, CheckedJSONExceptionFunction
<JSONObject
, LamiData
>> complexTypeGenBuilder
= ImmutableMap
.builder();
229 complexTypeGenBuilder
.put(LamiStrings
.DATA_CLASS_BITRATE
, (obj
) -> new LamiBitrate(getJSONObjectLongValue(obj
)));
230 complexTypeGenBuilder
.put(LamiStrings
.DATA_CLASS_CPU
, (obj
) -> new LamiCPU(obj
.getLong(LamiStrings
.ID
)));
231 complexTypeGenBuilder
.put(LamiStrings
.DATA_CLASS_DISK
, (obj
) -> new LamiDisk(getJSONObjectStringName(obj
)));
232 complexTypeGenBuilder
.put(LamiStrings
.DATA_CLASS_DURATION
, (obj
) -> new LamiDuration(getJSONObjectLongValue(obj
)));
233 complexTypeGenBuilder
.put(LamiStrings
.DATA_CLASS_PART
, (obj
) -> new LamiDiskPartition(getJSONObjectStringName(obj
)));
234 complexTypeGenBuilder
.put(LamiStrings
.DATA_CLASS_FD
, (obj
) -> new LamiFileDescriptor(obj
.getLong(LamiStrings
.FD
)));
235 complexTypeGenBuilder
.put(LamiStrings
.DATA_CLASS_NETIF
, (obj
) -> new LamiNetworkInterface(getJSONObjectStringName(obj
)));
236 complexTypeGenBuilder
.put(LamiStrings
.DATA_CLASS_PATH
, (obj
) -> new LamiPath(checkNotNull(obj
.getString(LamiStrings
.PATH
))));
237 complexTypeGenBuilder
.put(LamiStrings
.DATA_CLASS_PROCESS
, (obj
) -> {
238 String name
= obj
.optString(LamiStrings
.NAME
);
239 Long pid
= (obj
.has(LamiStrings
.PID
) ? obj
.getLong(LamiStrings
.PID
) : null);
240 Long tid
= (obj
.has(LamiStrings
.TID
) ? obj
.getLong(LamiStrings
.TID
) : null);
242 return new LamiProcess(name
, pid
, tid
);
244 complexTypeGenBuilder
.put(LamiStrings
.DATA_CLASS_RATIO
, (obj
) -> new LamiRatio(obj
.getDouble(LamiStrings
.VALUE
)));
245 complexTypeGenBuilder
.put(LamiStrings
.DATA_CLASS_IRQ
, (obj
) -> {
246 LamiIRQ
.Type irqType
= LamiIRQ
.Type
.HARD
;
248 if (obj
.has(LamiStrings
.HARD
)) {
249 boolean isHardIrq
= obj
.getBoolean(LamiStrings
.HARD
);
250 irqType
= (isHardIrq ? LamiIRQ
.Type
.HARD
: LamiIRQ
.Type
.SOFT
);
253 int nr
= obj
.getInt(LamiStrings
.NR
);
254 String name
= obj
.optString(LamiStrings
.NAME
);
256 return new LamiIRQ(irqType
, nr
, name
);
258 complexTypeGenBuilder
.put(LamiStrings
.DATA_CLASS_SIZE
, (obj
) -> new LamiSize(getJSONObjectLongValue(obj
)));
259 complexTypeGenBuilder
.put(LamiStrings
.DATA_CLASS_SYSCALL
, (obj
) -> new LamiSystemCall(getJSONObjectStringName(obj
)));
260 complexTypeGenBuilder
.put(LamiStrings
.DATA_CLASS_TIME_RANGE
, (obj
) -> {
261 long begin
= obj
.getLong(LamiStrings
.BEGIN
);
262 long end
= obj
.getLong(LamiStrings
.END
);
264 return new LamiTimeRange(begin
, end
);
266 complexTypeGenBuilder
.put(LamiStrings
.DATA_CLASS_TIMESTAMP
, (obj
) -> new LamiTimestamp(getJSONObjectLongValue(obj
)));
267 complexTypeGenBuilder
.put(LamiStrings
.DATA_CLASS_UNKNOWN
, (obj
) -> LamiUnknown
.INSTANCE
);
268 COMPLEX_TYPE_GENERATOR
= complexTypeGenBuilder
.build();
272 * Create a new LamiData for a primitive type (Integer, String, etc.)
276 * @return A new corresponding LamiData object
277 * @throws JSONException
278 * If the object type is not supported
280 private static LamiData
createFromPrimitiveObject(Object obj
) throws JSONException
{
281 Function
<Object
, LamiData
> func
= PRIMITIVE_TYPE_GENERATOR
.get(obj
.getClass());
283 throw new JSONException("Unhandled type: " + obj
.toString() + " of type " + obj
.getClass().toString()); //$NON-NLS-1$ //$NON-NLS-2$
285 /* We never return null in the implementations */
286 return checkNotNull(func
.apply(obj
));
290 * Create a new LamiData for a LAMI-specific type from a {@link JSONObject}.
294 * @return A new corresponding LamiData object
295 * @throws JSONException
296 * If the object type is not supported
298 private static LamiData
createFromJsonObject(JSONObject obj
) throws JSONException
{
299 String dataClass
= obj
.optString(LamiStrings
.CLASS
);
301 if (dataClass
== null) {
302 throw new JSONException("Cannot find data class"); //$NON-NLS-1$
305 CheckedJSONExceptionFunction
<JSONObject
, LamiData
> func
= COMPLEX_TYPE_GENERATOR
.get(dataClass
);
308 throw new JSONException(String
.format("Unsupported data class \"%s\"", dataClass
)); //$NON-NLS-1$
311 return func
.apply(obj
);