001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: * The Original Software is NetBeans. The Initial Developer of the Original
026: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
027: * Microsystems, Inc. All Rights Reserved.
028: *
029: * If you wish your version of this file to be governed by only the CDDL
030: * or only the GPL Version 2, indicate your decision by adding
031: * "[Contributor] elects to include this software in this distribution
032: * under the [CDDL or GPL Version 2] license." If you do not indicate a
033: * single choice of license, a recipient has the option to distribute
034: * your version of this file under either the CDDL, the GPL Version 2 or
035: * to extend the choice of license to its licensees as provided above.
036: * However, if you add GPL Version 2 code and therefore, elected the GPL
037: * Version 2 license, then the option applies only if the new code is
038: * made subject to such option by the copyright holder.
039: */
040:
041: package org.netbeans.lib.profiler.utils.formatting;
042:
043: import org.netbeans.lib.profiler.utils.*;
044: import java.util.logging.Level;
045: import java.util.logging.Logger;
046:
047: /**
048: * A class that can be used to obtain Java class or method name formatted in various ways.
049: *
050: * @author Ian Formanek
051: * @author Misha Dmitriev
052: */
053: public class PlainFormattableMethodName implements Formattable {
054: //~ Static fields/initializers -----------------------------------------------------------------------------------------------
055:
056: private static final Logger LOGGER = Logger
057: .getLogger(PlainFormattableMethodName.class.getName());
058: private static final String BOOLEAN_TEXT = "boolean"; // NOI18N
059: private static final String CHAR_TEXT = "char"; // NOI18N
060: private static final String BYTE_TEXT = "byte"; // NOI18N
061: private static final String SHORT_TEXT = "short"; // NOI18N
062: private static final String INT_TEXT = "int"; // NOI18N
063: private static final String LONG_TEXT = "long"; // NOI18N
064: private static final String FLOAT_TEXT = "float"; // NOI18N
065: private static final String DOUBLE_TEXT = "double"; // NOI18N
066: private static final String VOID_TEXT = "void"; // NOI18N
067: private static final char BOOLEAN_CODE = 'Z'; // NOI18N
068: private static final char CHAR_CODE = 'C'; // NOI18N
069: private static final char BYTE_CODE = 'B'; // NOI18N
070: private static final char SHORT_CODE = 'S'; // NOI18N
071: private static final char INT_CODE = 'I'; // NOI18N
072: private static final char LONG_CODE = 'J'; // NOI18N
073: private static final char FLOAT_CODE = 'F'; // NOI18N
074: private static final char DOUBLE_CODE = 'D'; // NOI18N
075: private static final char VOID_CODE = 'V'; // NOI18N
076:
077: //~ Instance fields ----------------------------------------------------------------------------------------------------------
078:
079: private String className;
080: private String methodName;
081: private String params;
082: private String returnType;
083: private int verbosity;
084:
085: //~ Constructors -------------------------------------------------------------------------------------------------------------
086:
087: /**
088: * Creates a new methodNameFormatter for given class/method/signature.
089: * The method name and signature can be null, in which case the formatter works for class only.
090: *
091: * @param cname A fully qualified name of the class, or null if only formatting methods
092: * - e.g. "java/lang/String"
093: * @param mname A name of method, empty string or "<init>" for constructors or null if method is not specified
094: * - e.g. "concat"
095: * @param sig VM signature of method or null if method is not specified or <clinit> is the method
096: * - e.g. "(Ljava/lang/String;)Ljava/lang/String;"
097: * @param verbosity verbosity level: 1 = getFormattedClass(), 2 = getFormattedMethod(), 3 = getFormattedClassAndMethod(),
098: * 4 = getFullFormattedMethod(), 5 = getFullFormatedClassAndMethod()
099: */
100: PlainFormattableMethodName(String cname, String mname, String sig,
101: int verbosity) {
102: int curPos;
103: int idx1;
104: int idx2;
105: char nextChar;
106:
107: this .verbosity = verbosity;
108:
109: StringBuffer arrayIndicator = new StringBuffer();
110:
111: if (cname != null) {
112: this .className = cname.replace('/', '.'); // NOI18N
113: } else {
114: // can be null in case formatter is only interested in method formatting // NOI18N
115: this .className = "<unknown class>"; // NOI18N
116: }
117:
118: if (this .className.length() == 0) { // deafult package wildcard
119: this .className = ".*"; // NOI18N
120: }
121:
122: if (this .className.endsWith(".")) { // slightly different wildcard notation; for the backward compatibility sake we keep it
123: this .className = this .className + "*";
124: }
125:
126: if (mname == null) {
127: // methodName can be null when class- or package-level view is used
128: params = ""; // NOI18N
129: returnType = ""; // NOI18N
130: } else {
131: if ("".equals(mname) && !this .className.contains("*")) {
132: // NOI18N
133: this .methodName = "<init>"; // NOI18N
134: } else {
135: this .methodName = mname;
136: }
137:
138: if ("<clinit>".equals(methodName)
139: || Wildcards.isMethodWildcard(methodName)) {
140: // NOI18N
141: params = ""; // NOI18N
142: returnType = ""; // NOI18N
143: } else {
144: idx1 = sig.lastIndexOf(')') + 1; // NOI18N
145:
146: if ((idx1 > 0) && !"<init>".equals(methodName)) {
147: // NOI18N
148: // For e.g. the "Thread" root node it may be zero; there was a bug with it when this method
149: // was hit for "Thread" in results export
150: returnType = sig.substring(idx1);
151: curPos = 0;
152:
153: while (returnType.charAt(curPos) == '[') {
154: // NOI18N
155: arrayIndicator.append("[]"); // NOI18N
156: curPos++;
157: }
158:
159: nextChar = returnType.charAt(curPos++);
160:
161: if (nextChar == BOOLEAN_CODE) {
162: returnType = BOOLEAN_TEXT
163: + arrayIndicator.toString();
164: } else if (nextChar == CHAR_CODE) {
165: returnType = CHAR_TEXT
166: + arrayIndicator.toString();
167: } else if (nextChar == BYTE_CODE) {
168: returnType = BYTE_TEXT
169: + arrayIndicator.toString();
170: } else if (nextChar == SHORT_CODE) {
171: returnType = SHORT_TEXT
172: + arrayIndicator.toString();
173: } else if (nextChar == INT_CODE) {
174: returnType = INT_TEXT
175: + arrayIndicator.toString();
176: } else if (nextChar == LONG_CODE) {
177: returnType = LONG_TEXT
178: + arrayIndicator.toString();
179: } else if (nextChar == FLOAT_CODE) {
180: returnType = FLOAT_TEXT
181: + arrayIndicator.toString();
182: } else if (nextChar == DOUBLE_CODE) {
183: returnType = DOUBLE_TEXT
184: + arrayIndicator.toString();
185: } else if (nextChar == VOID_CODE) {
186: returnType = VOID_TEXT
187: + arrayIndicator.toString();
188: } else {
189: // return type is a class
190: // Check if the class belongs to the java.lang.* package, and replace it with simple name if so.
191: // However, avoid doing so if it's from say java.lang.ref.* package - otherwise we'll get confusing
192: // names like ref.Reference
193: returnType = returnType.substring(curPos,
194: returnType.length() - 1); //strip "L" at the beginning
195: // and ";" at end
196:
197: if (returnType.startsWith("java/lang/")
198: && (returnType.indexOf('/', 10) == -1)) {
199: // NOI18N
200: returnType = returnType.substring(10);
201: }
202:
203: returnType = returnType.replace('$', '.'); // NOI18N
204: returnType = returnType.replace('/', '.')
205: + arrayIndicator.toString(); // NOI18N
206: }
207: } else {
208: // constructor or no end parenthesis
209: returnType = ""; // NOI18N
210: }
211:
212: idx1 = sig.indexOf('(') + 1; // NOI18N
213: idx2 = sig.lastIndexOf(')'); // NOI18N
214:
215: if (idx2 > 0) {
216: String paramsString = sig.substring(idx1, idx2);
217: StringBuffer paramsBuf = new StringBuffer();
218: arrayIndicator.setLength(0);
219: curPos = 0;
220:
221: while (curPos < paramsString.length()) {
222: while (paramsString.charAt(curPos) == '[') {
223: // NOI18N
224: arrayIndicator.append("[]"); // NOI18N
225: curPos++;
226: }
227:
228: nextChar = paramsString.charAt(curPos++);
229:
230: if (nextChar == BOOLEAN_CODE) {
231: paramsBuf.append(BOOLEAN_TEXT);
232: } else if (nextChar == CHAR_CODE) {
233: paramsBuf.append(CHAR_TEXT);
234: } else if (nextChar == BYTE_CODE) {
235: paramsBuf.append(BYTE_TEXT);
236: } else if (nextChar == SHORT_CODE) {
237: paramsBuf.append(SHORT_TEXT);
238: } else if (nextChar == INT_CODE) {
239: paramsBuf.append(INT_TEXT);
240: } else if (nextChar == LONG_CODE) {
241: paramsBuf.append(LONG_TEXT);
242: } else if (nextChar == FLOAT_CODE) {
243: paramsBuf.append(FLOAT_TEXT);
244: } else if (nextChar == DOUBLE_CODE) {
245: paramsBuf.append(DOUBLE_TEXT);
246: } else {
247: // it's a class
248: int startPos = curPos;
249:
250: while (paramsString.charAt(curPos) != ';') {
251: // NOI18N
252: curPos++;
253: }
254:
255: String typeName = paramsString.substring(
256: startPos, curPos); //strip "L" at the beginning and ";" at end
257:
258: if (typeName.startsWith("java/lang/")
259: && (typeName.indexOf('/', 10) == -1)) {
260: // NOI18N
261: typeName = typeName.substring(10); // NOI18N
262: }
263:
264: typeName = typeName.replace('$', '.'); // NOI18N
265: typeName = typeName.replace('/', '.'); // NOI18N
266: paramsBuf.append(typeName);
267: curPos++;
268: }
269:
270: if (arrayIndicator.length() > 0) {
271: paramsBuf.append(arrayIndicator.toString());
272: }
273:
274: arrayIndicator.setLength(0);
275:
276: if (curPos < paramsString.length()) {
277: paramsBuf.append(", "); // NOI18N
278: }
279: }
280:
281: params = paramsBuf.toString();
282: } else {
283: params = ""; // NOI18N
284: }
285: }
286: }
287:
288: if (LOGGER.isLoggable(Level.FINEST)) {
289: LOGGER.finest("Formattable method name for:");
290: LOGGER.finest("Class: " + this .className);
291: LOGGER.finest("Method: " + this .methodName);
292: LOGGER.finest("Return type: " + this .returnType);
293: LOGGER.finest("Parameters: " + this .params);
294: }
295: }
296:
297: //~ Methods ------------------------------------------------------------------------------------------------------------------
298:
299: /**
300: * @return formatted class name, using '.' to separate packages = e.g. "java.lang.String"
301: */
302: public String getFormattedClass() {
303: return className;
304: }
305:
306: /**
307: * @return formatted class and method name (with parameter types, but without its return type).
308: * Will return the same as getFormattedClass if method was not specified.
309: */
310: public String getFormattedClassAndMethod() {
311: if ((methodName == null) || (methodName.length() == 0)) {
312: return getFormattedClass();
313: } else {
314: return className + "." + getFormattedMethod(); // NOI18N
315: }
316: }
317:
318: /**
319: * @return formatted method name, with parameter types, but without the return type. Will return empty string
320: * if method was not specified.
321: */
322: public String getFormattedMethod() {
323: if (LOGGER.isLoggable(Level.FINER)) {
324: LOGGER.finer("Getting formatted method name for "
325: + methodName);
326: }
327:
328: if ((methodName == null) || (methodName.length() == 0)) {
329: return ""; // NOI18N // Understanding is that class-level view is used
330: } else if ("<clinit>".equals(methodName)) {
331: // NOI18N
332: return methodName;
333: } else {
334: return methodName + "(" + getParamsString() + ")"; // NOI18N
335: }
336: }
337:
338: /**
339: * @return formatted class and method name (with parameter types and return type)
340: * Will return the same as getFormattedClass if method was not specified.
341: */
342: public String getFullFormattedClassAndMethod() {
343: if ((methodName == null) || (methodName.length() == 0)) {
344: return getFormattedClass();
345: } else {
346: return className + "." + getFullFormattedMethod(); // NOI18N
347: }
348: }
349:
350: /**
351: * @return formatted method name with parameter types and return type (return type not used if constructor).
352: * Will return empty string if method was not specified.
353: */
354: public String getFullFormattedMethod() {
355: if (LOGGER.isLoggable(Level.FINER)) {
356: LOGGER.finer("Getting full formatted method name for "
357: + methodName);
358: }
359:
360: if ((methodName == null) || (methodName.length() == 0)) {
361: return ""; // NOI18N // Understanding is that class-level view is used
362: } else if ("<clinit>".equals(methodName)) {
363: // NOI18N
364: return methodName;
365: } else if ("<init>".equals(methodName)) {
366: // NOI18N
367: return methodName + "(" + getParamsString() + ")"; // NOI18N
368: } else if ("*".equals(methodName)) {
369: // NOI18N
370: return methodName;
371: } else {
372: return methodName + "(" + getParamsString() + ") : "
373: + getReturnTypeX(); // NOI18N
374: }
375: }
376:
377: /**
378: * @return parameters of the given method - formatted string -, empty string if the method has no parameters or
379: * no method was specified
380: */
381: public String getParamsString() {
382: return params;
383: }
384:
385: /**
386: * @return return type of the given method, empty String if void or method was not specified
387: */
388: public String getReturnTypeX() {
389: return returnType;
390: }
391:
392: public String toFormatted() {
393: switch (verbosity) {
394: case 1:
395: return getFormattedClass();
396: case 2:
397: return getFormattedMethod();
398: case 3:
399: return getFormattedClassAndMethod();
400: case 4:
401: return getFullFormattedMethod();
402: case 5:
403: return getFullFormattedClassAndMethod();
404: default:
405: return getFullFormattedClassAndMethod();
406: }
407: }
408:
409: public String toString() {
410: return getFullFormattedClassAndMethod();
411: }
412:
413: private boolean isAllWildCard(String methodName) {
414: return methodName.equals("<all>") || methodName.equals("*"); // NOI18N
415: }
416: }
|