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
26 package com.sun.tools.jdeprscan;
27
28 import java.util.regex.Matcher;
29 import java.util.regex.Pattern;
30
31 /**
32 * Utility class for pretty-printing various bits of API syntax.
33 */
34 public 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 'Q':
153 case 'L':
154 int semi = desc.indexOf(';', pos);
155 if (semi == -1) {
156 return null;
157 }
158 name = unslashify(desc.substring(pos, semi));
159 pos = semi + 1;
160 break;
161 default:
162 return null;
163 }
164
165 StringBuilder sb = new StringBuilder(name);
166 for (int i = 0; i < dims; i++) {
167 sb.append("[]");
168 }
169 pos_inout[0] = pos;
170 return sb.toString();
171 }
172
173 /**
174 * Converts a series of type descriptors into a comma-separated,
175 * readable string. This is used for the parameter types of a
176 * method descriptor.
177 *
178 * @param types the parameter types
179 * @return the readable string
180 */
181 static String parms(String types) {
182 int[] pos = new int[] { 0 };
183 StringBuilder sb = new StringBuilder();
184
185 boolean first = true;
186
187 String t;
188
189 while ((t = desc(types, pos)) != null) {
190 if (first) {
191 first = false;
192 } else {
193 sb.append(',');
194 }
195 sb.append(t);
196 }
197
198 return sb.toString();
199 }
200
201 /**
202 * Pattern for matching a method descriptor. Match results can
203 * be retrieved from named capture groups as follows: "name(params)return".
204 */
205 static final Pattern DESC_PAT = Pattern.compile("(?<name>.*)\\((?<args>.*)\\)(?<return>.*)");
206
207 /**
208 * Pretty-prints the data contained in the given DeprData object.
209 *
210 * @param dd the deprecation data object
211 * @return the formatted string
212 */
213 public static String print(DeprData dd) {
214 StringBuilder sb = new StringBuilder();
215 sb.append(depr(dd.since, dd.forRemoval))
216 .append(' ');
217
218 switch (dd.kind) {
219 case ANNOTATION_TYPE:
220 sb.append("@interface ");
221 sb.append(unslashify(dd.typeName));
222 break;
223 case CLASS:
224 sb.append("class ");
225 sb.append(unslashify(dd.typeName));
226 break;
227 case ENUM:
228 sb.append("enum ");
229 sb.append(unslashify(dd.typeName));
230 break;
231 case INTERFACE:
232 sb.append("interface ");
233 sb.append(unslashify(dd.typeName));
234 break;
235
236 case ENUM_CONSTANT:
237 case FIELD:
238 sb.append(unslashify(dd.typeName))
239 .append('.')
240 .append(dd.nameSig);
241 break;
242 case CONSTRUCTOR:
243 Matcher cons = DESC_PAT.matcher(dd.nameSig);
244 sb.append(unslashify(dd.typeName));
245 if (cons.matches()) {
246 sb.append('(')
247 .append(parms(cons.group("args")))
248 .append(')');
249 } else {
250 sb.append('.')
251 .append(dd.nameSig);
252 }
253 break;
254 case METHOD:
255 Matcher meth = DESC_PAT.matcher(dd.nameSig);
256 if (meth.matches()) {
257 sb.append(desc(meth.group("return")))
258 .append(' ')
259 .append(unslashify(dd.typeName))
260 .append('.')
261 .append(meth.group("name"))
262 .append('(')
263 .append(parms(meth.group("args")))
264 .append(')');
265 } else {
266 sb.append(unslashify(dd.typeName))
267 .append('.')
268 .append(dd.nameSig);
269 }
270 break;
271 }
272
273 return sb.toString();
274 }
275 }
--- EOF ---