1 /* 2 * Copyright (c) 2014, 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. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 import java.lang.reflect.Method; 25 import java.util.ArrayList; 26 27 public class MethodIdentifierParser { 28 29 private String logString; 30 private String className; 31 private String methodName; 32 private String methodDescriptor; 33 34 /** 35 * This is a utility class for parsing the log entries for a method. It supplies 36 * a few select methods for reflecting the class and method from that information. 37 * 38 * Example log entries: 39 * "java.util.TreeMap.successor(Ljava/util/TreeMap$Entry;)Ljava/util/TreeMap$Entry;" 40 */ 41 42 public MethodIdentifierParser(String logString) { 43 this.logString = logString; 44 45 int i = logString.lastIndexOf("."); // find start of method name 46 className = logString.substring(0, i); // classname is everything before 47 int i2 = logString.indexOf("("); // Signature starts with an '(' 48 methodName = logString.substring(i+1, i2); 49 methodDescriptor = logString.substring(i2, logString.length()); 50 51 // Add sanity check for extracted fields 52 } 53 54 public Method getMethod() throws NoSuchMethodException, SecurityException, ClassNotFoundException, Exception { 55 try { 56 return Class.forName(className).getDeclaredMethod(methodName, getParamenterDescriptorArray()); 57 } catch (UnexpectedTokenException e) { 58 throw new Exception("Parse failed"); 59 } 60 } 61 62 public Class<?>[] getParamenterDescriptorArray() throws ClassNotFoundException, UnexpectedTokenException { 63 ParameterDecriptorIterator s = new ParameterDecriptorIterator(methodDescriptor); 64 Class<?> paramType; 65 ArrayList<Class<?>> list = new ArrayList<Class<?>>(); 66 while ((paramType = s.nextParamType()) != null) { 67 list.add(paramType); 68 } 69 if (list.size() > 0) { 70 return list.toArray(new Class<?>[list.size()]); 71 } else { 72 return null; 73 } 74 } 75 76 class ParameterDecriptorIterator { 77 78 // This class uses charAt() indexing for startMark and i 79 // That is when i points to the last char it can be retrieved with 80 // charAt(i). Including the last char for a subString requires 81 // substring(startMark, i+1); 82 83 private String methodDescriptor; 84 private int startMark; 85 86 public ParameterDecriptorIterator(String signature) { 87 this.methodDescriptor = signature; 88 this.startMark = 0; 89 if (signature.charAt(0) == '(') { 90 this.startMark = 1; 91 } 92 } 93 94 public Class<?> nextParamType() throws UnexpectedTokenException { 95 int i = startMark; 96 while (methodDescriptor.length() > i) { 97 switch (methodDescriptor.charAt(i)) { 98 case 'C': 99 case 'B': 100 case 'I': 101 case 'J': 102 case 'Z': 103 case 'F': 104 case 'D': 105 case 'S': 106 // Primitive class case, but we may have gotten here with [ as first token 107 break; 108 case 'L': 109 // Internal class name suffixed by ';' 110 while (methodDescriptor.charAt(i) != ';') { 111 i++; 112 } 113 break; 114 case '[': 115 i++; // arrays -> do another pass 116 continue; 117 case ')': 118 return null; // end found 119 case 'V': 120 case ';': 121 default: 122 throw new UnexpectedTokenException(methodDescriptor, i); 123 } 124 break; 125 } 126 if (i == startMark) { 127 // Single char -> primitive class case 128 startMark++; // Update for next iteration 129 switch (methodDescriptor.charAt(i)) { 130 case 'C': 131 return char.class; 132 case 'B': 133 return byte.class; 134 case 'I': 135 return int.class; 136 case 'J': 137 return long.class; 138 case 'F': 139 return float.class; 140 case 'D': 141 return double.class; 142 case 'S': 143 return short.class; 144 case 'Z': 145 return boolean.class; 146 default: 147 throw new UnexpectedTokenException(methodDescriptor, i); 148 } 149 } else { 150 // Multi char case 151 String nextParam; 152 if (methodDescriptor.charAt(startMark) == 'L') { 153 // When reflecting a class the leading 'L' and trailing';' must be removed. 154 // (When reflecting an array of classes, they must remain...) 155 nextParam = methodDescriptor.substring(startMark+1, i); 156 } else { 157 // Any kind of array - simple case, use whole descriptor when reflecting. 158 nextParam = methodDescriptor.substring(startMark, i+1); 159 } 160 startMark = ++i; // Update for next iteration 161 try { 162 // The parameter descriptor uses JVM internal class identifier with '/' as 163 // package separator, but Class.forName expects '.'. 164 nextParam = nextParam.replace('/', '.'); 165 return Class.forName(nextParam); 166 } catch (ClassNotFoundException e) { 167 System.out.println("Class not Found: " + nextParam); 168 return null; 169 } 170 } 171 } 172 } 173 174 class UnexpectedTokenException extends Exception { 175 String descriptor; 176 int i; 177 public UnexpectedTokenException(String descriptor, int i) { 178 this.descriptor = descriptor; 179 this.i = i; 180 } 181 182 @Override 183 public String toString() { 184 return "Unexpected token at: " + i + " in signature: " + descriptor; 185 } 186 187 private static final long serialVersionUID = 1L; 188 } 189 190 public void debugPrint() { 191 System.out.println("mlf in: " + logString); 192 System.out.println("mlf class: " + className); 193 System.out.println("mlf method: " + methodName); 194 System.out.println("mlf methodDescriptor: " + methodDescriptor); 195 } 196 }