1 /*
   2  * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package jdk.jfr.internal;
  27 
  28 import java.util.ArrayList;
  29 import java.util.Collection;
  30 import java.util.HashMap;
  31 import java.util.List;
  32 import java.util.Map;
  33 import java.util.Objects;
  34 
  35 import jdk.jfr.AnnotationElement;
  36 import jdk.jfr.Event;
  37 import jdk.jfr.SettingControl;
  38 import jdk.jfr.ValueDescriptor;
  39 
  40 /**
  41  * Internal data structure that describes a type,
  42  *
  43  * Used to create event types, value descriptor and annotations.
  44  *
  45  */
  46 public class Type implements Comparable<Type> {
  47     public static final String SUPER_TYPE_ANNOTATION = java.lang.annotation.Annotation.class.getName();
  48     public static final String SUPER_TYPE_SETTING = SettingControl.class.getName();
  49     public static final String SUPER_TYPE_EVENT = Event.class.getName();
  50     public static final String ORACLE_EVENT_PREFIX = "com.oracle.jdk.";
  51     public static final String ORACLE_TYPE_PREFIX = "com.oracle.jfr.types.";
  52 
  53     // Initialization of known types
  54     private final static Map<Type, Class<?>> knownTypes = new HashMap<>();
  55     static final Type BOOLEAN = register(boolean.class, new Type("boolean", null, 4));
  56     static final Type CHAR = register(char.class, new Type("char", null, 5));
  57     static final Type FLOAT = register(float.class, new Type("float", null, 6));
  58     static final Type DOUBLE = register(double.class, new Type("double", null, 7));
  59     static final Type BYTE = register(byte.class, new Type("byte", null, 8));
  60     static final Type SHORT = register(short.class, new Type("short", null, 9));
  61     static final Type INT = register(int.class, new Type("int", null, 10));
  62     static final Type LONG = register(long.class, new Type("long", null, 11));
  63     static final Type CLASS = register(Class.class, new Type("java.lang.Class", null, 20));
  64     static final Type STRING = register(String.class, new Type("java.lang.String", null, 21));
  65     static final Type THREAD = register(Thread.class, new Type("java.lang.Thread", null, 22));
  66     static final Type STACK_TRACE = register(null, new Type(ORACLE_TYPE_PREFIX + "StackTrace", null, 23));
  67 
  68     private final AnnotationConstruct annos = new AnnotationConstruct();
  69     private final String name;
  70     private final String superType;
  71     private final boolean constantPool;
  72     private final long id;
  73     private final ArrayList<ValueDescriptor> fields = new ArrayList<>();
  74     private Boolean simpleType; // calculated lazy
  75     private boolean remove = true;
  76     /**
  77      * Creates a type
  78      *
  79      * @param javaTypeName i.e "com.oracle.jfr.type.StackFrame"
  80      * @param superType i.e "com.oracle.jfr.Event" and "java.lang.Annotation"
  81      * @param id the class id that represents the class in the JVM
  82      *
  83      */
  84     public Type(String javaTypeName, String superType, long typeId) {
  85         this(javaTypeName, superType, typeId, false);
  86     }
  87 
  88     Type(String javaTypeName, String superType, long typeId, boolean contantPool) {
  89         this(javaTypeName, superType, typeId, contantPool, null);
  90     }
  91 
  92     Type(String javaTypeName, String superType, long typeId, boolean contantPool, Boolean simpleType) {
  93         Objects.requireNonNull(javaTypeName);
  94 
  95         if (!isValidJavaIdentifier(javaTypeName)) {
  96             throw new IllegalArgumentException(javaTypeName + " is not a valid Java identifier");
  97         }
  98         this.constantPool = contantPool;
  99         this.superType = superType;
 100         this.name = javaTypeName;
 101         this.id = typeId;
 102         this.simpleType = simpleType;
 103     }
 104 
 105     static boolean isDefinedByJVM(long id) {
 106         return id < JVM.RESERVED_CLASS_ID_LIMIT;
 107     }
 108 
 109     public static long getTypeId(Class<?> clazz) {
 110         Type type = Type.getKnownType(clazz);
 111         return type == null ? JVM.getJVM().getTypeId(clazz) : type.getId();
 112     }
 113 
 114     static Collection<Type> getKnownTypes() {
 115         return knownTypes.keySet();
 116     }
 117 
 118     public static boolean isValidJavaIdentifier(String identifier) {
 119         if (identifier.isEmpty()) {
 120             return false;
 121         }
 122         if (!Character.isJavaIdentifierStart(identifier.charAt(0))) {
 123             return false;
 124         }
 125         for (int i = 1; i < identifier.length(); i++) {
 126             char c = identifier.charAt(i);
 127             if (c != '.') {
 128                 if (!Character.isJavaIdentifierPart(c)) {
 129                     return false;
 130                 }
 131             }
 132         }
 133         return true;
 134     }
 135 
 136     public static boolean isValidJavaFieldType(String name) {
 137         for (Map.Entry<Type, Class<?>> entry : knownTypes.entrySet()) {
 138             Class<?> clazz = entry.getValue();
 139             if (clazz != null && name.equals(clazz.getName())) {
 140                 return true;
 141             }
 142         }
 143         return false;
 144     }
 145 
 146     public static Class<?> getKnownType(String className) {
 147         for (Map.Entry<Type, Class<?>> entry : knownTypes.entrySet()) {
 148             Class<?> clazz = entry.getValue();
 149             if (clazz != null) {
 150                 String knownClassName = clazz.getName();
 151                 if (className.equals(knownClassName)) {
 152                     return clazz;
 153                 }
 154             }
 155         }
 156         return null;
 157     }
 158 
 159     static boolean isKnownType(Class<?> type) {
 160         if (type.isPrimitive()) {
 161             return true;
 162         }
 163         if (type.equals(Class.class) || type.equals(Thread.class) || type.equals(String.class)) {
 164             return true;
 165         }
 166         return false;
 167     }
 168 
 169     public static Type getKnownType(Class<?> clazz) {
 170         for (Map.Entry<Type, Class<?>> entry : knownTypes.entrySet()) {
 171             if (clazz != null && clazz.equals(entry.getValue())) {
 172                 return entry.getKey();
 173             }
 174         }
 175         return null;
 176     }
 177 
 178     public String getName() {
 179         return name;
 180     }
 181 
 182     public String getLogName() {
 183        return getName() + "(" + getId() + ")";
 184     }
 185 
 186     public List<ValueDescriptor> getFields() {
 187         return fields;
 188     }
 189 
 190     public boolean isSimpleType() {
 191         if (simpleType == null) {
 192             simpleType = calculateSimpleType();
 193         }
 194         return simpleType.booleanValue();
 195     }
 196 
 197     private boolean calculateSimpleType() {
 198         if (fields.size() != 1) {
 199             return false;
 200         }
 201         // annotation, settings and event can never be simple types
 202         return superType == null;
 203     }
 204 
 205     public boolean isDefinedByJVM() {
 206         return id < JVM.RESERVED_CLASS_ID_LIMIT;
 207     }
 208 
 209     private static Type register(Class<?> clazz, Type type) {
 210         knownTypes.put(type, clazz);
 211         return type;
 212     }
 213 
 214     public void add(ValueDescriptor valueDescriptor) {
 215         Objects.requireNonNull(valueDescriptor);
 216         fields.add(valueDescriptor);
 217     }
 218 
 219     void trimFields() {
 220         fields.trimToSize();
 221     }
 222 
 223     void setAnnotations(List<AnnotationElement> annotations) {
 224         annos.setAnnotationElements(annotations);
 225     }
 226 
 227     public String getSuperType() {
 228         return superType;
 229     }
 230 
 231     public long getId() {
 232         return id;
 233     }
 234 
 235     public boolean isConstantPool() {
 236         return constantPool;
 237     }
 238 
 239     public String getLabel() {
 240         return annos.getLabel();
 241     }
 242 
 243     public List<AnnotationElement> getAnnotationElements() {
 244         return annos.getUnmodifiableAnnotationElements();
 245     }
 246 
 247     public <T> T getAnnotation(Class<? extends java.lang.annotation.Annotation> clazz) {
 248         return annos.getAnnotation(clazz);
 249     }
 250 
 251     public String getDescription() {
 252         return annos.getDescription();
 253     }
 254 
 255     @Override
 256     public int hashCode() {
 257         return Long.hashCode(id);
 258     }
 259 
 260     @Override
 261     public boolean equals(Object object) {
 262         if (object instanceof Type) {
 263             Type that = (Type) object;
 264             return that.id == this.id;
 265         }
 266         return false;
 267     }
 268 
 269     @Override
 270     public int compareTo(Type that) {
 271         return Long.compare(this.id, that.id);
 272     }
 273 
 274     void log(String action, LogTag logTag, LogLevel level) {
 275         if (logTag.shouldLog(level.level) && !isSimpleType()) {
 276             Logger.log(logTag, LogLevel.TRACE, action + " " + typeText() + " " + getLogName() + " {");
 277             for (ValueDescriptor v : getFields()) {
 278                 String array = v.isArray() ? "[]" : "";
 279                 Logger.log(logTag, LogLevel.TRACE, "  " + v.getTypeName() + array + " " + v.getName() + ";");
 280             }
 281             Logger.log(logTag, LogLevel.TRACE, "}");
 282         } else {
 283             if (logTag.shouldLog(LogLevel.INFO.level) && !isSimpleType()) {
 284                 Logger.log(logTag, LogLevel.INFO, action + " " + typeText() + " " + getLogName());
 285             }
 286         }
 287     }
 288 
 289     private String typeText() {
 290         if (this instanceof PlatformEventType) {
 291             return "event type";
 292         }
 293         if (Type.SUPER_TYPE_SETTING.equals(superType)) {
 294             return "setting type";
 295         }
 296         if (Type.SUPER_TYPE_ANNOTATION.equals(superType)) {
 297             return "annotation type";
 298         }
 299         return "type";
 300     }
 301 
 302     @Override
 303     public String toString() {
 304         StringBuilder sb = new StringBuilder();
 305         sb.append(getLogName());
 306         if (!getFields().isEmpty()) {
 307             sb.append(" {\n");
 308             for (ValueDescriptor td : getFields()) {
 309                 sb.append("  type=" + td.getTypeName() + "(" + td.getTypeId() + ") name=" + td.getName() + "\n");
 310             }
 311             sb.append("}\n");
 312         }
 313         return sb.toString();
 314     }
 315 
 316     public void setRemove(boolean remove) {
 317        this.remove = remove;
 318     }
 319 
 320     public boolean getRemove() {
 321         return remove;
 322     }
 323 }