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.cmd;
  27 
  28 import java.io.FileNotFoundException;
  29 import java.io.IOException;
  30 import java.io.PrintWriter;
  31 import java.nio.file.Path;
  32 import java.util.ArrayList;
  33 import java.util.Collections;
  34 import java.util.HashSet;
  35 import java.util.List;
  36 import java.util.StringJoiner;
  37 
  38 import jdk.jfr.AnnotationElement;
  39 import jdk.jfr.ValueDescriptor;
  40 import jdk.jfr.consumer.RecordedEvent;
  41 import jdk.jfr.consumer.RecordedObject;
  42 import jdk.jfr.consumer.RecordingFile;
  43 import jdk.jfr.internal.PrivateAccess;
  44 import jdk.jfr.internal.Type;
  45 import jdk.jfr.internal.consumer.ChunkHeader;
  46 import jdk.jfr.internal.consumer.RecordingInput;
  47 
  48 public final class PrettyWriter extends StructuredWriter {
  49 
  50     public PrettyWriter(PrintWriter destination) {
  51         super(destination);
  52     }
  53 
  54     void print(Path source) throws FileNotFoundException, IOException {
  55         try (RecordingInput input = new RecordingInput(source.toFile())) {
  56             HashSet<Type> typeSet = new HashSet<>();
  57             for (ChunkHeader ch = new ChunkHeader(input); !ch.isLastChunk(); ch = ch.nextHeader()) {
  58                 typeSet.addAll(ch.readMetadata().getTypes());
  59             }
  60             List<Type> types = new ArrayList<>(typeSet);
  61             Collections.sort(types, (c1, c2) -> Long.compare(c1.getId(), c2.getId()));
  62             for (Type t : types) {
  63                 printType(t);
  64             }
  65             flush();
  66         }
  67 
  68         try (RecordingFile es = new RecordingFile(source)) {
  69             while (es.hasMoreEvents()) {
  70                 print(es.readEvent());
  71                 flush();
  72             }
  73         }
  74         flush();
  75     }
  76 
  77     private void printType(Type t) throws IOException {
  78         print("// id: ");
  79         println(String.valueOf(t.getId()));
  80         int commentIndex = t.getName().length() + 10;
  81         String typeName = t.getName();
  82         int index = typeName.lastIndexOf(".");
  83         if (index != -1) {
  84             println("package " + typeName.substring(0, index) + ";");
  85         }
  86         printAnnotations(commentIndex, t.getAnnotationElements());
  87         print("class " + typeName.substring(index + 1));
  88         String superType = t.getSuperType();
  89         if (superType != null) {
  90             print(" extends " + superType);
  91         }
  92         println(" {");
  93         indent();
  94         for (ValueDescriptor v : t.getFields()) {
  95             printField(commentIndex, v);
  96         }
  97         retract();
  98         println("}");
  99         println();
 100     }
 101 
 102     private void printField(int commentIndex, ValueDescriptor v) throws IOException {
 103         println();
 104         printAnnotations(commentIndex, v.getAnnotationElements());
 105         printIndent();
 106         Type vType = PrivateAccess.getInstance().getType(v);
 107         if (Type.SUPER_TYPE_SETTING.equals(vType.getSuperType())) {
 108             print("static ");
 109         }
 110         print(makeSimpleType(v.getTypeName()));
 111         if (v.isArray()) {
 112             print("[]");
 113         }
 114         print(" ");
 115         print(v.getName());
 116         print(";");
 117         printCommentRef(commentIndex, v.getTypeId());
 118     }
 119 
 120     private void printCommentRef(int commentIndex, long typeId) throws IOException {
 121         int column = getColumn();
 122         if (column > commentIndex) {
 123             print("  ");
 124         } else {
 125             while (column < commentIndex) {
 126                 print(" ");
 127                 column++;
 128             }
 129         }
 130         println(" // id=" + typeId);
 131     }
 132 
 133     private void printAnnotations(int commentIndex, List<AnnotationElement> annotations) throws IOException {
 134         for (AnnotationElement a : annotations) {
 135             printIndent();
 136             print("@");
 137             print(makeSimpleType(a.getTypeName()));
 138             List<ValueDescriptor> vs = a.getValueDescriptors();
 139             if (!vs.isEmpty()) {
 140                 print("(");
 141                 printAnnotation(a);
 142                 print(")");
 143                 printCommentRef(commentIndex, a.getTypeId());
 144             } else {
 145                 println();
 146             }
 147         }
 148     }
 149 
 150     private void printAnnotation(AnnotationElement a) throws IOException {
 151         StringJoiner sj = new StringJoiner(", ");
 152         for (ValueDescriptor v : a.getValueDescriptors()) {
 153             StringBuilder sb = new StringBuilder();
 154             Object o = a.getValue(v.getName());
 155             if (o instanceof String) {
 156                 sb.append("\"");
 157                 sb.append((String) o);
 158                 sb.append("\"");
 159             } else {
 160                 sb.append(o);
 161             }
 162             sj.add(sb.toString());
 163         }
 164         print(sj.toString());
 165     }
 166 
 167     private String makeSimpleType(String typeName) {
 168         int index = typeName.lastIndexOf(".");
 169         return typeName.substring(index + 1);
 170     }
 171 
 172     public void print(RecordedEvent event) throws IOException {
 173         print(makeSimpleType(event.getEventType().getName()), " ");
 174         print((RecordedObject) event, "");
 175     }
 176 
 177     public void print(RecordedObject struct, String postFix) throws IOException {
 178         println("{");
 179         indent();
 180         for (ValueDescriptor v : struct.getFields()) {
 181             printIndent();
 182             print(v.getName(), " = ");
 183             printValue(struct.getValue(v.getName()), "");
 184         }
 185         retract();
 186         printIndent();
 187         println("}" + postFix);
 188     }
 189 
 190     private void printArray(Object[] array) throws IOException {
 191         println("[");
 192         indent();
 193         for (int i = 0; i < array.length; i++) {
 194             printIndent();
 195             printValue(array[i], i + 1 < array.length ? ", " : "");
 196         }
 197         retract();
 198         printIndent();
 199         println("]");
 200     }
 201 
 202     private void printValue(Object value, String postFix) throws IOException {
 203         if (value == null) {
 204             println("null" + postFix);
 205         } else if (value instanceof RecordedObject) {
 206             print((RecordedObject) value, postFix);
 207         } else if (value.getClass().isArray()) {
 208             printArray((Object[]) value);
 209         } else {
 210             String text = String.valueOf(value);
 211             if (value instanceof String) {
 212                 text = "\"" + text + "\"";
 213             }
 214             println(text);
 215         }
 216     }
 217 }