--- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/consumer/ParserFactory.java 2019-02-08 18:32:25.570531148 +0300 @@ -0,0 +1,303 @@ +/* + * Copyright (c) 2016, 2018, 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.consumer; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import jdk.jfr.EventType; +import jdk.jfr.ValueDescriptor; +import jdk.jfr.internal.MetadataDescriptor; +import jdk.jfr.internal.PrivateAccess; +import jdk.jfr.internal.Type; +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 constantPools; + + public ParserFactory(MetadataDescriptor metadata, TimeConverter timeConverter) throws IOException { + this.constantPools = new LongMap<>(); + this.timeConverter = timeConverter; + for (Type t : metadata.getTypes()) { + types.put(t.getId(), t); + } + for (Type t : types) { + if (!t.getFields().isEmpty()) { // Avoid primitives + CompositeParser cp = createCompositeParser(t); + 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 getConstantPools() { + return constantPools; + } + + 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)); + } + return new EventParser(timeConverter, eventType, parsers.toArray(new Parser[0])); + } + + private Parser createParser(ValueDescriptor v) 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)); + } + long id = v.getTypeId(); + Type type = types.get(id); + if (type == null) { + throw new IOException("Type '" + v.getTypeName() + "' is not defined"); + } + if (constantPool) { + ConstantMap pool = constantPools.get(id); + if (pool == null) { + pool = new ConstantMap(ObjectFactory.create(type, timeConverter), type.getName()); + constantPools.put(id, pool); + } + return new ConstantMapValueParser(pool); + } + Parser parser = parsers.get(id); + if (parser == null) { + if (!v.getFields().isEmpty()) { + return createCompositeParser(type); + } else { + return registerParserType(type, createPrimitiveParser(type)); + } + } + return parser; + } + + private Parser createPrimitiveParser(Type type) 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()); + constantPools.put(type.getId(), pool); + return new StringParser(pool); + 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) 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); + } + return composite; + } + + private static final class BooleanParser extends Parser { + @Override + public Object parse(RecordingInput input) throws IOException { + return input.readBoolean() ? Boolean.TRUE : Boolean.FALSE; + } + } + + private static final class ByteParser extends Parser { + @Override + public Object parse(RecordingInput input) throws IOException { + return Byte.valueOf(input.readByte()); + } + } + + private static final class LongParser extends Parser { + @Override + public Object parse(RecordingInput input) throws IOException { + return Long.valueOf(input.readLong()); + } + } + + private static final class IntegerParser extends Parser { + @Override + public Object parse(RecordingInput input) throws IOException { + return Integer.valueOf(input.readInt()); + } + } + + private static final class ShortParser extends Parser { + @Override + public Object parse(RecordingInput input) throws IOException { + return Short.valueOf(input.readShort()); + } + } + + private static final class CharacterParser extends Parser { + @Override + public Object parse(RecordingInput input) throws IOException { + return Character.valueOf(input.readChar()); + } + } + + private static final class FloatParser extends Parser { + @Override + public Object parse(RecordingInput input) throws IOException { + return Float.valueOf(input.readFloat()); + } + } + + private static final class DoubleParser extends Parser { + @Override + public Object parse(RecordingInput input) throws IOException { + return Double.valueOf(input.readDouble()); + } + } + + private static final class StringParser extends Parser { + private final ConstantMap stringConstantMap; + private String last; + + StringParser(ConstantMap stringConstantMap) { + this.stringConstantMap = stringConstantMap; + } + + @Override + public Object parse(RecordingInput input) throws IOException { + String s = parseEncodedString(input); + if (!Objects.equals(s, last)) { + last = s; + } + return last; + } + + private String parseEncodedString(RecordingInput input) throws IOException { + byte encoding = input.readByte(); + if (encoding == RecordingInput.STRING_ENCODING_CONSTANT_POOL) { + long id = input.readLong(); + return (String) stringConstantMap.get(id); + } else { + return input.readEncodedString(encoding); + } + } + } + + 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; + } + } + + 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; + } + } + + private static final class ConstantMapValueParser extends Parser { + private final ConstantMap pool; + + ConstantMapValueParser(ConstantMap pool) { + this.pool = pool; + } + + @Override + public Object parse(RecordingInput input) throws IOException { + return pool.get(input.readLong()); + } + } +}