1/* 2 * Copyright (c) 2016, 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 26package com.sun.tools.jdeprscan; 27 28import java.util.regex.Matcher; 29import java.util.regex.Pattern; 30 31/** 32 * Utility class for pretty-printing various bits of API syntax. 33 */ 34public class Pretty { 35 /** 36 * Converts deprecation information into an {@code @Deprecated} annotation. 37 * The output is minimized: an empty since string is omitted, a forRemoval 38 * value of false is omitted; and if both are omitted, the trailing parentheses 39 * are also omitted. 40 * 41 * @param since the since value 42 * @param forRemoval the forRemoval value 43 * @return string containing an annotation 44 */ 45 static String depr(String since, boolean forRemoval) { 46 String d = "@Deprecated"; 47 48 if (since.isEmpty() && !forRemoval) { 49 return d; 50 } 51 52 StringBuilder sb = new StringBuilder(d).append('('); 53 54 if (!since.isEmpty()) { 55 sb.append("since=\"") 56 .append(since.replace("\"", "\\\"")) 57 .append('"'); 58 } 59 60 if (forRemoval) { 61 if (!since.isEmpty()) { 62 sb.append(", "); 63 } 64 sb.append("forRemoval=true"); 65 } 66 67 sb.append(')'); 68 69 return sb.toString(); 70 } 71 72 /** 73 * Converts a slash-$ style name into a dot-separated name. 74 * 75 * @param n the input name 76 * @return the result name 77 */ 78 static String unslashify(String n) { 79 return n.replace("/", ".") 80 .replace("$", "."); 81 } 82 83 /** 84 * Converts a type descriptor to a readable string. 85 * 86 * @param desc the input descriptor 87 * @return the result string 88 */ 89 static String desc(String desc) { 90 return desc(desc, new int[] { 0 }); 91 } 92 93 /** 94 * Converts one type descriptor to a readable string, starting 95 * from position {@code pos_inout[0]}, and updating it to the 96 * location following the descriptor just parsed. A type descriptor 97 * mostly corresponds to a FieldType in JVMS 4.3.2. It can be one of a 98 * BaseType (a single character denoting a primitive, plus void), 99 * an object type ("Lname;"), or an array type (one more more '[' followed 100 * by a base or object type). 101 * 102 * @param desc a string possibly containing several descriptors 103 * @param pos_inout on input, the start position; on return, the position 104 * following the just-parsed descriptor 105 * @return the result string 106 */ 107 static String desc(String desc, int[] pos_inout) { 108 int dims = 0; 109 int pos = pos_inout[0]; 110 final int len = desc.length(); 111 112 while (pos < len && desc.charAt(pos) == '[') { 113 pos++; 114 dims++; 115 } 116 117 String name; 118 119 if (pos >= len) { 120 return null; 121 } 122 123 char c = desc.charAt(pos++); 124 switch (c) { 125 case 'Z': 126 name = "boolean"; 127 break; 128 case 'B': 129 name = "byte"; 130 break; 131 case 'S': 132 name = "short"; 133 break; 134 case 'C': 135 name = "char"; 136 break; 137 case 'I': 138 name = "int"; 139 break; 140 case 'J': 141 name = "long"; 142 break; 143 case 'F': 144 name = "float"; 145 break; 146 case 'D': 147 name = "double"; 148 break; 149 case 'V': 150 name = "void"; 151 break; 152 case 'L': 153 int semi = desc.indexOf(';', pos); 154 if (semi == -1) { 155 return null; 156 } 157 name = unslashify(desc.substring(pos, semi)); 158 pos = semi + 1; 159 break; 160 default: 161 return null; 162 } 163 164 StringBuilder sb = new StringBuilder(name); 165 for (int i = 0; i < dims; i++) { 166 sb.append("[]"); 167 } 168 pos_inout[0] = pos; 169 return sb.toString(); 170 } 171 172 /** 173 * Converts a series of type descriptors into a comma-separated, 174 * readable string. This is used for the parameter types of a 175 * method descriptor. 176 * 177 * @param types the parameter types 178 * @return the readable string 179 */ 180 static String parms(String types) { 181 int[] pos = new int[] { 0 }; 182 StringBuilder sb = new StringBuilder(); 183 184 boolean first = true; 185 186 String t; 187 188 while ((t = desc(types, pos)) != null) { 189 if (first) { 190 first = false; 191 } else { 192 sb.append(','); 193 } 194 sb.append(t); 195 } 196 197 return sb.toString(); 198 } 199 200 /** 201 * Pattern for matching a method descriptor. Match results can 202 * be retrieved from named capture groups as follows: "name(params)return". 203 */ 204 static final Pattern DESC_PAT = Pattern.compile("(?<name>.*)\\((?<args>.*)\\)(?<return>.*)"); 205 206 /** 207 * Pretty-prints the data contained in the given DeprData object. 208 * 209 * @param dd the deprecation data object 210 * @return the formatted string 211 */ 212 public static String print(DeprData dd) { 213 StringBuilder sb = new StringBuilder(); 214 sb.append(depr(dd.since, dd.forRemoval)) 215 .append(' '); 216 217 switch (dd.kind) { 218 case ANNOTATION_TYPE: 219 sb.append("@interface "); 220 sb.append(unslashify(dd.typeName)); 221 break; 222 case CLASS: 223 sb.append("class "); 224 sb.append(unslashify(dd.typeName)); 225 break; 226 case ENUM: 227 sb.append("enum "); 228 sb.append(unslashify(dd.typeName)); 229 break; 230 case INTERFACE: 231 sb.append("interface "); 232 sb.append(unslashify(dd.typeName)); 233 break; 234 235 case ENUM_CONSTANT: 236 case FIELD: 237 sb.append(unslashify(dd.typeName)) 238 .append('.') 239 .append(dd.nameSig); 240 break; 241 case CONSTRUCTOR: 242 Matcher cons = DESC_PAT.matcher(dd.nameSig); 243 sb.append(unslashify(dd.typeName)); 244 if (cons.matches()) { 245 sb.append('(') 246 .append(parms(cons.group("args"))) 247 .append(')'); 248 } else { 249 sb.append('.') 250 .append(dd.nameSig); 251 } 252 break; 253 case METHOD: 254 Matcher meth = DESC_PAT.matcher(dd.nameSig); 255 if (meth.matches()) { 256 sb.append(desc(meth.group("return"))) 257 .append(' ') 258 .append(unslashify(dd.typeName)) 259 .append('.') 260 .append(meth.group("name")) 261 .append('(') 262 .append(parms(meth.group("args"))) 263 .append(')'); 264 } else { 265 sb.append(unslashify(dd.typeName)) 266 .append('.') 267 .append(dd.nameSig); 268 } 269 break; 270 } 271 272 return sb.toString(); 273 } 274} 275