Source code: jreversepro/common/Helper.java
1 /*
2 * @(#)Helper.java
3 *
4 * JReversePro - Java Decompiler / Disassembler.
5 * Copyright (C) 2000 2001 Karthik Kumar.
6 * EMail: akkumar@users.sourceforge.net
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it , under the terms of the GNU General Public License as published
10 * by the Free Software Foundation; either version 2 of the License,
11 * or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16 * See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program.If not, write to
19 * The Free Software Foundation, Inc.,
20 * 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
22 */
23 package jreversepro.common;
24
25 import java.util.List;
26 import java.util.ArrayList;
27
28 /**
29 * Helper contains a list of assorted methods that 'helps'
30 * in manipulating the data present in the class file.
31 * @author Karthik Kumar
32 **/
33 public class Helper implements KeyWords {
34
35 /**
36 * Private constructor to prevent any instance from being created.
37 **/
38 private Helper() { }
39
40 /**
41 * Working Version Could be compromised
42 **/
43 static final String DEFAULT_VERSION = "1.2.2";
44
45
46 /** Debug flag
47 * Default value = false.
48 **/
49 static boolean debugFlag;
50
51 /**
52 * static initializer method.
53 **/
54 static {
55 debugFlag = false;
56 }
57
58 /**
59 * @param logMsg Message to be logged.
60 **/
61 public static void log(String logMsg) {
62 if (debugFlag) {
63 System.out.println(logMsg);
64 }
65 }
66
67 /**
68 * @param ex Exception to be logged.
69 **/
70 public static void log(Exception ex) {
71 if (debugFlag) {
72 ex.printStackTrace(System.out);
73 }
74 }
75
76 /**
77 * Log without end-of-line at the end.
78 * @param logMsg Message to be logged.
79 **/
80 public static void logNoEol(String logMsg) {
81 if (debugFlag) {
82 System.out.print(logMsg);
83 }
84 }
85
86 /**
87 * Toggles the debug flag.
88 * @return Returns the new debug flag after toggling.
89 **/
90 public static boolean toggleDebug() {
91 debugFlag = !debugFlag;
92 return debugFlag;
93 }
94
95 /**
96 * @return the value of debug flag.
97 **/
98 public static boolean isDebug() {
99 return debugFlag;
100 }
101
102 /**
103 * Returns the Package name alone from a fully qualified name.
104 * <p>
105 * For Example , if <code>FullName = java/lang/StringBuffer,</code>
106 * <br>then a call to <code>getPackageName(arg)</code> returns the
107 * value <code>java.lang</code>.
108 * <p>
109 *
110 * @param aFullName A Fully Qualified Name.
111 * @return the package name , alone with the dots separating the
112 * classes.
113 **/
114 public static String getPackageName(String aFullName) {
115 aFullName = Helper.getJavaDataType(aFullName, false);
116 aFullName = aFullName.replace('/', '.');
117 int dotIndex = aFullName.lastIndexOf(".");
118 if (dotIndex != -1) {
119 return aFullName.substring(0, dotIndex);
120 } else {
121 return "";
122 }
123 }
124
125
126 /**
127 * Determines the Java representation , given the JVM representation
128 * of data types.
129 * <p>
130 * <table>
131 * <tr><th><code>dataType</code> </th>
132 * <th><code>formatDataType(dataType)</code></th></tr>
133 * <tr><td><code>B</code></td><td>byte</td></tr>
134 * <tr><td><code>C</code></td><td>char</td></tr>
135 * <tr><td><code>D</code></td><td>double</td></tr>
136 * <tr><td><code>F</code></td><td>float</td></tr>
137 * <tr><td><code>I</code></td><td>int</td></tr>
138 * <tr><td><code>J</code></td><td>long</td></tr>
139 * <tr><td><code>S</code></td><td>short</td></tr>
140 * <tr><td><code>V</code></td><td>void</td></tr>
141 * <tr><td><code>Z</code></td><td>boolean</td></tr>
142 * <tr><td><code>[Z</code></td><td>boolean [] , array representation
143 * </td></tr>
144 * <tr><td><code>Ljava/lang/String </code></td><td>
145 * <code> java/lang/String</code> </td></tr>
146 * </table>
147 * @param aDataType JVM representation of the data type.
148 * @param associated If set, then an array representation is returned.ñ
149 * @return Java Language representation of aDataType.
150 **/
151 public static String getJavaDataType(String aDataType,
152 boolean associated) {
153
154 char firstChar = aDataType.charAt(0);
155
156 if (aDataType.length() == 1) {
157 switch (firstChar) {
158 case 'B': return "byte";
159 case 'C': return "char";
160 case 'D': return "double";
161 case 'F': return "float";
162 case 'I': return "int";
163 case 'J': return "long";
164 case 'S': return "short";
165 case 'V': return "void";
166 case 'Z': return "boolean";
167 default:
168 return "invalid";
169 }
170 } else if (firstChar == '[') {
171 String type = getJavaDataType(aDataType.substring(1),
172 associated);
173 if (!associated) {
174 return type + "[]";
175 } else {
176 return type;
177 }
178 } else if (firstChar == 'L') {
179 int len = aDataType.length();
180 if (aDataType.indexOf(";") == -1) {
181 return aDataType.substring(1);
182 } else {
183 return aDataType.substring(1, len - 1);
184 }
185 }
186 return aDataType;
187 }
188
189 /**
190 * Determines the length of the JVM datatype representation
191 * given the JVM signature.
192 * <p>
193 * <table>
194 * <tr><th><code>dataType</code> </th>
195 * <th><code>getSignTokenLength(dataType)(dataType)</code></th></tr>
196 * <tr><td><code>all basic data types</code></td><td>1</td></tr>
197 * <tr><td><code>[XYZ</code></td><td>len(XYZ) + 1
198 * </td></tr>
199 * <tr><td><code>Ljava/lang/String </code></td><td>
200 * <code> len(Ljava/lang/String)</code> </td></tr>
201 * </table>
202 * @param aDataType Signature of a method as present in
203 * the class file in JVM representation, containing a list
204 * of datatypes.
205 * @return the length of the first valid datatype.
206 **/
207 public static int getSignTokenLength(String aDataType) {
208 char ch = aDataType.charAt(0);
209 switch(ch) {
210 case 'B':
211 case 'C':
212 case 'D':
213 case 'F':
214 case 'I':
215 case 'J':
216 case 'S':
217 case 'V':
218 case 'Z':
219 return 1;
220 case '[':
221 return (
222 getSignTokenLength(aDataType.substring(1)) + 1);
223 case 'L':
224 int semiColon = aDataType.indexOf(";");
225 if (semiColon == -1) {
226 return aDataType.length();
227 } else {
228 return (semiColon + 1);
229 }
230 default:
231 return 0;
232 }
233 }
234
235 /**
236 * Returns the arguments in array form
237 * given the JVM signature.
238 * <p>
239 * For example , <code>IILjava/lang/String</code>
240 * could be returned as <br>
241 * <code>( int , int , java/lang/String )</code>.
242 * @param aSignature Signature of the method.
243 * @return The method arguments as a List
244 **/
245 public static List getArguments(String aSignature) {
246 List args = new ArrayList();
247 int endIndex = aSignature.indexOf(")");
248 if (endIndex != 1) {
249 aSignature = aSignature.substring(1 , endIndex);
250
251 String origStr = aSignature;
252 int length = origStr.length();
253 //Start Processing Rhs
254 int curIndex = 0;
255
256 while (curIndex < length) {
257 aSignature = origStr.substring(curIndex);
258 int tokenLength = getSignTokenLength(aSignature);
259 String tokenString =
260 aSignature.substring(0, tokenLength);
261 int semiColon = tokenString.indexOf(";");
262 if (semiColon != -1) {
263 tokenString = tokenString.substring(0, semiColon);
264 }
265 args.add(tokenString);
266 curIndex += tokenLength;
267 }
268 }
269 return args;
270 }
271
272
273 /**
274 * Given the Signature of the method , this provides us the
275 * return type.
276 * <p>
277 * @param aSignature Signature of the method.
278 * @return the return type associated with the method signature,
279 * The type returned corresponds to JVM representation.
280 **/
281 public static String getReturnType(String aSignature) {
282 int index = aSignature.indexOf(")");
283 return aSignature.substring(index + 1);
284 }
285
286 /**
287 * Extracts the value of a particular number of bits.
288 * <p>
289 * For example <code>lowNBits(169 , 5 )</code>
290 * returns <code> (10101001,5) -> 10101 i.e 21 </code> <br>.
291 * @param aValue Value containing the integer in string form.
292 * @param aNumBits Number of bits that is to be extracted.
293 * @return the masked value of the N Bit Number.
294 **/
295 public static String lowNbits(String aValue, int aNumBits) {
296 int val = Integer.parseInt(aValue);
297 if (val == 0) {
298 return aValue;
299 } else {
300 int res = 0;
301 for (int i = 0; i < aNumBits; i++) {
302 int mask = (int) Math.pow(2, i);
303 if ((val & mask) != 0) {
304 res += mask;
305 }
306 }
307 return (String.valueOf(res));
308 }
309 }
310
311 /**
312 * Inserts a '\' before all the escape characters ,
313 * line '\n' , '\t' to provide better readability.
314 * <p>
315 * @param aLiteral String containing the escape characters.
316 * @return the new String containing the new escape sequence
317 * of characters.
318 **/
319 public static String replaceEscapeChars(String aLiteral) {
320 StringBuffer result = new StringBuffer("");
321 for (int i = 0 ; i < aLiteral.length() ; i++) {
322 result.append(representChar(aLiteral.charAt(i)));
323 }
324 return result.toString();
325 }
326
327 /**
328 * Returns the String representation of the character .
329 * @param aChar - Character.
330 * @return the new String representing the character.
331 **/
332 private static String representChar(char aChar) {
333 switch(aChar) {
334 case '\n': return "\\n";
335 case '\t': return "\\t";
336 case '\\': return "\\\\";
337 case '"': return "\\" + "\"";
338 default: return String.valueOf(aChar);
339 }
340 }
341
342
343
344 /**
345 * Checks for the version compatibility between the system JRE
346 * and the JRE for which the application is written for.
347 * @return true , if System JRE is >= DEFAULT_VERISON ( 1.2.2 ).<br>
348 * false , otherwise.
349 **/
350 public static boolean versionCheck() {
351 String version = System.getProperty("java.version");
352 for (int i = 0 ; i <= 5 ; i += 2) {
353 int versionVal = (int) (version.charAt(i));
354 int workingVal = (int) (DEFAULT_VERSION.charAt(i));
355 if (versionVal > workingVal) {
356 return true;
357 } else if (versionVal < workingVal) {
358 System.err.println(
359 "This Software is designed to run under "
360 + DEFAULT_VERSION);
361 System.err.println("Please upgrade your JRE"
362 + " from http://java.sun.com/products/j2se"
363 + " for your operating system");
364 return false;
365 }
366 }
367 return true;
368 }
369
370
371 /**
372 * Checks if the given datatype is a basic data type or not.
373 * @param type the datatype to be checked.
374 * @return true , if it is.
375 * false , otherwise.
376 **/
377 public static boolean isBasicType(String type) {
378 return (type.equals(KeyWords.INT)
379 || type.equals(KeyWords.BOOLEAN)
380 || type.equals(KeyWords.BYTE)
381 || type.equals(KeyWords.CHAR)
382 || type.equals(KeyWords.SHORT)
383 || type.equals(KeyWords.FLOAT)
384 || type.equals(KeyWords.LONG)
385 || type.equals(KeyWords.DOUBLE));
386 }
387
388 /**
389 * Both boolean and char are represented as integers .
390 * This takes care of the conversions
391 * @param value Old Value.
392 * @param datatype Datatype of the value.
393 * @return Returns the new value after making appropriate
394 * changes.
395 **/
396 public static String getValue(String value, String datatype) {
397 if (datatype == null) {
398 return value;
399 }
400 int lastQIndex = value.lastIndexOf('?');
401 int lastColonIndex = value.lastIndexOf(":");
402
403 if (lastQIndex == -1
404 || lastColonIndex == -1
405 || lastQIndex > lastColonIndex) {
406 return getAtomicValue(value, datatype);
407 }
408 String condition = value.substring(0, lastQIndex);
409 String val1 = value.substring(lastQIndex + 1, lastColonIndex);
410 String val2 = value.substring(lastColonIndex + 1);
411
412 StringBuffer result = new StringBuffer(condition);
413 result.append("? " + getAtomicValue(val1, datatype));
414 result.append(": " + getAtomicValue(val2, datatype));
415
416 return result.toString();
417 }
418
419
420 /**
421 * Both boolean and char are represented as integers .
422 * This takes care of the conversions
423 * @param value Old Value.
424 * @param datatype Datatype of the value.
425 * @return Returns the new value after making appropriate
426 * changes.
427 **/
428 private static String getAtomicValue(String value , String datatype) {
429 value = value.trim();
430 if (datatype.equals(JVM_BOOLEAN)) { //boolean
431 if (value.compareTo("1") == 0) {
432 return TRUE;
433 } else if (value.compareTo("0") == 0) {
434 return FALSE;
435 } else {
436 return value;
437 }
438 } else if (datatype.equals(JVM_CHAR)) { //Character
439 try {
440 StringBuffer sb = new StringBuffer("");
441 int intvalue = Integer.parseInt(value);
442 sb.append("'" + (char) intvalue + "'");
443 return sb.toString();
444 } catch (NumberFormatException _ex) {
445 return value;
446 }
447 }
448 return value;
449 }
450
451 /**
452 * Converts a signed 'byte' to an unsigned integer.
453 * <p>
454 * @param aByteVal a Byte Value.
455 * @return unsigned integer equivalent of aByteVal.
456 **/
457 public static int signedToUnsigned(int aByteVal) {
458 return (aByteVal < 0) ? (aByteVal += 256) : aByteVal;
459 }
460 }