| /* |
| * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package jdk.jfr.internal.consumer; |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import jdk.jfr.EventType; |
| import jdk.jfr.ValueDescriptor; |
| import jdk.jfr.internal.LongMap; |
| import jdk.jfr.internal.MetadataDescriptor; |
| import jdk.jfr.internal.PrivateAccess; |
| import jdk.jfr.internal.Type; |
| import jdk.jfr.internal.consumer.Parser; |
| import jdk.jfr.internal.consumer.RecordingInput; |
| |
| /** |
| * Class that create parsers suitable for reading events and constant pools |
| */ |
| final class ParserFactory { |
| private final LongMap<Parser> parsers = new LongMap<>(); |
| private final TimeConverter timeConverter; |
| private final LongMap<Type> types = new LongMap<>(); |
| private final LongMap<ConstantLookup> constantLookups; |
| |
| public ParserFactory(MetadataDescriptor metadata, LongMap<ConstantLookup> constantLookups, TimeConverter timeConverter) throws IOException { |
| this.constantLookups = constantLookups; |
| this.timeConverter = timeConverter; |
| for (Type t : metadata.getTypes()) { |
| types.put(t.getId(), t); |
| } |
| // Add to separate list |
| // so createCompositeParser can throw |
| // IOException outside lambda |
| List<Type> typeList = new ArrayList<>(); |
| types.forEach(typeList::add); |
| for (Type t : typeList) { |
| if (!t.getFields().isEmpty()) { // Avoid primitives |
| CompositeParser cp = createCompositeParser(t, false); |
| if (t.isSimpleType()) { // Reduce to nested parser |
| parsers.put(t.getId(), cp.parsers[0]); |
| } |
| } |
| } |
| // Override event types with event parsers |
| for (EventType t : metadata.getEventTypes()) { |
| parsers.put(t.getId(), createEventParser(t)); |
| } |
| } |
| |
| public LongMap<Parser> getParsers() { |
| return parsers; |
| } |
| |
| public LongMap<Type> getTypeMap() { |
| return types; |
| } |
| |
| private EventParser createEventParser(EventType eventType) throws IOException { |
| List<Parser> parsers = new ArrayList<Parser>(); |
| for (ValueDescriptor f : eventType.getFields()) { |
| parsers.add(createParser(f, true)); |
| } |
| return new EventParser(timeConverter, eventType, parsers.toArray(new Parser[0])); |
| } |
| |
| private Parser createParser(ValueDescriptor v, boolean event) throws IOException { |
| boolean constantPool = PrivateAccess.getInstance().isConstantPool(v); |
| if (v.isArray()) { |
| Type valueType = PrivateAccess.getInstance().getType(v); |
| ValueDescriptor element = PrivateAccess.getInstance().newValueDescriptor(v.getName(), valueType, v.getAnnotationElements(), 0, constantPool, null); |
| return new ArrayParser(createParser(element, event)); |
| } |
| long id = v.getTypeId(); |
| Type type = types.get(id); |
| if (type == null) { |
| throw new IOException("Type '" + v.getTypeName() + "' is not defined"); |
| } |
| if (constantPool) { |
| ConstantLookup lookup = constantLookups.get(id); |
| if (lookup == null) { |
| ConstantMap pool = new ConstantMap(ObjectFactory.create(type, timeConverter), type.getName()); |
| lookup = new ConstantLookup(pool, type); |
| constantLookups.put(id, lookup); |
| } |
| if (event) { |
| return new EventValueConstantParser(lookup); |
| } |
| return new ConstantValueParser(lookup); |
| } |
| Parser parser = parsers.get(id); |
| if (parser == null) { |
| if (!v.getFields().isEmpty()) { |
| return createCompositeParser(type, event); |
| } else { |
| return registerParserType(type, createPrimitiveParser(type, constantPool)); |
| } |
| } |
| return parser; |
| } |
| |
| private Parser createPrimitiveParser(Type type, boolean event) throws IOException { |
| switch (type.getName()) { |
| case "int": |
| return new IntegerParser(); |
| case "long": |
| return new LongParser(); |
| case "float": |
| return new FloatParser(); |
| case "double": |
| return new DoubleParser(); |
| case "char": |
| return new CharacterParser(); |
| case "boolean": |
| return new BooleanParser(); |
| case "short": |
| return new ShortParser(); |
| case "byte": |
| return new ByteParser(); |
| case "java.lang.String": |
| ConstantMap pool = new ConstantMap(ObjectFactory.create(type, timeConverter), type.getName()); |
| ConstantLookup lookup = new ConstantLookup(pool, type); |
| constantLookups.put(type.getId(), lookup); |
| return new StringParser(lookup, event); |
| default: |
| throw new IOException("Unknown primitive type " + type.getName()); |
| } |
| } |
| |
| private Parser registerParserType(Type t, Parser parser) { |
| Parser p = parsers.get(t.getId()); |
| // check if parser exists (known type) |
| if (p != null) { |
| return p; |
| } |
| parsers.put(t.getId(), parser); |
| return parser; |
| } |
| |
| private CompositeParser createCompositeParser(Type type, boolean event) throws IOException { |
| List<ValueDescriptor> vds = type.getFields(); |
| Parser[] parsers = new Parser[vds.size()]; |
| CompositeParser composite = new CompositeParser(parsers); |
| // need to pre-register so recursive types can be handled |
| registerParserType(type, composite); |
| |
| int index = 0; |
| for (ValueDescriptor vd : vds) { |
| parsers[index++] = createParser(vd, event); |
| } |
| return composite; |
| } |
| |
| private static final class BooleanParser extends Parser { |
| @Override |
| public Object parse(RecordingInput input) throws IOException { |
| return input.readBoolean() ? Boolean.TRUE : Boolean.FALSE; |
| } |
| |
| @Override |
| public void skip(RecordingInput input) throws IOException { |
| input.skipBytes(1); |
| } |
| } |
| |
| private static final class ByteParser extends Parser { |
| @Override |
| public Object parse(RecordingInput input) throws IOException { |
| return Byte.valueOf(input.readByte()); |
| } |
| |
| @Override |
| public void skip(RecordingInput input) throws IOException { |
| input.skipBytes(1); |
| } |
| } |
| |
| private static final class LongParser extends Parser { |
| private Object lastLongObject = Long.valueOf(0); |
| private long last = 0; |
| |
| @Override |
| public Object parse(RecordingInput input) throws IOException { |
| long l = input.readLong(); |
| if (l == last) { |
| return lastLongObject; |
| } |
| last = l; |
| lastLongObject = Long.valueOf(l); |
| return lastLongObject; |
| } |
| |
| @Override |
| public void skip(RecordingInput input) throws IOException { |
| input.readLong(); |
| } |
| } |
| |
| private static final class IntegerParser extends Parser { |
| private Integer lastIntegergObject = Integer.valueOf(0); |
| private int last = 0; |
| |
| @Override |
| public Object parse(RecordingInput input) throws IOException { |
| int i = input.readInt(); |
| if (i != last) { |
| last = i; |
| lastIntegergObject = Integer.valueOf(i); |
| } |
| return lastIntegergObject; |
| } |
| |
| @Override |
| public void skip(RecordingInput input) throws IOException { |
| input.readInt(); |
| } |
| } |
| |
| private static final class ShortParser extends Parser { |
| @Override |
| public Object parse(RecordingInput input) throws IOException { |
| return Short.valueOf(input.readShort()); |
| } |
| |
| @Override |
| public void skip(RecordingInput input) throws IOException { |
| input.readShort(); |
| } |
| } |
| |
| private static final class CharacterParser extends Parser { |
| @Override |
| public Object parse(RecordingInput input) throws IOException { |
| return Character.valueOf(input.readChar()); |
| } |
| |
| @Override |
| public void skip(RecordingInput input) throws IOException { |
| input.readChar(); |
| } |
| } |
| |
| private static final class FloatParser extends Parser { |
| @Override |
| public Object parse(RecordingInput input) throws IOException { |
| return Float.valueOf(input.readFloat()); |
| } |
| |
| @Override |
| public void skip(RecordingInput input) throws IOException { |
| input.skipBytes(Float.SIZE); |
| } |
| } |
| |
| private static final class DoubleParser extends Parser { |
| @Override |
| public Object parse(RecordingInput input) throws IOException { |
| return Double.valueOf(input.readDouble()); |
| } |
| |
| @Override |
| public void skip(RecordingInput input) throws IOException { |
| input.skipBytes(Double.SIZE); |
| } |
| } |
| |
| private final static class ArrayParser extends Parser { |
| private final Parser elementParser; |
| |
| public ArrayParser(Parser elementParser) { |
| this.elementParser = elementParser; |
| } |
| |
| @Override |
| public Object parse(RecordingInput input) throws IOException { |
| final int size = input.readInt(); |
| final Object[] array = new Object[size]; |
| for (int i = 0; i < size; i++) { |
| array[i] = elementParser.parse(input); |
| } |
| return array; |
| } |
| |
| @Override |
| public void skip(RecordingInput input) throws IOException { |
| final int size = input.readInt(); |
| for (int i = 0; i < size; i++) { |
| elementParser.skip(input); |
| } |
| } |
| } |
| |
| private final static class CompositeParser extends Parser { |
| private final Parser[] parsers; |
| |
| public CompositeParser(Parser[] valueParsers) { |
| this.parsers = valueParsers; |
| } |
| |
| @Override |
| public Object parse(RecordingInput input) throws IOException { |
| final Object[] values = new Object[parsers.length]; |
| for (int i = 0; i < values.length; i++) { |
| values[i] = parsers[i].parse(input); |
| } |
| return values; |
| } |
| |
| @Override |
| public void skip(RecordingInput input) throws IOException { |
| for (int i = 0; i < parsers.length; i++) { |
| parsers[i].skip(input); |
| } |
| } |
| } |
| |
| private static final class EventValueConstantParser extends Parser { |
| private final ConstantLookup lookup; |
| private Object lastValue = 0; |
| private long lastKey = -1; |
| EventValueConstantParser(ConstantLookup lookup) { |
| this.lookup = lookup; |
| } |
| |
| @Override |
| public Object parse(RecordingInput input) throws IOException { |
| long key = input.readLong(); |
| if (key == lastKey) { |
| return lastValue; |
| } |
| lastKey = key; |
| lastValue = lookup.getCurrentResolved(key); |
| return lastValue; |
| } |
| |
| @Override |
| public void skip(RecordingInput input) throws IOException { |
| input.readLong(); |
| } |
| } |
| |
| private static final class ConstantValueParser extends Parser { |
| private final ConstantLookup lookup; |
| ConstantValueParser(ConstantLookup lookup) { |
| this.lookup = lookup; |
| } |
| |
| @Override |
| public Object parse(RecordingInput input) throws IOException { |
| return lookup.getCurrent(input.readLong()); |
| } |
| |
| @Override |
| public void skip(RecordingInput input) throws IOException { |
| input.readLong(); |
| } |
| } |
| } |