Commit | Line | Data |
---|---|---|
4208b510 AM |
1 | /******************************************************************************* |
2 | * Copyright (c) 2015, 2016 EfficiOS Inc. and others | |
3 | * | |
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 | *******************************************************************************/ | |
9 | ||
10 | package org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types; | |
11 | ||
12 | import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull; | |
13 | ||
14 | import java.util.Map; | |
15 | import java.util.function.Function; | |
16 | ||
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; | |
21 | ||
22 | import com.google.common.collect.ImmutableMap; | |
23 | ||
24 | /** | |
25 | * Base class for data types allowed in LAMI analysis scripts JSON output. | |
26 | * | |
27 | * @author Alexandre Montplaisir | |
28 | * @author Philippe Proulx | |
29 | */ | |
30 | public abstract class LamiData { | |
31 | ||
32 | /** | |
33 | * Enum of all the valid data types | |
34 | */ | |
35 | @SuppressWarnings("javadoc") | |
36 | public enum DataType { | |
37 | ||
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$ | |
44 | ||
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$ | |
63 | ||
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; | |
69 | ||
70 | private DataType(String name, String title, boolean isContinous, @Nullable String units, @Nullable Class<?> cls) { | |
71 | fName = name; | |
72 | fTitle = title; | |
73 | fIsContinuous = isContinous; | |
74 | fUnits = units; | |
75 | fClass = cls; | |
76 | } | |
77 | ||
78 | /** | |
79 | * Indicates if this data type represents a continuous numerical value. | |
80 | * | |
81 | * For example, time or bitrates are continuous values, but CPU or IRQ | |
82 | * numbers are not (you can't have CPU 1.5!) | |
83 | * | |
84 | * @return If this aspect is continuous | |
85 | */ | |
86 | public boolean isContinuous() { | |
87 | return fIsContinuous; | |
88 | } | |
89 | ||
90 | /** | |
91 | * Get the units of this data type, if any. | |
92 | * | |
93 | * @return The units, or <code>null</code> if there are no units | |
94 | */ | |
95 | public @Nullable String getUnits() { | |
96 | return fUnits; | |
97 | } | |
98 | ||
99 | /** | |
100 | * The default title for columns containing these units. | |
101 | * | |
102 | * @return The data type's column title | |
103 | */ | |
104 | public String getTitle() { | |
105 | return fTitle; | |
106 | } | |
107 | ||
108 | /** | |
109 | * Get the data type from its JSON string representation. | |
110 | * | |
111 | * @param value | |
112 | * The string | |
113 | * @return The corresponding data type | |
114 | */ | |
115 | public static DataType fromString(String value) { | |
116 | for (DataType type : DataType.values()) { | |
117 | if (type.fName.equals(value)) { | |
118 | return type; | |
119 | } | |
120 | } | |
4d717917 | 121 | throw new IllegalArgumentException("Unrecognized type: " + value); //$NON-NLS-1$ |
4208b510 AM |
122 | } |
123 | ||
124 | /** | |
125 | * Get the date type enum element from its implementation Class. | |
126 | * | |
127 | * @param cls | |
128 | * The data type class | |
129 | * @return The data type | |
130 | */ | |
131 | public static @Nullable DataType fromClass(Class<? extends LamiData> cls) { | |
132 | for (DataType type : DataType.values()) { | |
133 | if (cls.equals(type.fClass)) { | |
134 | return type; | |
135 | } | |
136 | } | |
137 | ||
138 | return null; | |
139 | } | |
140 | } | |
141 | ||
142 | @Override | |
143 | public abstract @Nullable String toString(); | |
144 | ||
145 | // ------------------------------------------------------------------------ | |
146 | // Convenience methods | |
147 | // ------------------------------------------------------------------------ | |
148 | ||
149 | /** | |
150 | * Convenience method to get the "value" field from a JSON object. Many LAMI | |
151 | * types have a "value" field. | |
152 | * | |
153 | * @param obj | |
154 | * The JSON object | |
155 | * @return The read value | |
156 | * @throws JSONException | |
157 | * If the object does not actually have a "value" field. | |
158 | */ | |
159 | private static final long getJSONObjectLongValue(JSONObject obj) throws JSONException { | |
160 | return obj.getLong(LamiStrings.VALUE); | |
161 | } | |
162 | ||
163 | /** | |
164 | * Convenience method to get the "name" field from a JSON object. Many LAMI | |
165 | * types have a "nam" field. | |
166 | * | |
167 | * @param obj | |
168 | * The JSON object | |
169 | * @return The read name | |
170 | * @throws JSONException | |
171 | * If the object does not actually have a "name" field. | |
172 | */ | |
173 | private static final String getJSONObjectStringName(JSONObject obj) throws JSONException { | |
174 | return checkNotNull(obj.getString(LamiStrings.NAME)); | |
175 | } | |
176 | ||
177 | // ------------------------------------------------------------------------ | |
178 | // "Factory" methods and helpers | |
179 | // ------------------------------------------------------------------------ | |
180 | ||
181 | /** | |
182 | * Factory method to build a new LamiData object from either a | |
183 | * {@link JSONObject} or a standard Java {@link Object} representing a | |
184 | * primitive type. | |
185 | * | |
186 | * @param obj | |
187 | * The source object | |
188 | * @return The corresponding LamiData object | |
189 | * @throws JSONException | |
190 | * If the object type is not supported | |
191 | */ | |
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; | |
197 | } else { | |
198 | return createFromPrimitiveObject(obj); | |
199 | } | |
200 | } | |
201 | ||
202 | @FunctionalInterface | |
203 | private static interface CheckedJSONExceptionFunction<T, R> { | |
204 | R apply(T t) throws JSONException; | |
205 | } | |
206 | ||
207 | /** | |
208 | * Map returning the Functions to build new LAMI objects for JSON primitive | |
209 | * types | |
210 | */ | |
211 | private static final Map<Class<?>, Function<Object, LamiData>> PRIMITIVE_TYPE_GENERATOR; | |
212 | static { | |
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(); | |
220 | } | |
221 | ||
222 | /** | |
223 | * Map returning the Functions to build new LAMI objects for LAMI-specific | |
224 | * types | |
225 | */ | |
226 | private static final Map<String, CheckedJSONExceptionFunction<JSONObject, LamiData>> COMPLEX_TYPE_GENERATOR; | |
227 | static { | |
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); | |
241 | ||
242 | return new LamiProcess(name, pid, tid); | |
243 | }); | |
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; | |
247 | ||
248 | if (obj.has(LamiStrings.HARD)) { | |
249 | boolean isHardIrq = obj.getBoolean(LamiStrings.HARD); | |
250 | irqType = (isHardIrq ? LamiIRQ.Type.HARD : LamiIRQ.Type.SOFT); | |
251 | } | |
252 | ||
253 | int nr = obj.getInt(LamiStrings.NR); | |
254 | String name = obj.optString(LamiStrings.NAME); | |
255 | ||
256 | return new LamiIRQ(irqType, nr, name); | |
257 | }); | |
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); | |
263 | ||
264 | return new LamiTimeRange(begin, end); | |
265 | }); | |
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(); | |
269 | } | |
270 | ||
271 | /** | |
272 | * Create a new LamiData for a primitive type (Integer, String, etc.) | |
273 | * | |
274 | * @param obj | |
275 | * The source object | |
276 | * @return A new corresponding LamiData object | |
277 | * @throws JSONException | |
278 | * If the object type is not supported | |
279 | */ | |
280 | private static LamiData createFromPrimitiveObject(Object obj) throws JSONException { | |
281 | Function<Object, LamiData> func = PRIMITIVE_TYPE_GENERATOR.get(obj.getClass()); | |
282 | if (func == null) { | |
283 | throw new JSONException("Unhandled type: " + obj.toString() + " of type " + obj.getClass().toString()); //$NON-NLS-1$ //$NON-NLS-2$ | |
284 | } | |
285 | /* We never return null in the implementations */ | |
286 | return checkNotNull(func.apply(obj)); | |
287 | } | |
288 | ||
289 | /** | |
290 | * Create a new LamiData for a LAMI-specific type from a {@link JSONObject}. | |
291 | * | |
292 | * @param obj | |
293 | * The source object | |
294 | * @return A new corresponding LamiData object | |
295 | * @throws JSONException | |
296 | * If the object type is not supported | |
297 | */ | |
298 | private static LamiData createFromJsonObject(JSONObject obj) throws JSONException { | |
299 | String dataClass = obj.optString(LamiStrings.CLASS); | |
300 | ||
301 | if (dataClass == null) { | |
302 | throw new JSONException("Cannot find data class"); //$NON-NLS-1$ | |
303 | } | |
304 | ||
305 | CheckedJSONExceptionFunction<JSONObject, LamiData> func = COMPLEX_TYPE_GENERATOR.get(dataClass); | |
306 | ||
307 | if (func == null) { | |
308 | throw new JSONException(String.format("Unsupported data class \"%s\"", dataClass)); //$NON-NLS-1$ | |
309 | } | |
310 | ||
311 | return func.apply(obj); | |
312 | } | |
313 | } |