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 '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 }
--- EOF ---