diff -r 2c3cc4b01880 -r c16ac7a2eba4 src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ParserFactory.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ParserFactory.java Wed Oct 30 19:43:52 2019 +0100 @@ -0,0 +1,382 @@ +/* + * 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 parsers = new LongMap<>(); + private final TimeConverter timeConverter; + private final LongMap types = new LongMap<>(); + private final LongMap constantLookups; + + public ParserFactory(MetadataDescriptor metadata, LongMap 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 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 getParsers() { + return parsers; + } + + public LongMap getTypeMap() { + return types; + } + + private EventParser createEventParser(EventType eventType) throws IOException { + List parsers = new ArrayList(); + 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 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(); + } + } +}