blob: 7258449f41fa46c8ffa654cd0108a920df9670bd [file] [log] [blame]
/*
* Copyright (c) 2016, 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 com.sun.tools.jdeprscan;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Utility class for pretty-printing various bits of API syntax.
*/
public class Pretty {
/**
* Converts deprecation information into an {@code @Deprecated} annotation.
* The output is minimized: an empty since string is omitted, a forRemoval
* value of false is omitted; and if both are omitted, the trailing parentheses
* are also omitted.
*
* @param since the since value
* @param forRemoval the forRemoval value
* @return string containing an annotation
*/
static String depr(String since, boolean forRemoval) {
String d = "@Deprecated";
if (since.isEmpty() && !forRemoval) {
return d;
}
StringBuilder sb = new StringBuilder(d).append('(');
if (!since.isEmpty()) {
sb.append("since=\"")
.append(since.replace("\"", "\\\""))
.append('"');
}
if (forRemoval) {
if (!since.isEmpty()) {
sb.append(", ");
}
sb.append("forRemoval=true");
}
sb.append(')');
return sb.toString();
}
/**
* Converts a slash-$ style name into a dot-separated name.
*
* @param n the input name
* @return the result name
*/
static String unslashify(String n) {
return n.replace("/", ".")
.replace("$", ".");
}
/**
* Converts a type descriptor to a readable string.
*
* @param desc the input descriptor
* @return the result string
*/
static String desc(String desc) {
return desc(desc, new int[] { 0 });
}
/**
* Converts one type descriptor to a readable string, starting
* from position {@code pos_inout[0]}, and updating it to the
* location following the descriptor just parsed. A type descriptor
* mostly corresponds to a FieldType in JVMS 4.3.2. It can be one of a
* BaseType (a single character denoting a primitive, plus void),
* an object type ("Lname;"), or an array type (one more more '[' followed
* by a base or object type).
*
* @param desc a string possibly containing several descriptors
* @param pos_inout on input, the start position; on return, the position
* following the just-parsed descriptor
* @return the result string
*/
static String desc(String desc, int[] pos_inout) {
int dims = 0;
int pos = pos_inout[0];
final int len = desc.length();
while (pos < len && desc.charAt(pos) == '[') {
pos++;
dims++;
}
String name;
if (pos >= len) {
return null;
}
char c = desc.charAt(pos++);
switch (c) {
case 'Z':
name = "boolean";
break;
case 'B':
name = "byte";
break;
case 'S':
name = "short";
break;
case 'C':
name = "char";
break;
case 'I':
name = "int";
break;
case 'J':
name = "long";
break;
case 'F':
name = "float";
break;
case 'D':
name = "double";
break;
case 'V':
name = "void";
break;
case 'L':
int semi = desc.indexOf(';', pos);
if (semi == -1) {
return null;
}
name = unslashify(desc.substring(pos, semi));
pos = semi + 1;
break;
default:
return null;
}
StringBuilder sb = new StringBuilder(name);
for (int i = 0; i < dims; i++) {
sb.append("[]");
}
pos_inout[0] = pos;
return sb.toString();
}
/**
* Converts a series of type descriptors into a comma-separated,
* readable string. This is used for the parameter types of a
* method descriptor.
*
* @param types the parameter types
* @return the readable string
*/
static String parms(String types) {
int[] pos = new int[] { 0 };
StringBuilder sb = new StringBuilder();
boolean first = true;
String t;
while ((t = desc(types, pos)) != null) {
if (first) {
first = false;
} else {
sb.append(',');
}
sb.append(t);
}
return sb.toString();
}
/**
* Pattern for matching a method descriptor. Match results can
* be retrieved from named capture groups as follows: "name(params)return".
*/
static final Pattern DESC_PAT = Pattern.compile("(?<name>.*)\\((?<args>.*)\\)(?<return>.*)");
/**
* Pretty-prints the data contained in the given DeprData object.
*
* @param dd the deprecation data object
* @return the formatted string
*/
public static String print(DeprData dd) {
StringBuilder sb = new StringBuilder();
sb.append(depr(dd.since, dd.forRemoval))
.append(' ');
switch (dd.kind) {
case ANNOTATION_TYPE:
sb.append("@interface ");
sb.append(unslashify(dd.typeName));
break;
case CLASS:
sb.append("class ");
sb.append(unslashify(dd.typeName));
break;
case ENUM:
sb.append("enum ");
sb.append(unslashify(dd.typeName));
break;
case INTERFACE:
sb.append("interface ");
sb.append(unslashify(dd.typeName));
break;
case ENUM_CONSTANT:
case FIELD:
sb.append(unslashify(dd.typeName))
.append('.')
.append(dd.nameSig);
break;
case CONSTRUCTOR:
Matcher cons = DESC_PAT.matcher(dd.nameSig);
sb.append(unslashify(dd.typeName));
if (cons.matches()) {
sb.append('(')
.append(parms(cons.group("args")))
.append(')');
} else {
sb.append('.')
.append(dd.nameSig);
}
break;
case METHOD:
Matcher meth = DESC_PAT.matcher(dd.nameSig);
if (meth.matches()) {
sb.append(desc(meth.group("return")))
.append(' ')
.append(unslashify(dd.typeName))
.append('.')
.append(meth.group("name"))
.append('(')
.append(parms(meth.group("args")))
.append(')');
} else {
sb.append(unslashify(dd.typeName))
.append('.')
.append(dd.nameSig);
}
break;
}
return sb.toString();
}
}