1 /*
   2  * Copyright (c) 2016, 2018, 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.Collections;
  31 import java.util.HashMap;
  32 import java.util.List;
  33 import java.util.Map;
  34 import java.util.Objects;
  35 
  36 import jdk.jfr.AnnotationElement;
  37 import jdk.jfr.Event;
  38 import jdk.jfr.SettingControl;
  39 import jdk.jfr.ValueDescriptor;
  40 
  41 /**
  42  * Internal data structure that describes a type,
  43  *
  44  * Used to create event types, value descriptor and annotations.
  45  *
  46  */
  47 public class Type implements Comparable<Type> {
  48     public static final String SUPER_TYPE_ANNOTATION = java.lang.annotation.Annotation.class.getName();
  49     public static final String SUPER_TYPE_SETTING = SettingControl.class.getName();
  50     public static final String SUPER_TYPE_EVENT = Event.class.getName();
  51     public static final String EVENT_NAME_PREFIX = "jdk.";
  52     public static final String TYPES_PREFIX = "jdk.types.";
  53     public static final String SETTINGS_PREFIX = "jdk.settings.";
  54 
  55 
  56     // Initialization of known types
  57     private final static Map<Type, Class<?>> knownTypes = new HashMap<>();
  58     static final Type BOOLEAN = register(boolean.class, new Type("boolean", null, 4));
  59     static final Type CHAR = register(char.class, new Type("char", null, 5));
  60     static final Type FLOAT = register(float.class, new Type("float", null, 6));
  61     static final Type DOUBLE = register(double.class, new Type("double", null, 7));
  62     static final Type BYTE = register(byte.class, new Type("byte", null, 8));
  63     static final Type SHORT = register(short.class, new Type("short", null, 9));
  64     static final Type INT = register(int.class, new Type("int", null, 10));
  65     static final Type LONG = register(long.class, new Type("long", null, 11));
  66     static final Type CLASS = register(Class.class, new Type("java.lang.Class", null, 20));
  67     static final Type STRING = register(String.class, new Type("java.lang.String", null, 21));
  68     static final Type THREAD = register(Thread.class, new Type("java.lang.Thread", null, 22));
  69     static final Type STACK_TRACE = register(null, new Type(TYPES_PREFIX + "StackTrace", null, 23));
  70 
  71     private final AnnotationConstruct annos = new AnnotationConstruct();
  72     private final String name;
  73     private final String superType;
  74     private final boolean constantPool;
  75     private final long id;
  76     private List<ValueDescriptor> fields = new ArrayList<>();
  77     private Boolean simpleType; // calculated lazy
  78     private boolean remove = true;
  79     /**
  80      * Creates a type
  81      *
  82      * @param javaTypeName i.e "java.lang.String"
  83      * @param superType i.e "java.lang.Annotation"
  84      * @param id the class id that represents the class in the JVM
  85      *
  86      */
  87     public Type(String javaTypeName, String superType, long typeId) {
  88         this(javaTypeName, superType, typeId, false);
  89     }
  90 
  91     Type(String javaTypeName, String superType, long typeId, boolean contantPool) {
  92         this(javaTypeName, superType, typeId, contantPool, null);
  93     }
  94 
  95     Type(String javaTypeName, String superType, long typeId, boolean contantPool, Boolean simpleType) {
  96         Objects.requireNonNull(javaTypeName);
  97 
  98         if (!isValidJavaIdentifier(javaTypeName)) {
  99             throw new IllegalArgumentException(javaTypeName + " is not a valid Java identifier");
 100         }
 101         this.constantPool = contantPool;
 102         this.superType = superType;
 103         this.name = javaTypeName;
 104         this.id = typeId;
 105         this.simpleType = simpleType;
 106     }
 107 
 108     static boolean isDefinedByJVM(long id) {
 109         return id < JVM.RESERVED_CLASS_ID_LIMIT;
 110     }
 111 
 112     public static long getTypeId(Class<?> clazz) {
 113         Type type = Type.getKnownType(clazz);
 114         return type == null ? JVM.getJVM().getTypeId(clazz) : type.getId();
 115     }
 116 
 117     static Collection<Type> getKnownTypes() {
 118         return knownTypes.keySet();
 119     }
 120 
 121     public static boolean isValidJavaIdentifier(String identifier) {
 122         if (identifier.isEmpty()) {
 123             return false;
 124         }
 125         if (!Character.isJavaIdentifierStart(identifier.charAt(0))) {
 126             return false;
 127         }
 128         for (int i = 1; i < identifier.length(); i++) {
 129             char c = identifier.charAt(i);
 130             if (c != '.') {
 131                 if (!Character.isJavaIdentifierPart(c)) {
 132                     return false;
 133                 }
 134             }
 135         }
 136         return true;
 137     }
 138 
 139     public static boolean isValidJavaFieldType(String name) {
 140         for (Map.Entry<Type, Class<?>> entry : knownTypes.entrySet()) {
 141             Class<?> clazz = entry.getValue();
 142             if (clazz != null && name.equals(clazz.getName())) {
 143                 return true;
 144             }
 145         }
 146         return false;
 147     }
 148 
 149     public static Type getKnownType(String typeName) {
 150         for (Type type : knownTypes.keySet()) {
 151             if (type.getName().equals(typeName)) {
 152                 return type;
 153             }
 154         }
 155         return null;
 156     }
 157 
 158     static boolean isKnownType(Class<?> type) {
 159         if (type.isPrimitive()) {
 160             return true;
 161         }
 162         if (type.equals(Class.class) || type.equals(Thread.class) || type.equals(String.class)) {
 163             return true;
 164         }
 165         return false;
 166     }
 167 
 168     public static Type getKnownType(Class<?> clazz) {
 169         for (Map.Entry<Type, Class<?>> entry : knownTypes.entrySet()) {
 170             if (clazz != null && clazz.equals(entry.getValue())) {
 171                 return entry.getKey();
 172             }
 173         }
 174         return null;
 175     }
 176 
 177     public String getName() {
 178         return name;
 179     }
 180 
 181     public String getLogName() {
 182        return getName() + "(" + getId() + ")";
 183     }
 184 
 185     public List<ValueDescriptor> getFields() {
 186         if (fields instanceof ArrayList) {
 187             ((ArrayList<ValueDescriptor>) fields).trimToSize();
 188             fields = Collections.unmodifiableList(fields);
 189         }
 190         return fields;
 191     }
 192 
 193     public boolean isSimpleType() {
 194         if (simpleType == null) {
 195             simpleType = calculateSimpleType();
 196         }
 197         return simpleType.booleanValue();
 198     }
 199 
 200     private boolean calculateSimpleType() {
 201         if (fields.size() != 1) {
 202             return false;
 203         }
 204         // annotation, settings and event can never be simple types
 205         return superType == null;
 206     }
 207 
 208     public boolean isDefinedByJVM() {
 209         return id < JVM.RESERVED_CLASS_ID_LIMIT;
 210     }
 211 
 212     private static Type register(Class<?> clazz, Type type) {
 213         knownTypes.put(type, clazz);
 214         return type;
 215     }
 216 
 217     public void add(ValueDescriptor valueDescriptor) {
 218         Objects.requireNonNull(valueDescriptor);
 219         fields.add(valueDescriptor);
 220     }
 221 
 222     void trimFields() {
 223         getFields();
 224     }
 225 
 226     void setAnnotations(List<AnnotationElement> annotations) {
 227         annos.setAnnotationElements(annotations);
 228     }
 229 
 230     public String getSuperType() {
 231         return superType;
 232     }
 233 
 234     public long getId() {
 235         return id;
 236     }
 237 
 238     public boolean isConstantPool() {
 239         return constantPool;
 240     }
 241 
 242     public String getLabel() {
 243         return annos.getLabel();
 244     }
 245 
 246     public List<AnnotationElement> getAnnotationElements() {
 247         return annos.getUnmodifiableAnnotationElements();
 248     }
 249 
 250     public <T> T getAnnotation(Class<? extends java.lang.annotation.Annotation> clazz) {
 251         return annos.getAnnotation(clazz);
 252     }
 253 
 254     public String getDescription() {
 255         return annos.getDescription();
 256     }
 257 
 258     @Override
 259     public int hashCode() {
 260         return Long.hashCode(id);
 261     }
 262 
 263     @Override
 264     public boolean equals(Object object) {
 265         if (object instanceof Type) {
 266             Type that = (Type) object;
 267             return that.id == this.id;
 268         }
 269         return false;
 270     }
 271 
 272     @Override
 273     public int compareTo(Type that) {
 274         return Long.compare(this.id, that.id);
 275     }
 276 
 277     void log(String action, LogTag logTag, LogLevel level) {
 278         if (Logger.shouldLog(logTag, level) && !isSimpleType()) {
 279             Logger.log(logTag, LogLevel.TRACE, action + " " + typeText() + " " + getLogName() + " {");
 280             for (ValueDescriptor v : getFields()) {
 281                 String array = v.isArray() ? "[]" : "";
 282                 Logger.log(logTag, LogLevel.TRACE, "  " + v.getTypeName() + array + " " + v.getName() + ";");
 283             }
 284             Logger.log(logTag, LogLevel.TRACE, "}");
 285         } else {
 286             if (Logger.shouldLog(logTag, LogLevel.INFO) && !isSimpleType()) {
 287                 Logger.log(logTag, LogLevel.INFO, action + " " + typeText() + " " + getLogName());
 288             }
 289         }
 290     }
 291 
 292     private String typeText() {
 293         if (this instanceof PlatformEventType) {
 294             return "event type";
 295         }
 296         if (Type.SUPER_TYPE_SETTING.equals(superType)) {
 297             return "setting type";
 298         }
 299         if (Type.SUPER_TYPE_ANNOTATION.equals(superType)) {
 300             return "annotation type";
 301         }
 302         return "type";
 303     }
 304 
 305     @Override
 306     public String toString() {
 307         StringBuilder sb = new StringBuilder();
 308         sb.append(getLogName());
 309         if (!getFields().isEmpty()) {
 310             sb.append(" {\n");
 311             for (ValueDescriptor td : getFields()) {
 312                 sb.append("  type=" + td.getTypeName() + "(" + td.getTypeId() + ") name=" + td.getName() + "\n");
 313             }
 314             sb.append("}\n");
 315         }
 316         return sb.toString();
 317     }
 318 
 319     public void setRemove(boolean remove) {
 320        this.remove = remove;
 321     }
 322 
 323     public boolean getRemove() {
 324         return remove;
 325     }
 326 }