| /* |
| * Copyright (c) 2014, 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. |
| * |
| * 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. |
| */ |
| |
| import java.lang.reflect.Method; |
| import java.util.ArrayList; |
| |
| public class MethodIdentifierParser { |
| |
| private String logString; |
| private String className; |
| private String methodName; |
| private String methodDescriptor; |
| |
| /** |
| * This is a utility class for parsing the log entries for a method. It supplies |
| * a few select methods for reflecting the class and method from that information. |
| * |
| * Example log entries: |
| * "java.util.TreeMap.successor(Ljava/util/TreeMap$Entry;)Ljava/util/TreeMap$Entry;" |
| */ |
| |
| public MethodIdentifierParser(String logString) { |
| this.logString = logString; |
| |
| int i = logString.lastIndexOf("."); // find start of method name |
| className = logString.substring(0, i); // classname is everything before |
| int i2 = logString.indexOf("("); // Signature starts with an '(' |
| methodName = logString.substring(i+1, i2); |
| methodDescriptor = logString.substring(i2, logString.length()); |
| |
| // Add sanity check for extracted fields |
| } |
| |
| public Method getMethod() throws NoSuchMethodException, SecurityException, ClassNotFoundException { |
| try { |
| return Class.forName(className).getDeclaredMethod(methodName, getParamenterDescriptorArray()); |
| } catch (UnexpectedTokenException e) { |
| throw new RuntimeException("Parse failed"); |
| } |
| } |
| |
| public Class<?>[] getParamenterDescriptorArray() throws ClassNotFoundException, UnexpectedTokenException { |
| ParameterDecriptorIterator s = new ParameterDecriptorIterator(methodDescriptor); |
| Class<?> paramType; |
| ArrayList<Class<?>> list = new ArrayList<Class<?>>(); |
| while ((paramType = s.nextParamType()) != null) { |
| list.add(paramType); |
| } |
| if (list.size() > 0) { |
| return list.toArray(new Class<?>[list.size()]); |
| } else { |
| return null; |
| } |
| } |
| |
| class ParameterDecriptorIterator { |
| |
| // This class uses charAt() indexing for startMark and i |
| // That is when i points to the last char it can be retrieved with |
| // charAt(i). Including the last char for a subString requires |
| // substring(startMark, i+1); |
| |
| private String methodDescriptor; |
| private int startMark; |
| |
| public ParameterDecriptorIterator(String signature) { |
| this.methodDescriptor = signature; |
| this.startMark = 0; |
| if (signature.charAt(0) == '(') { |
| this.startMark = 1; |
| } |
| } |
| |
| public Class<?> nextParamType() throws UnexpectedTokenException { |
| int i = startMark; |
| while (methodDescriptor.length() > i) { |
| switch (methodDescriptor.charAt(i)) { |
| case 'C': |
| case 'B': |
| case 'I': |
| case 'J': |
| case 'Z': |
| case 'F': |
| case 'D': |
| case 'S': |
| // Primitive class case, but we may have gotten here with [ as first token |
| break; |
| case 'L': |
| // Internal class name suffixed by ';' |
| while (methodDescriptor.charAt(i) != ';') { |
| i++; |
| } |
| break; |
| case '[': |
| i++; // arrays -> do another pass |
| continue; |
| case ')': |
| return null; // end found |
| case 'V': |
| case ';': |
| default: |
| throw new UnexpectedTokenException(methodDescriptor, i); |
| } |
| break; |
| } |
| if (i == startMark) { |
| // Single char -> primitive class case |
| startMark++; // Update for next iteration |
| switch (methodDescriptor.charAt(i)) { |
| case 'C': |
| return char.class; |
| case 'B': |
| return byte.class; |
| case 'I': |
| return int.class; |
| case 'J': |
| return long.class; |
| case 'F': |
| return float.class; |
| case 'D': |
| return double.class; |
| case 'S': |
| return short.class; |
| case 'Z': |
| return boolean.class; |
| default: |
| throw new UnexpectedTokenException(methodDescriptor, i); |
| } |
| } else { |
| // Multi char case |
| String nextParam; |
| if (methodDescriptor.charAt(startMark) == 'L') { |
| // When reflecting a class the leading 'L' and trailing';' must be removed. |
| // (When reflecting an array of classes, they must remain...) |
| nextParam = methodDescriptor.substring(startMark+1, i); |
| } else { |
| // Any kind of array - simple case, use whole descriptor when reflecting. |
| nextParam = methodDescriptor.substring(startMark, i+1); |
| } |
| startMark = ++i; // Update for next iteration |
| try { |
| // The parameter descriptor uses JVM internal class identifier with '/' as |
| // package separator, but Class.forName expects '.'. |
| nextParam = nextParam.replace('/', '.'); |
| return Class.forName(nextParam); |
| } catch (ClassNotFoundException e) { |
| System.out.println("Class not Found: " + nextParam); |
| return null; |
| } |
| } |
| } |
| } |
| |
| class UnexpectedTokenException extends Exception { |
| String descriptor; |
| int i; |
| public UnexpectedTokenException(String descriptor, int i) { |
| this.descriptor = descriptor; |
| this.i = i; |
| } |
| |
| @Override |
| public String toString() { |
| return "Unexpected token at: " + i + " in signature: " + descriptor; |
| } |
| |
| private static final long serialVersionUID = 1L; |
| } |
| |
| public void debugPrint() { |
| System.out.println("mlf in: " + logString); |
| System.out.println("mlf class: " + className); |
| System.out.println("mlf method: " + methodName); |
| System.out.println("mlf methodDescriptor: " + methodDescriptor); |
| } |
| } |