Commit | Line | Data |
---|---|---|
866e5b51 | 1 | /******************************************************************************* |
60ae41e1 | 2 | * Copyright (c) 2011, 2014 Ericsson, Ecole Polytechnique de Montreal and others |
866e5b51 FC |
3 | * |
4 | * All rights reserved. This program and the accompanying materials are made | |
5 | * 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 | * Contributors: Matthew Khouzam - Initial API and implementation | |
10 | * Contributors: Simon Marchi - Initial API and implementation | |
11 | *******************************************************************************/ | |
12 | ||
f357bcd4 | 13 | package org.eclipse.tracecompass.ctf.core.event.types; |
866e5b51 | 14 | |
816fde81 MK |
15 | import java.util.Collections; |
16 | import java.util.HashSet; | |
866e5b51 FC |
17 | import java.util.LinkedList; |
18 | import java.util.List; | |
0c2409c1 | 19 | import java.util.Map; |
816fde81 | 20 | import java.util.Set; |
866e5b51 | 21 | |
f78eb6a7 | 22 | import org.eclipse.jdt.annotation.Nullable; |
680f9173 | 23 | import org.eclipse.tracecompass.ctf.core.CTFException; |
f357bcd4 AM |
24 | import org.eclipse.tracecompass.ctf.core.event.io.BitBuffer; |
25 | import org.eclipse.tracecompass.ctf.core.event.scope.IDefinitionScope; | |
a4fa4e36 | 26 | |
0c2409c1 MK |
27 | import com.google.common.collect.ImmutableMap; |
28 | ||
866e5b51 | 29 | /** |
d37aaa7f | 30 | * A CTF enum declaration. |
0594c61c | 31 | * |
816fde81 MK |
32 | * The definition of a enum point basic data type. It will take the data from a |
33 | * trace and store it (and make it fit) as an integer and a string. | |
d37aaa7f FC |
34 | * |
35 | * @version 1.0 | |
36 | * @author Matthew Khouzam | |
37 | * @author Simon Marchi | |
866e5b51 | 38 | */ |
8fa270f6 | 39 | public final class EnumDeclaration extends Declaration implements ISimpleDatatypeDeclaration { |
866e5b51 | 40 | |
0c2409c1 MK |
41 | /** |
42 | * A pair of longs class | |
43 | * | |
6b6f22ef | 44 | * @since 2.0 |
0c2409c1 MK |
45 | */ |
46 | public static class Pair { | |
47 | private final long fFirst; | |
48 | private final long fSecond; | |
49 | ||
50 | private Pair(long first, long second) { | |
51 | fFirst = first; | |
52 | fSecond = second; | |
53 | } | |
54 | ||
55 | /** | |
56 | * @return the first element | |
57 | */ | |
58 | public long getFirst() { | |
59 | return fFirst; | |
60 | } | |
61 | ||
62 | /** | |
63 | * @return the second element | |
64 | */ | |
65 | public long getSecond() { | |
66 | return fSecond; | |
67 | } | |
68 | } | |
69 | ||
866e5b51 FC |
70 | // ------------------------------------------------------------------------ |
71 | // Attributes | |
72 | // ------------------------------------------------------------------------ | |
73 | ||
816fde81 MK |
74 | private final EnumTable fTable = new EnumTable(); |
75 | private final IntegerDeclaration fContainerType; | |
3de23137 | 76 | private final Set<String> fLabels = new HashSet<>(); |
866e5b51 FC |
77 | |
78 | // ------------------------------------------------------------------------ | |
79 | // Constructors | |
80 | // ------------------------------------------------------------------------ | |
81 | ||
9ac2eb62 MK |
82 | /** |
83 | * constructor | |
84 | * | |
85 | * @param containerType | |
86 | * the enum is an int, this is the type that the data is | |
87 | * contained in. If you have 1000 possible values, you need at | |
88 | * least a 10 bit enum. If you store 2 values in a 128 bit int, | |
89 | * you are wasting space. | |
90 | */ | |
866e5b51 | 91 | public EnumDeclaration(IntegerDeclaration containerType) { |
816fde81 | 92 | fContainerType = containerType; |
866e5b51 FC |
93 | } |
94 | ||
95 | // ------------------------------------------------------------------------ | |
96 | // Getters/Setters/Predicates | |
97 | // ------------------------------------------------------------------------ | |
98 | ||
9ac2eb62 MK |
99 | /** |
100 | * | |
101 | * @return The container type | |
102 | */ | |
866e5b51 | 103 | public IntegerDeclaration getContainerType() { |
816fde81 | 104 | return fContainerType; |
866e5b51 FC |
105 | } |
106 | ||
fd74e6c1 MK |
107 | @Override |
108 | public long getAlignment() { | |
109 | return this.getContainerType().getAlignment(); | |
110 | } | |
9ac2eb62 | 111 | |
a4fa4e36 MK |
112 | @Override |
113 | public int getMaximumSize() { | |
114 | return fContainerType.getMaximumSize(); | |
115 | } | |
116 | ||
866e5b51 FC |
117 | // ------------------------------------------------------------------------ |
118 | // Operations | |
119 | // ------------------------------------------------------------------------ | |
120 | ||
121 | @Override | |
680f9173 | 122 | public EnumDefinition createDefinition(@Nullable IDefinitionScope definitionScope, String fieldName, BitBuffer input) throws CTFException { |
a4fa4e36 MK |
123 | alignRead(input); |
124 | IntegerDefinition value = getContainerType().createDefinition(definitionScope, fieldName, input); | |
125 | return new EnumDefinition(this, definitionScope, fieldName, value); | |
866e5b51 FC |
126 | } |
127 | ||
9ac2eb62 | 128 | /** |
8fa270f6 MK |
129 | * Add a value. Do not overlap, this is <em><strong>not</strong></em> an |
130 | * interval tree. | |
816fde81 MK |
131 | * |
132 | * @param low | |
133 | * lowest value that this int can be to have label as a return | |
134 | * string | |
135 | * @param high | |
136 | * highest value that this int can be to have label as a return | |
137 | * string | |
138 | * @param label | |
139 | * the name of the value. | |
9ac2eb62 MK |
140 | * @return was the value be added? true == success |
141 | */ | |
f78eb6a7 | 142 | public boolean add(long low, long high, @Nullable String label) { |
816fde81 MK |
143 | fLabels.add(label); |
144 | return fTable.add(low, high, label); | |
866e5b51 FC |
145 | } |
146 | ||
9ac2eb62 | 147 | /** |
816fde81 MK |
148 | * Check if the label for a value (enum a{day=0,night=1} would return "day" |
149 | * for query(0) | |
150 | * | |
151 | * @param value | |
152 | * the value to lookup | |
9ac2eb62 MK |
153 | * @return the label of that value, can be null |
154 | */ | |
f78eb6a7 | 155 | public @Nullable String query(long value) { |
816fde81 MK |
156 | return fTable.query(value); |
157 | } | |
158 | ||
0c2409c1 MK |
159 | /** |
160 | * Get the lookup table | |
161 | * | |
162 | * @return the lookup table | |
6b6f22ef | 163 | * @since 2.0 |
0c2409c1 MK |
164 | */ |
165 | public Map<String, Pair> getEnumTable() { | |
166 | ImmutableMap.Builder<String, Pair> builder = new ImmutableMap.Builder<>(); | |
167 | for (LabelAndRange range : fTable.ranges) { | |
168 | builder.put(range.getLabel(), new Pair(range.low, range.high)); | |
169 | } | |
170 | return builder.build(); | |
171 | ||
172 | } | |
173 | ||
816fde81 MK |
174 | /** |
175 | * Gets a set of labels of the enum | |
176 | * | |
177 | * @return A set of labels of the enum, can be empty but not null | |
178 | */ | |
179 | public Set<String> getLabels() { | |
180 | return Collections.unmodifiableSet(fLabels); | |
866e5b51 FC |
181 | } |
182 | ||
866e5b51 FC |
183 | /* |
184 | * Maps integer range -> string. A simple list for now, but feel free to | |
185 | * optimize it. Babeltrace suggests an interval tree. | |
186 | */ | |
816fde81 | 187 | private class EnumTable { |
866e5b51 | 188 | |
3de23137 | 189 | private final List<LabelAndRange> ranges = new LinkedList<>(); |
866e5b51 FC |
190 | |
191 | public EnumTable() { | |
192 | } | |
193 | ||
e00e6663 | 194 | public synchronized boolean add(long low, long high, @Nullable String label) { |
816fde81 | 195 | LabelAndRange newRange = new LabelAndRange(low, high, label); |
866e5b51 | 196 | |
816fde81 | 197 | for (LabelAndRange r : ranges) { |
866e5b51 FC |
198 | if (r.intersects(newRange)) { |
199 | return false; | |
200 | } | |
201 | } | |
202 | ||
203 | ranges.add(newRange); | |
204 | ||
205 | return true; | |
206 | } | |
207 | ||
9ac2eb62 | 208 | /** |
a511da0d | 209 | * Return the first label that matches a value |
816fde81 MK |
210 | * |
211 | * @param value | |
212 | * the value to query | |
9ac2eb62 MK |
213 | * @return the label corresponding to that value |
214 | */ | |
e00e6663 | 215 | public synchronized @Nullable String query(long value) { |
816fde81 | 216 | for (LabelAndRange r : ranges) { |
866e5b51 | 217 | if (r.intersects(value)) { |
096bc534 | 218 | return r.getLabel(); |
866e5b51 FC |
219 | } |
220 | } | |
866e5b51 FC |
221 | return null; |
222 | } | |
223 | ||
e00e6663 MK |
224 | @Override |
225 | public synchronized int hashCode() { | |
226 | final int prime = 31; | |
227 | int result = 1; | |
228 | for (LabelAndRange range : ranges) { | |
229 | result = prime * result + range.hashCode(); | |
230 | } | |
231 | return result; | |
232 | } | |
233 | ||
234 | @Override | |
235 | public synchronized boolean equals(@Nullable Object obj) { | |
236 | if (this == obj) { | |
237 | return true; | |
238 | } | |
239 | if (obj == null) { | |
240 | return false; | |
241 | } | |
242 | if (getClass() != obj.getClass()) { | |
243 | return false; | |
244 | } | |
245 | EnumTable other = (EnumTable) obj; | |
246 | if (ranges.size() != other.ranges.size()) { | |
247 | return false; | |
248 | } | |
249 | for (int i = 0; i < ranges.size(); i++) { | |
250 | if (!ranges.get(i).equals(other.ranges.get(i))) { | |
251 | return false; | |
252 | } | |
253 | } | |
254 | return true; | |
255 | } | |
256 | ||
816fde81 | 257 | } |
866e5b51 | 258 | |
096bc534 | 259 | private static class LabelAndRange { |
866e5b51 | 260 | |
816fde81 | 261 | private final long low, high; |
f78eb6a7 | 262 | private final @Nullable String fLabel; |
866e5b51 | 263 | |
096bc534 MK |
264 | /** |
265 | * Get the label | |
266 | * | |
267 | * @return the label | |
268 | */ | |
e00e6663 | 269 | public @Nullable String getLabel() { |
096bc534 MK |
270 | return fLabel; |
271 | } | |
272 | ||
f78eb6a7 | 273 | public LabelAndRange(long low, long high, @Nullable String str) { |
816fde81 MK |
274 | this.low = low; |
275 | this.high = high; | |
276 | this.fLabel = str; | |
277 | } | |
866e5b51 | 278 | |
816fde81 MK |
279 | public boolean intersects(long i) { |
280 | return (i >= this.low) && (i <= this.high); | |
281 | } | |
282 | ||
283 | public boolean intersects(LabelAndRange other) { | |
284 | return this.intersects(other.low) | |
285 | || this.intersects(other.high); | |
866e5b51 | 286 | } |
e00e6663 MK |
287 | |
288 | @Override | |
289 | public int hashCode() { | |
290 | final int prime = 31; | |
291 | int result = 1; | |
292 | final String label = fLabel; | |
293 | result = prime * result + ((label == null) ? 0 : label.hashCode()); | |
294 | result = prime * result + (int) (high ^ (high >>> 32)); | |
295 | result = prime * result + (int) (low ^ (low >>> 32)); | |
296 | return result; | |
297 | } | |
298 | ||
299 | @Override | |
300 | public boolean equals(@Nullable Object obj) { | |
301 | if (this == obj) { | |
302 | return true; | |
303 | } | |
304 | if (obj == null) { | |
305 | return false; | |
306 | } | |
307 | if (getClass() != obj.getClass()) { | |
308 | return false; | |
309 | } | |
310 | LabelAndRange other = (LabelAndRange) obj; | |
311 | final String label = fLabel; | |
312 | if (label == null) { | |
313 | if (other.fLabel != null) { | |
314 | return false; | |
315 | } | |
316 | } else if (!label.equals(other.fLabel)) { | |
317 | return false; | |
318 | } | |
319 | if (high != other.high) { | |
320 | return false; | |
321 | } | |
322 | if (low != other.low) { | |
323 | return false; | |
324 | } | |
325 | return true; | |
326 | } | |
866e5b51 FC |
327 | } |
328 | ||
329 | @Override | |
330 | public String toString() { | |
331 | /* Only used for debugging */ | |
66aa25f0 MK |
332 | StringBuilder sb = new StringBuilder(); |
333 | sb.append("[declaration] enum["); //$NON-NLS-1$ | |
334 | for (String label : fLabels) { | |
335 | sb.append("label:").append(label).append(' '); //$NON-NLS-1$ | |
336 | } | |
337 | sb.append("type:").append(fContainerType.toString()); //$NON-NLS-1$ | |
338 | sb.append(']'); | |
339 | String string = sb.toString(); | |
340 | if (string == null) { | |
341 | throw new IllegalStateException(); | |
342 | } | |
343 | return string; | |
866e5b51 FC |
344 | } |
345 | ||
e00e6663 MK |
346 | @Override |
347 | public int hashCode() { | |
348 | final int prime = 31; | |
349 | int result = prime + fContainerType.hashCode(); | |
350 | for (String label : fLabels) { | |
351 | result = prime * result + label.hashCode(); | |
352 | } | |
353 | result = prime * result + fTable.hashCode(); | |
354 | return result; | |
355 | } | |
356 | ||
357 | @Override | |
358 | public boolean equals(@Nullable Object obj) { | |
359 | if (this == obj) { | |
360 | return true; | |
361 | } | |
362 | if (obj == null) { | |
363 | return false; | |
364 | } | |
365 | if (getClass() != obj.getClass()) { | |
366 | return false; | |
367 | } | |
368 | EnumDeclaration other = (EnumDeclaration) obj; | |
369 | if (!fContainerType.equals(other.fContainerType)) { | |
370 | return false; | |
371 | } | |
372 | if (fLabels.size() != other.fLabels.size()) { | |
373 | return false; | |
374 | } | |
375 | if (!fLabels.containsAll(other.fLabels)) { | |
376 | return false; | |
377 | } | |
378 | if (!fTable.equals(other.fTable)) { | |
379 | return false; | |
380 | } | |
381 | return true; | |
382 | } | |
383 | ||
66aa25f0 MK |
384 | @Override |
385 | public boolean isBinaryEquivalent(@Nullable IDeclaration obj) { | |
386 | if (this == obj) { | |
387 | return true; | |
388 | } | |
389 | if (obj == null) { | |
390 | return false; | |
391 | } | |
392 | if (getClass() != obj.getClass()) { | |
393 | return false; | |
394 | } | |
395 | EnumDeclaration other = (EnumDeclaration) obj; | |
396 | if (!fContainerType.isBinaryEquivalent(other.fContainerType)) { | |
397 | return false; | |
398 | } | |
399 | if (fLabels.size() != other.fLabels.size()) { | |
400 | return false; | |
401 | } | |
402 | if (!fLabels.containsAll(other.fLabels)) { | |
403 | return false; | |
404 | } | |
405 | if (!fTable.equals(other.fTable)) { | |
406 | return false; | |
407 | } | |
408 | return true; | |
409 | } | |
410 | ||
866e5b51 | 411 | } |