1 /*
   2  * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
   3  *
   4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   5  *
   6  * The contents of this file are subject to the terms of either the Universal Permissive License
   7  * v 1.0 as shown at http://oss.oracle.com/licenses/upl
   8  *
   9  * or the following license:
  10  *
  11  * Redistribution and use in source and binary forms, with or without modification, are permitted
  12  * provided that the following conditions are met:
  13  *
  14  * 1. Redistributions of source code must retain the above copyright notice, this list of conditions
  15  * and the following disclaimer.
  16  *
  17  * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
  18  * conditions and the following disclaimer in the documentation and/or other materials provided with
  19  * the distribution.
  20  *
  21  * 3. Neither the name of the copyright holder nor the names of its contributors may be used to
  22  * endorse or promote products derived from this software without specific prior written permission.
  23  *
  24  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
  25  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  26  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
  27  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  29  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  30  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
  31  * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  32  */
  33 package org.openjdk.jmc.flightrecorder.internal.parser.v1;
  34 
  35 import java.io.IOException;
  36 import java.text.MessageFormat;
  37 import java.util.ArrayList;
  38 import java.util.HashMap;
  39 import java.util.Iterator;
  40 import java.util.List;
  41 import java.util.Map;
  42 
  43 import org.openjdk.jmc.common.collection.FastAccessNumberMap;
  44 import org.openjdk.jmc.common.unit.ContentType;
  45 import org.openjdk.jmc.common.unit.IUnit;
  46 import org.openjdk.jmc.common.unit.StructContentType;
  47 import org.openjdk.jmc.common.unit.UnitLookup;
  48 import org.openjdk.jmc.common.util.LabeledIdentifier;
  49 import org.openjdk.jmc.flightrecorder.internal.InvalidJfrFileException;
  50 import org.openjdk.jmc.flightrecorder.internal.parser.LoaderContext;
  51 import org.openjdk.jmc.flightrecorder.internal.parser.v1.ChunkMetadata.AnnotatedElement;
  52 import org.openjdk.jmc.flightrecorder.internal.parser.v1.ChunkMetadata.AnnotationElement;
  53 import org.openjdk.jmc.flightrecorder.internal.parser.v1.ChunkMetadata.ClassElement;
  54 import org.openjdk.jmc.flightrecorder.internal.parser.v1.ChunkMetadata.FieldElement;
  55 import org.openjdk.jmc.flightrecorder.internal.parser.v1.StructTypes.JfrFrame;
  56 import org.openjdk.jmc.flightrecorder.internal.parser.v1.StructTypes.JfrJavaClass;
  57 import org.openjdk.jmc.flightrecorder.internal.parser.v1.StructTypes.JfrJavaClassLoader;
  58 import org.openjdk.jmc.flightrecorder.internal.parser.v1.StructTypes.JfrJavaModule;
  59 import org.openjdk.jmc.flightrecorder.internal.parser.v1.StructTypes.JfrJavaPackage;
  60 import org.openjdk.jmc.flightrecorder.internal.parser.v1.StructTypes.JfrMethod;
  61 import org.openjdk.jmc.flightrecorder.internal.parser.v1.StructTypes.JfrOldObject;
  62 import org.openjdk.jmc.flightrecorder.internal.parser.v1.StructTypes.JfrOldObjectArray;
  63 import org.openjdk.jmc.flightrecorder.internal.parser.v1.StructTypes.JfrOldObjectField;
  64 import org.openjdk.jmc.flightrecorder.internal.parser.v1.StructTypes.JfrOldObjectGcRoot;
  65 import org.openjdk.jmc.flightrecorder.internal.parser.v1.StructTypes.JfrStackTrace;
  66 import org.openjdk.jmc.flightrecorder.internal.parser.v1.StructTypes.JfrThread;
  67 import org.openjdk.jmc.flightrecorder.internal.parser.v1.StructTypes.JfrThreadGroup;
  68 import org.openjdk.jmc.flightrecorder.internal.parser.v1.ValueReaders.AbstractStructReader;
  69 import org.openjdk.jmc.flightrecorder.internal.parser.v1.ValueReaders.ArrayReader;
  70 import org.openjdk.jmc.flightrecorder.internal.parser.v1.ValueReaders.IValueReader;
  71 import org.openjdk.jmc.flightrecorder.internal.parser.v1.ValueReaders.PoolReader;
  72 import org.openjdk.jmc.flightrecorder.internal.parser.v1.ValueReaders.PrimitiveReader;
  73 import org.openjdk.jmc.flightrecorder.internal.parser.v1.ValueReaders.QuantityReader;
  74 import org.openjdk.jmc.flightrecorder.internal.parser.v1.ValueReaders.ReflectiveReader;
  75 import org.openjdk.jmc.flightrecorder.internal.parser.v1.ValueReaders.StringReader;
  76 import org.openjdk.jmc.flightrecorder.internal.parser.v1.ValueReaders.StructReader;
  77 import org.openjdk.jmc.flightrecorder.internal.parser.v1.ValueReaders.TicksTimestampReader;
  78 import org.openjdk.jmc.flightrecorder.internal.util.JfrInternalConstants;
  79 import org.openjdk.jmc.flightrecorder.messages.internal.Messages;
  80 import org.openjdk.jmc.flightrecorder.parser.IEventSink;
  81 import org.openjdk.jmc.flightrecorder.parser.ValueField;
  82 
  83 class TypeManager {
  84 
  85         private static class NopEventSink implements IEventSink {
  86                 @Override
  87                 public void addEvent(Object[] values) {
  88                 }
  89         }
  90 
  91         private static class SkipFieldsEventSink implements IEventSink {
  92                 private final IEventSink subSink;
  93                 private final List<Integer> skipFields;
  94                 private final Object[] reusableStruct;
  95 
  96                 SkipFieldsEventSink(IEventSink subSink, List<Integer> skipFields, int fieldCount) {
  97                         this.subSink = subSink;
  98                         this.skipFields = skipFields;
  99                         reusableStruct = new Object[fieldCount - skipFields.size()];
 100                 }
 101 
 102                 @Override
 103                 public void addEvent(Object[] fieldValues) {
 104                         Iterator<Integer> skipIter = skipFields.iterator();
 105                         int skipNext = skipIter.next();
 106                         int j = 0;
 107                         for (int i = 0; i < fieldValues.length; i++) {
 108                                 if (i != skipNext) {
 109                                         reusableStruct[j++] = fieldValues[i];
 110                                 } else if (skipIter.hasNext()) {
 111                                         skipNext = skipIter.next();
 112                                 }
 113                         }
 114                         subSink.addEvent(reusableStruct);
 115                 }
 116         }
 117 
 118         // NOTE: Using constant pool id as identifier.
 119         private static final Map<Long, StructContentType<Object[]>> STRUCT_TYPES = new HashMap<>();
 120 
 121         private class TypeEntry {
 122                 private static final String STRUCT_TYPE_STACK_TRACE = "com.oracle.jfr.types.StackTrace"; //$NON-NLS-1$
 123                 private static final String STRUCT_TYPE_STACK_FRAME = "com.oracle.jfr.types.StackFrame"; //$NON-NLS-1$
 124                 private static final String STRUCT_TYPE_METHOD = "com.oracle.jfr.types.Method"; //$NON-NLS-1$
 125                 private static final String STRUCT_TYPE_CLASS = "java.lang.Class"; //$NON-NLS-1$
 126                 private static final String STRUCT_TYPE_CLASS_LOADER = "com.oracle.jfr.types.ClassLoader"; //$NON-NLS-1$
 127                 private static final String STRUCT_TYPE_MODULE = "com.oracle.jfr.types.Module"; //$NON-NLS-1$
 128                 private static final String STRUCT_TYPE_PACKAGE = "com.oracle.jfr.types.Package"; //$NON-NLS-1$
 129                 private static final String STRUCT_TYPE_OLD_OBJECT = "com.oracle.jfr.types.OldObject"; //$NON-NLS-1$
 130                 private static final String STRUCT_TYPE_OLD_OBJECT_ARRAY = "com.oracle.jfr.types.OldObjectArray"; //$NON-NLS-1$
 131                 private static final String STRUCT_TYPE_OLD_OBJECT_FIELD = "com.oracle.jfr.types.OldObjectField"; //$NON-NLS-1$
 132                 private static final String STRUCT_TYPE_OLD_OBJECT_GC_ROOT = "com.oracle.jfr.types.OldObjectGcRoot"; //$NON-NLS-1$
 133                 private static final String STRUCT_TYPE_THREAD_GROUP = "com.oracle.jfr.types.ThreadGroup"; //$NON-NLS-1$
 134                 private static final String STRUCT_TYPE_THREAD = "java.lang.Thread"; //$NON-NLS-1$
 135 
 136                 final ClassElement element;
 137                 final FastAccessNumberMap<Object> constants;
 138                 private IValueReader reader;
 139 
 140                 TypeEntry(ClassElement element) {
 141                         this(element, new FastAccessNumberMap<>());
 142                 }
 143 
 144                 /**
 145                  * Temporary constructor for sharing constants. Only used for Strings.
 146                  */
 147                 TypeEntry(ClassElement element, FastAccessNumberMap<Object> constants) {
 148                         this.element = element;
 149                         this.constants = constants;
 150                 }
 151 
 152                 public IValueReader getReader() throws InvalidJfrFileException {
 153                         if (reader == null) {
 154                                 int fieldCount = element.getFieldCount();
 155                                 if (element.isSimpleType() && fieldCount == 1) {
 156                                         FieldElement singleField = element.fields.get(0);
 157                                         if (singleField.classId == element.classId) {
 158                                                 throw new InvalidJfrFileException(
 159                                                                 element.typeIdentifier + " is a simple type referring to itself"); //$NON-NLS-1$
 160                                         } else {
 161                                                 reader = createFieldReader(element.fields.get(0), null);
 162                                         }
 163                                 } else if (fieldCount == 0 && element.superType == null) {
 164                                         if (StringReader.STRING.equals(element.typeIdentifier)) {
 165                                                 reader = new StringReader(constants);
 166                                         } else {
 167                                                 reader = new PrimitiveReader(element.typeIdentifier);
 168                                         }
 169                                 } else {
 170                                         AbstractStructReader typeReader = createStructReader(element.typeIdentifier, element.label,
 171                                                         element.description, fieldCount);
 172                                         // assign before resolving field since it may be recursive
 173                                         reader = typeReader;
 174                                         for (int i = 0; i < fieldCount; i++) {
 175                                                 FieldElement fe = element.fields.get(i);
 176                                                 IValueReader reader = createFieldReader(fe, null);
 177                                                 String labelOrId = (fe.label == null) ? fe.fieldIdentifier : fe.label;
 178                                                 typeReader.addField(fe.fieldIdentifier, labelOrId, fe.description, reader);
 179                                         }
 180                                 }
 181                         }
 182                         return reader;
 183                 }
 184 
 185                 private AbstractStructReader createStructReader(
 186                         String identifier, String name, String description, int fieldCount) {
 187                         switch (identifier) {
 188                         case STRUCT_TYPE_THREAD:
 189                                 return new ReflectiveReader(JfrThread.class, fieldCount, UnitLookup.THREAD);
 190                         case STRUCT_TYPE_THREAD_GROUP:
 191                                 return new ReflectiveReader(JfrThreadGroup.class, fieldCount, UnitLookup.THREAD_GROUP);
 192                         case STRUCT_TYPE_CLASS:
 193                                 return new ReflectiveReader(JfrJavaClass.class, fieldCount, UnitLookup.CLASS);
 194                         case STRUCT_TYPE_CLASS_LOADER:
 195                                 return new ReflectiveReader(JfrJavaClassLoader.class, fieldCount, UnitLookup.CLASS_LOADER);
 196                         case STRUCT_TYPE_OLD_OBJECT_GC_ROOT:
 197                                 return new ReflectiveReader(JfrOldObjectGcRoot.class, fieldCount, UnitLookup.OLD_OBJECT_GC_ROOT);
 198                         case STRUCT_TYPE_OLD_OBJECT:
 199                                 return new ReflectiveReader(JfrOldObject.class, fieldCount, UnitLookup.OLD_OBJECT);
 200                         case STRUCT_TYPE_OLD_OBJECT_ARRAY:
 201                                 return new ReflectiveReader(JfrOldObjectArray.class, fieldCount, UnitLookup.OLD_OBJECT_ARRAY);
 202                         case STRUCT_TYPE_OLD_OBJECT_FIELD:
 203                                 return new ReflectiveReader(JfrOldObjectField.class, fieldCount, UnitLookup.OLD_OBJECT_FIELD);
 204                         case STRUCT_TYPE_METHOD:
 205                                 return new ReflectiveReader(JfrMethod.class, fieldCount, UnitLookup.METHOD);
 206                         case STRUCT_TYPE_STACK_FRAME:
 207                                 return new ReflectiveReader(JfrFrame.class, fieldCount, UnitLookup.STACKTRACE_FRAME);
 208                         case STRUCT_TYPE_STACK_TRACE:
 209                                 return new ReflectiveReader(JfrStackTrace.class, fieldCount, UnitLookup.STACKTRACE);
 210                         case STRUCT_TYPE_MODULE:
 211                                 return new ReflectiveReader(JfrJavaModule.class, fieldCount, UnitLookup.MODULE);
 212                         case STRUCT_TYPE_PACKAGE:
 213                                 return new ReflectiveReader(JfrJavaPackage.class, fieldCount, UnitLookup.PACKAGE);
 214                         default:
 215                                 synchronized (STRUCT_TYPES) {
 216                                         StructContentType<Object[]> structType = STRUCT_TYPES.get(element.classId);
 217                                         if (structType == null) {
 218                                                 structType = new StructContentType<>(element.typeIdentifier, element.label,
 219                                                                 element.description);
 220                                                 STRUCT_TYPES.put(element.classId, structType);
 221                                         }
 222                                         return new StructReader(structType, fieldCount);
 223                                 }
 224                         }
 225                 }
 226 
 227                 void resolveConstants() throws InvalidJfrFileException {
 228                         IValueReader r = reader;
 229                         if (r != null) {
 230                                 for (Object c : constants) {
 231                                         r.resolve(c);
 232                                         // FIXME: During resolve, some constants may become equal. Should we ensure canonical constants?
 233                                 }
 234                         }
 235                 }
 236 
 237                 void readConstant(IDataInput input) throws InvalidJfrFileException, IOException {
 238                         // FIXME: Constant lookup can perhaps be optimized (across chunks)
 239                         long constantIndex = input.readLong();
 240                         Object value = constants.get(constantIndex);
 241                         if (value == null) {
 242                                 value = getReader().read(input, true);
 243                                 constants.put(constantIndex, value);
 244                         } else {
 245                                 getReader().skip(input);
 246                         }
 247                 }
 248         }
 249 
 250         private class EventTypeEntry {
 251                 private final ClassElement element;
 252                 private final List<IValueReader> valueReaders;
 253                 private Object[] reusableStruct;
 254                 private IEventSink eventSink;
 255                 private LabeledIdentifier eventType;
 256 
 257                 EventTypeEntry(ClassElement element) {
 258                         this.element = element;
 259                         valueReaders = new ArrayList<>(element.getFieldCount());
 260                 }
 261 
 262                 void readEvent(IDataInput input) throws InvalidJfrFileException, IOException {
 263                         for (int i = 0; i < valueReaders.size(); i++) {
 264                                 reusableStruct[i] = valueReaders.get(i).read(input, false);
 265                         }
 266                         eventSink.addEvent(reusableStruct);
 267                 }
 268 
 269                 LabeledIdentifier getValueType() {
 270                         if (eventType == null) {
 271                                 eventType = new LabeledIdentifier(element.typeIdentifier, element.classId, element.label,
 272                                                 element.description);
 273                         }
 274                         return eventType;
 275                 }
 276 
 277                 void init(LoaderContext context) throws InvalidJfrFileException, IOException {
 278                         if (context.hideExperimentals() && element.experimental) {
 279                                 eventSink = new NopEventSink();
 280                         } else {
 281                                 List<ValueField> fieldsList = new ArrayList<>();
 282                                 List<Integer> skipFields = new ArrayList<>();
 283                                 for (int i = 0; i < element.getFieldCount(); i++) {
 284                                         FieldElement fe = element.fields.get(i);
 285                                         String valueType = context.getValueInterpretation(element.typeIdentifier, fe.fieldIdentifier);
 286                                         IValueReader reader = createFieldReader(fe, valueType);
 287                                         String fieldLabel = buildLabel(fe.fieldIdentifier, fe);
 288                                         if (context.hideExperimentals() && fe.experimental) {
 289                                                 valueReaders.add(reader);
 290                                                 skipFields.add(i);
 291                                         } else if (reader instanceof StructReader) {
 292                                                 // Flattening of nested structs
 293                                                 ClassElement fieldType = getTypeEntry(fe.classId).element;
 294                                                 for (int j = 0; j < fieldType.getFieldCount(); j++) {
 295                                                         FieldElement nestedField = fieldType.fields.get(j);
 296                                                         String nestedId = fe.fieldIdentifier + ":" + nestedField.fieldIdentifier; //$NON-NLS-1$
 297                                                         String nestedValueType = context.getValueInterpretation(element.typeIdentifier, nestedId);
 298                                                         IValueReader nestedReader = createFieldReader(nestedField, nestedValueType);
 299                                                         valueReaders.add(nestedReader);
 300                                                         String nestedLabel = fieldLabel + " : " //$NON-NLS-1$
 301                                                                         + (nestedField.label == null ? nestedField.fieldIdentifier : nestedField.label);
 302                                                         fieldsList.add(new ValueField(nestedId, nestedLabel, nestedField.description,
 303                                                                         nestedReader.getContentType()));
 304                                                 }
 305                                         } else {
 306                                                 valueReaders.add(reader);
 307                                                 fieldsList.add(new ValueField(fe.fieldIdentifier, fieldLabel, fe.description,
 308                                                                 reader.getContentType()));
 309                                         }
 310                                 }
 311                                 String typeLabel = buildLabel(element.typeIdentifier, element);
 312                                 // FIXME: Consider making the category array into something else, like an event type metadata array?
 313                                 eventSink = context.getSinkFactory().create(element.typeIdentifier, typeLabel, element.category,
 314                                                 element.description, fieldsList);
 315                                 reusableStruct = new Object[valueReaders.size()];
 316                                 if (skipFields.size() > 0) {
 317                                         eventSink = new SkipFieldsEventSink(eventSink, skipFields, reusableStruct.length);
 318                                 }
 319                         }
 320                 }
 321         }
 322 
 323         private final FastAccessNumberMap<TypeEntry> otherTypes = new FastAccessNumberMap<>();
 324         private final FastAccessNumberMap<EventTypeEntry> eventTypes = new FastAccessNumberMap<>();
 325         private final ChunkStructure header;
 326 
 327         TypeManager(List<ClassElement> classList, LoaderContext context, ChunkStructure header)
 328                         throws InvalidJfrFileException, IOException {
 329                 this.header = header;
 330                 for (ClassElement ce : classList) {
 331                         if (ce.isEventType()) {
 332                                 eventTypes.put(ce.classId, new EventTypeEntry(ce));
 333                         } else {
 334                                 otherTypes.put(ce.classId, new TypeEntry(ce));
 335                         }
 336                 }
 337                 for (ClassElement ce : classList) {
 338                         resolveAnnotations(ce);
 339                         for (int i = 0; i < ce.getFieldCount(); i++) {
 340                                 resolveAnnotations(ce.fields.get(i));
 341                         }
 342                 }
 343 
 344                 for (EventTypeEntry ce : eventTypes) {
 345                         ce.init(context);
 346                 }
 347         }
 348 
 349         void readEvent(long typeId, IDataInput input) throws InvalidJfrFileException, IOException {
 350                 EventTypeEntry entry = eventTypes.get(typeId);
 351                 if (entry == null) {
 352                         throw new InvalidJfrFileException("Event type with id " + typeId + " was not declared"); //$NON-NLS-1$ //$NON-NLS-2$
 353                 }
 354                 entry.readEvent(input);
 355         }
 356 
 357         void readConstants(long typeId, IDataInput input, int constantCount) throws InvalidJfrFileException, IOException {
 358                 TypeEntry entry = getTypeEntry(typeId);
 359                 for (int j = 0; j < constantCount; j++) {
 360                         entry.readConstant(input);
 361                 }
 362         }
 363 
 364         void resolveConstants() throws InvalidJfrFileException {
 365                 for (TypeEntry classEntry : otherTypes) {
 366                         classEntry.resolveConstants();
 367                 }
 368         }
 369 
 370         private TypeEntry getTypeEntry(long typeId) throws InvalidJfrFileException {
 371                 TypeEntry entry = otherTypes.get(typeId);
 372                 if (entry == null) {
 373                         throw new InvalidJfrFileException("Class with id " + typeId + " was not declared"); //$NON-NLS-1$ //$NON-NLS-2$
 374                 }
 375                 return entry;
 376         }
 377 
 378         private void resolveAnnotations(AnnotatedElement ae) throws InvalidJfrFileException {
 379                 if (ae.annotations != null) {
 380                         for (AnnotationElement a : ae.annotations) {
 381                                 ClassElement annotationType = getTypeEntry(a.classId).element;
 382                                 ae.resolveAnnotation(annotationType.typeIdentifier, a.values);
 383                         }
 384                 }
 385         }
 386 
 387         private IValueReader createFieldReader(FieldElement f, String valueType) throws InvalidJfrFileException {
 388                 TypeEntry fieldType = getTypeEntry(f.classId);
 389                 String typeIdentifier = fieldType.element.typeIdentifier;
 390                 boolean isNumeric = PrimitiveReader.isNumeric(typeIdentifier);
 391                 IValueReader reader = fieldType.getReader();
 392                 if (f.ticksUnitKind == UnitLookup.TIMESPAN) {
 393                         reader = new QuantityReader(typeIdentifier, header.getTicksTimespanUnit(), f.unsigned);
 394                 } else if (f.ticksUnitKind == UnitLookup.TIMESTAMP) {
 395                         reader = new TicksTimestampReader(typeIdentifier, header, f.unsigned);
 396                 } else if (f.unit != null) {
 397                         reader = new QuantityReader(typeIdentifier, f.unit, f.unsigned);
 398                 } else if (isNumeric) {
 399                         if (JfrInternalConstants.TYPE_IDENTIFIER_VALUE_INTERPRETATION.equals(valueType)) {
 400                                 reader = new TypeIdentifierReader(typeIdentifier, f.unsigned);
 401                         } else {
 402                                 IUnit unit = UnitLookup.getUnitOrNull(valueType);
 403                                 /*
 404                                  * FIXME: Currently we convert all numbers to quantities. This might not be ideal,
 405                                  * for example for thread IDs. See multiple notes referring to this method in
 406                                  * StructTypes.
 407                                  */
 408                                 reader = new QuantityReader(typeIdentifier, unit == null ? UnitLookup.NUMBER_UNITY : unit, f.unsigned);
 409                         }
 410                 }
 411                 if (f.isStoredInPool()) {
 412                         if (isNumeric) {
 413                                 throw new InvalidJfrFileException("Numerics should not be put in constant pools"); //$NON-NLS-1$
 414                         }
 415                         reader = new PoolReader(fieldType.constants, reader.getContentType());
 416                 }
 417                 return f.isArray() ? new ArrayReader(reader) : reader;
 418         }
 419 
 420         private static String buildLabel(String id, AnnotatedElement element) {
 421                 String labelOrId = element.label == null ? id : element.label;
 422                 return element.experimental
 423                                 ? MessageFormat.format(Messages.getString(Messages.TypeManager_EXPERIMENTAL_TYPE), labelOrId)
 424                                 : labelOrId;
 425         }
 426 
 427         private class TypeIdentifierReader implements IValueReader {
 428                 private final String typeIdentifier;
 429                 private final boolean unsigned;
 430 
 431                 TypeIdentifierReader(String typeIdentifier, boolean unsigned) throws InvalidJfrFileException {
 432                         this.typeIdentifier = typeIdentifier;
 433                         this.unsigned = unsigned;
 434                 }
 435 
 436                 @Override
 437                 public Object read(IDataInput in, boolean allowUnresolvedReference)
 438                                 throws IOException, InvalidJfrFileException {
 439                         long typeId = PrimitiveReader.readLong(in, typeIdentifier, unsigned);
 440                         return eventTypes.get(typeId).getValueType();
 441                 }
 442 
 443                 @Override
 444                 public Object resolve(Object value) throws InvalidJfrFileException {
 445                         return value;
 446                 }
 447 
 448                 @Override
 449                 public void skip(IDataInput in) throws IOException, InvalidJfrFileException {
 450                         PrimitiveReader.readLong(in, typeIdentifier, unsigned);
 451                 }
 452 
 453                 @Override
 454                 public ContentType<?> getContentType() {
 455                         return UnitLookup.LABELED_IDENTIFIER;
 456                 }
 457         }
 458 }