001: /**
002: * YGuard -- an obfuscation library for Java(TM) classfiles.
003: *
004: * Original Copyright (c) 1999 Mark Welsh (markw@retrologic.com)
005: * Modifications Copyright (c) 2002 yWorks GmbH (yguard@yworks.com)
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this library; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020: *
021: * The author may be contacted at yguard@yworks.com
022: *
023: * Java and all Java-based marks are trademarks or registered
024: * trademarks of Sun Microsystems, Inc. in the U.S. and other countries.
025: */package com.yworks.yguard.obf;
026:
027: import java.io.*;
028: import java.util.*;
029: import java.util.zip.*;
030: import java.util.jar.*;
031: import java.security.*;
032: import java.lang.reflect.Modifier;
033:
034: import com.yworks.yguard.*;
035: import com.yworks.yguard.obf.classfile.*;
036: import java.util.AbstractMap;
037: import java.util.jar.Attributes;
038: import java.util.jar.Manifest;
039:
040: /**
041: * Classfile database for obfuscation.
042: *
043: * @author Mark Welsh
044: */
045: public class GuardDB implements ClassConstants {
046: // Constants -------------------------------------------------------------
047: private static final String STREAM_NAME_MANIFEST = "META-INF/MANIFEST.MF";
048: private static final String MANIFEST_NAME_TAG = "Name";
049: private static final String MANIFEST_DIGESTALG_TAG = "Digest-Algorithms";
050: private static final String CLASS_EXT = ".class";
051: private static final String SIGNATURE_PREFIX = "META-INF/";
052: private static final String SIGNATURE_EXT = ".SF";
053: private static final String LOG_MEMORY_USED = " Memory in use after class data structure built: ";
054: private static final String LOG_MEMORY_TOTAL = " Total memory available : ";
055: private static final String LOG_MEMORY_BYTES = " bytes";
056: private static final String WARNING_SCRIPT_ENTRY_ABSENT = "<!-- WARNING - identifier from script file not found in JAR: ";
057: private static final String ERROR_CORRUPT_CLASS = "<!-- ERROR - corrupt class file: ";
058:
059: // Fields ----------------------------------------------------------------
060: private JarFile[] inJar; // JAR file for obfuscation
061: private Manifest[] oldManifest; // MANIFEST.MF
062: private Manifest[] newManifest; // MANIFEST.MF
063: private ClassTree classTree; // Tree of packages, classes. methods, fields
064: private boolean hasMap = false;
065:
066: /** Utility field holding list of Listeners. */
067: private transient java.util.ArrayList listenerList;
068:
069: /** Holds value of property replaceClassNameStrings. */
070: private boolean replaceClassNameStrings;
071:
072: /** Holds value of property pedantic. */
073: private boolean pedantic;
074:
075: private ResourceHandler resourceHandler;
076: private String[] digestStrings;
077:
078: // Has the mapping been generated already?
079:
080: // Class Methods ---------------------------------------------------------
081:
082: // Instance Methods ------------------------------------------------------
083: /** A classfile database for obfuscation. */
084: public GuardDB(File[] inFile) throws java.io.IOException {
085: inJar = new JarFile[inFile.length];
086: for (int i = 0; i < inFile.length; i++)
087: inJar[i] = new JarFile(inFile[i]);
088: }
089:
090: /** Close input JAR file and log-file at GC-time. */
091: protected void finalize() throws java.io.IOException {
092: close();
093: }
094:
095: public void setResourceHandler(ResourceHandler handler) {
096: resourceHandler = handler;
097: }
098:
099: public String getOutName(String inName) {
100: return classTree.getOutName(inName);
101: }
102:
103: /**
104: * Go through database marking certain entities for retention, while
105: * maintaining polymorphic integrity.
106: */
107: public void retain(Collection rgsEntries, PrintWriter log)
108: throws java.io.IOException {
109:
110: // Build database if not already done, or if a mapping has already been generated
111: if (classTree == null || hasMap) {
112: hasMap = false;
113: buildClassTree(log);
114: }
115:
116: // Enumerate the entries in the RGS script
117: for (Iterator it = rgsEntries.iterator(); it.hasNext();) {
118: YGuardRule entry = (YGuardRule) it.next();
119: try {
120: switch (entry.type) {
121: case YGuardRule.TYPE_LINE_NUMBER_MAPPER:
122: classTree.retainLineNumberTable(entry.name,
123: entry.lineNumberTableMapper);
124: break;
125: case YGuardRule.TYPE_SOURCE_ATTRIBUTE_MAP:
126: classTree.retainSourceFileAttributeMap(entry.name,
127: entry.obfName);
128: break;
129: case YGuardRule.TYPE_ATTR:
130: classTree.retainAttribute(entry.name);
131: break;
132: case YGuardRule.TYPE_ATTR2:
133: classTree.retainAttributeForClass(entry.descriptor,
134: entry.name);
135: break;
136: case YGuardRule.TYPE_CLASS:
137: classTree.retainClass(entry.name,
138: entry.retainClasses, entry.retainMethods,
139: entry.retainFields, true);
140: break;
141: case YGuardRule.TYPE_METHOD:
142: classTree
143: .retainMethod(entry.name, entry.descriptor);
144: break;
145: case YGuardRule.TYPE_PACKAGE:
146: classTree.retainPackage(entry.name);
147: break;
148: case YGuardRule.TYPE_FIELD:
149: classTree.retainField(entry.name);
150: break;
151: case YGuardRule.TYPE_PACKAGE_MAP:
152: classTree.retainPackageMap(entry.name,
153: entry.obfName);
154: break;
155: case YGuardRule.TYPE_CLASS_MAP:
156: classTree.retainClassMap(entry.name, entry.obfName);
157: break;
158: case YGuardRule.TYPE_METHOD_MAP:
159: classTree.retainMethodMap(entry.name,
160: entry.descriptor, entry.obfName);
161: break;
162: case YGuardRule.TYPE_FIELD_MAP:
163: classTree.retainFieldMap(entry.name, entry.obfName);
164: break;
165: default:
166: throw new ParseException("Illegal type: "
167: + entry.type);
168: }
169: } catch (RuntimeException e) {
170: // DEBUG
171: // e.printStackTrace();
172: log.println(WARNING_SCRIPT_ENTRY_ABSENT + entry.name
173: + " -->");
174: }
175: }
176: }
177:
178: /** Remap each class based on the remap database, and remove attributes. */
179: public void remapTo(File[] out, Filter fileFilter, PrintWriter log,
180: boolean conserveManifest) throws java.io.IOException,
181: ClassNotFoundException {
182: // Build database if not already done
183: if (classTree == null) {
184: buildClassTree(log);
185: }
186:
187: // Generate map table if not already done
188: if (!hasMap) {
189: createMap(log);
190: }
191:
192: oldManifest = new Manifest[out.length];
193: newManifest = new Manifest[out.length];
194: parseManifest();
195:
196: StringBuffer replaceNameLog = new StringBuffer();
197: StringBuffer replaceContentsLog = new StringBuffer();
198:
199: JarOutputStream outJar = null;
200: // Open the entry and prepare to process it
201: DataInputStream inStream = null;
202: OutputStream os = null;
203: for (int i = 0; i < inJar.length; i++) {
204: os = null;
205: outJar = null;
206: //store the whole jar in memory, I known this might be alot, but anyway
207: //this is the best option, if you want to create correct jar files...
208: List jarEntries = new ArrayList();
209: try {
210: // Go through the input Jar, removing attributes and remapping the Constant Pool
211: // for each class file. Other files are copied through unchanged, except for manifest
212: // and any signature files - these are deleted and the manifest is regenerated.
213: Enumeration entries = inJar[i].entries();
214: fireObfuscatingJar(inJar[i].getName(), out[i].getName());
215: ByteArrayOutputStream baos = new ByteArrayOutputStream(
216: 2048);
217: while (entries.hasMoreElements()) {
218: // Get the next entry from the input Jar
219: JarEntry inEntry = (JarEntry) entries.nextElement();
220:
221: // Ignore directories
222: if (inEntry.isDirectory()) {
223: continue;
224: }
225:
226: inStream = new DataInputStream(
227: new BufferedInputStream(inJar[i]
228: .getInputStream(inEntry)));
229: String inName = inEntry.getName();
230: if (inName.endsWith(CLASS_EXT)) {
231: if (fileFilter == null
232: || fileFilter.accepts(inName)) {
233: // Write the obfuscated version of the class to the output Jar
234: ClassFile cf = ClassFile.create(inStream);
235: fireObfuscatingClass(Conversion
236: .toJavaClass(cf.getName()));
237: cf.remap(classTree,
238: replaceClassNameStrings, log);
239: JarEntry outEntry = new JarEntry(cf
240: .getName()
241: + CLASS_EXT);
242:
243: DataOutputStream classOutputStream;
244: MessageDigest[] digests;
245: if (digestStrings == null) {
246: digestStrings = new String[] { "SHA-1",
247: "MD5" };
248: }
249: digests = new MessageDigest[digestStrings.length];
250: OutputStream stream = baos;
251: // Create an OutputStream piped through a number of digest generators for the manifest
252:
253: for (int j = 0; j < digestStrings.length; j++) {
254: String digestString = digestStrings[j];
255: MessageDigest digest = MessageDigest
256: .getInstance(digestString);
257: digests[j] = digest;
258: stream = new DigestOutputStream(stream,
259: digest);
260: }
261: classOutputStream = new DataOutputStream(
262: stream);
263:
264: // Dump the classfile, while creating the digests
265: cf.write(classOutputStream);
266: classOutputStream.flush();
267: jarEntries.add(new Object[] { outEntry,
268: baos.toByteArray() });
269: baos.reset();
270: // Now update the manifest entry for the class with new name and new digests
271: updateManifest(i, inName, cf.getName()
272: + CLASS_EXT, digests);
273: }
274: } else if (STREAM_NAME_MANIFEST.equals(inName
275: .toUpperCase())
276: || (inName.length() > (SIGNATURE_PREFIX
277: .length() + 1 + SIGNATURE_EXT
278: .length())
279: && inName.indexOf(SIGNATURE_PREFIX) != -1 && inName
280: .substring(
281: inName.length()
282: - SIGNATURE_EXT
283: .length(),
284: inName.length()).equals(
285: SIGNATURE_EXT))) {
286: // Don't pass through the manifest or signature files
287: continue;
288: } else {
289: // Copy the non-class entry through unchanged
290: long size = inEntry.getSize();
291: if (size != -1) {
292:
293: // Create an OutputStream piped through a number of digest generators for the manifest
294: MessageDigest shaDigest = MessageDigest
295: .getInstance("SHA");
296: MessageDigest md5Digest = MessageDigest
297: .getInstance("MD5");
298: DataOutputStream dataOutputStream = new DataOutputStream(
299: new DigestOutputStream(
300: new DigestOutputStream(
301: baos, shaDigest),
302: md5Digest));
303:
304: String outName;
305:
306: StringBuffer outNameBuffer = new StringBuffer(
307: 80);
308:
309: if (resourceHandler != null
310: && resourceHandler.filterName(
311: inName, outNameBuffer)) {
312: outName = outNameBuffer.toString();
313: if (!outName.equals(inName)) {
314: replaceNameLog
315: .append(" <resource name=\"");
316: replaceNameLog.append(ClassTree
317: .toUtf8XmlString(inName));
318: replaceNameLog.append("\" map=\"");
319: replaceNameLog.append(ClassTree
320: .toUtf8XmlString(outName));
321: replaceNameLog.append("\"/>\n");
322: }
323: } else {
324: outName = classTree.getOutName(inName);
325: }
326:
327: if (resourceHandler == null
328: || !resourceHandler.filterContent(
329: inStream, dataOutputStream,
330: inName)) {
331: byte[] bytes = new byte[(int) size];
332: inStream.readFully(bytes);
333:
334: // outName = classTree.getOutName(inName);
335: // Dump the data, while creating the digests
336: dataOutputStream.write(bytes, 0,
337: bytes.length);
338: } else {
339: replaceContentsLog
340: .append(" <resource name=\"");
341: replaceContentsLog.append(ClassTree
342: .toUtf8XmlString(inName));
343: replaceContentsLog.append("\"/>\n");
344: }
345:
346: dataOutputStream.flush();
347: JarEntry outEntry = new JarEntry(outName);
348:
349: jarEntries.add(new Object[] { outEntry,
350: baos.toByteArray() });
351: baos.reset();
352: // Now update the manifest entry for the entry with new name and new digests
353: MessageDigest[] digests = { shaDigest,
354: md5Digest };
355: updateManifest(i, inName, outName, digests);
356: }
357: }
358: }
359:
360: os = new FileOutputStream(out[i]);
361: if (conserveManifest) {
362: outJar = new JarOutputStream(
363: new BufferedOutputStream(os),
364: oldManifest[i]);
365: } else {
366: outJar = new JarOutputStream(
367: new BufferedOutputStream(os),
368: newManifest[i]);
369: }
370: outJar.setComment(Version.getJarComment());
371:
372: // sort the entries in ascending order
373: Collections.sort(jarEntries, new Comparator() {
374: public int compare(Object a, Object b) {
375: Object[] array1 = (Object[]) a;
376: JarEntry entry1 = (JarEntry) array1[0];
377: Object[] array2 = (Object[]) b;
378: JarEntry entry2 = (JarEntry) array2[0];
379: return entry1.getName().compareTo(
380: entry2.getName());
381: }
382: });
383: // Finally, write the big bunch of data
384: Set directoriesWritten = new HashSet();
385: for (int j = 0; j < jarEntries.size(); j++) {
386: Object[] array = (Object[]) jarEntries.get(j);
387: JarEntry entry = (JarEntry) array[0];
388: String name = entry.getName();
389: // make sure the directory entries are written to the jar file
390: if (!entry.isDirectory()) {
391: int index = 0;
392: while ((index = name.indexOf("/", index + 1)) >= 0) {
393: String directory = name.substring(0,
394: index + 1);
395: if (!directoriesWritten.contains(directory)) {
396: directoriesWritten.add(directory);
397: JarEntry directoryEntry = new JarEntry(
398: directory);
399: outJar.putNextEntry(directoryEntry);
400: outJar.closeEntry();
401: }
402: }
403: }
404: // write the entry itself
405: byte[] bytes = (byte[]) array[1];
406: outJar.putNextEntry(entry);
407: outJar.write(bytes);
408: outJar.closeEntry();
409: }
410:
411: } catch (Exception e) {
412: // Log exceptions before exiting
413: log.println();
414: log.println("<!-- An exception has occured.");
415: if (e instanceof java.util.zip.ZipException) {
416: log
417: .println("This is most likely due to a duplicate .class file in your jar!");
418: log
419: .println("Please check that there are no out-of-date or backup duplicate .class files in your jar!");
420: }
421: log.println(e.toString());
422: e.printStackTrace(log);
423: log.println("-->");
424: throw new IOException(
425: "An error ('"
426: + e.getMessage()
427: + "') occured during the remapping! See the log!)");
428: } finally {
429: inJar[i].close();
430: if (inStream != null) {
431: inStream.close();
432: }
433: if (outJar != null) {
434: outJar.close();
435: }
436: if (os != null) {
437: os.close();
438: }
439: }
440: }
441: // Write the mapping table to the log file
442: classTree.dump(log);
443: if (replaceContentsLog.length() > 0
444: || replaceNameLog.length() > 0) {
445: log.println("<!--");
446: if (replaceNameLog.length() > 0) {
447: log.println("\n<adjust replaceName=\"true\">");
448: log.print(replaceNameLog);
449: log.println("</adjust>");
450: }
451: if (replaceContentsLog.length() > 0) {
452: log.println("\n<adjust replaceContents=\"true\">");
453: log.print(replaceContentsLog);
454: log.println("</adjust>");
455: }
456: log.println("-->");
457: }
458:
459: }
460:
461: /** Close input JAR file. */
462: public void close() throws java.io.IOException {
463: for (int i = 0; i < inJar.length; i++) {
464: if (inJar[i] != null) {
465: inJar[i].close();
466: inJar[i] = null;
467: }
468: }
469: }
470:
471: // Parse the RFC822-style MANIFEST.MF file
472: private void parseManifest() throws java.io.IOException {
473: for (int i = 0; i < oldManifest.length; i++) {
474: // The manifest file is the first in the jar and is called
475: // (case insensitively) 'MANIFEST.MF'
476: oldManifest[i] = inJar[i].getManifest();
477:
478: if (oldManifest[i] == null) {
479: oldManifest[i] = new Manifest();
480: }
481:
482: // Create a fresh manifest, with a version header
483: newManifest[i] = new Manifest();
484:
485: // copy all main attributes
486: for (Iterator it = oldManifest[i].getMainAttributes()
487: .entrySet().iterator(); it.hasNext();) {
488: Map.Entry entry = (Map.Entry) it.next();
489: Attributes.Name name = (Attributes.Name) entry.getKey();
490: String value = (String) entry.getValue();
491: if (resourceHandler != null) {
492: name = new Attributes.Name(resourceHandler
493: .filterString(name.toString(),
494: "META-INF/MANIFEST.MF"));
495: value = resourceHandler.filterString(value,
496: "META-INF/MANIFEST.MF");
497: }
498: newManifest[i].getMainAttributes().putValue(
499: name.toString(), value);
500: }
501:
502: newManifest[i].getMainAttributes().putValue(
503: "Created-by",
504: "yGuard Bytecode Obfuscator "
505: + Version.getVersion());
506:
507: // copy all directory entries
508: for (Iterator it = oldManifest[i].getEntries().entrySet()
509: .iterator(); it.hasNext();) {
510: Map.Entry entry = (Map.Entry) it.next();
511: String name = (String) entry.getKey();
512: if (name.endsWith("/")) {
513: newManifest[i].getEntries().put(name,
514: (Attributes) entry.getValue());
515: }
516: }
517: }
518: }
519:
520: // Update an entry in the manifest file
521: private void updateManifest(int manifestIndex, String inName,
522: String outName, MessageDigest[] digests) {
523: // Create fresh section for entry, and enter "Name" header
524:
525: Manifest nm = newManifest[manifestIndex];
526: Manifest om = oldManifest[manifestIndex];
527:
528: Attributes oldAtts = om.getAttributes(inName);
529: Attributes newAtts = new Attributes();
530: //newAtts.putValue(MANIFEST_NAME_TAG, outName);
531:
532: // copy over non-name and none digest entries
533: if (oldAtts != null) {
534: for (Iterator it = oldAtts.entrySet().iterator(); it
535: .hasNext();) {
536: Map.Entry entry = (Map.Entry) it.next();
537: Object key = entry.getKey();
538: String name = key.toString();
539: if (!name.equalsIgnoreCase(MANIFEST_NAME_TAG)
540: && name.indexOf("Digest") == -1) {
541: newAtts.remove(name);
542: newAtts.putValue(name, (String) entry.getValue());
543: }
544: }
545: }
546:
547: // Create fresh digest entries in the new section
548: if (digests != null && digests.length > 0) {
549: // Digest-Algorithms header
550: StringBuffer sb = new StringBuffer();
551: for (int i = 0; i < digests.length; i++) {
552: sb.append(digests[i].getAlgorithm());
553: if (i < digests.length - 1) {
554: sb.append(", ");
555: }
556: }
557: newAtts.remove(MANIFEST_DIGESTALG_TAG);
558: newAtts.putValue(MANIFEST_DIGESTALG_TAG, sb.toString());
559:
560: // *-Digest headers
561: for (int i = 0; i < digests.length; i++) {
562: newAtts.remove(digests[i].getAlgorithm() + "-Digest");
563: newAtts.putValue(digests[i].getAlgorithm() + "-Digest",
564: Tools.toBase64(digests[i].digest()));
565: }
566: }
567:
568: if (!newAtts.isEmpty()) {
569: // Append the new section to the new manifest
570: nm.getEntries().put(outName, newAtts);
571: }
572: }
573:
574: // Create a classfile database.
575: private void buildClassTree(PrintWriter log)
576: throws java.io.IOException {
577: // Go through the input Jar, adding each class file to the database
578: classTree = new ClassTree();
579: classTree.setPedantic(isPedantic());
580: classTree.setReplaceClassNameStrings(replaceClassNameStrings);
581: ClassFile.resetDangerHeader();
582:
583: Map parsedClasses = new HashMap();
584: for (int i = 0; i < inJar.length; i++) {
585: Enumeration entries = inJar[i].entries();
586: fireParsingJar(inJar[i].getName());
587: while (entries.hasMoreElements()) {
588: // Get the next entry from the input Jar
589: ZipEntry inEntry = (ZipEntry) entries.nextElement();
590: String name = inEntry.getName();
591: if (name.endsWith(CLASS_EXT)) {
592: fireParsingClass(Conversion.toJavaClass(name));
593: // Create a full internal representation of the class file
594: DataInputStream inStream = new DataInputStream(
595: new BufferedInputStream(inJar[i]
596: .getInputStream(inEntry)));
597: ClassFile cf = null;
598: try {
599: cf = ClassFile.create(inStream);
600: } catch (Exception e) {
601: log.println(ERROR_CORRUPT_CLASS
602: + createJarName(inJar[i], name)
603: + " -->");
604: e.printStackTrace(log);
605: throw new ParseException(e);
606: } finally {
607: inStream.close();
608: }
609:
610: if (cf != null) {
611: Object[] old = (Object[]) parsedClasses.get(cf
612: .getName());
613: if (old != null) {
614: int jarIndex = ((Integer) old[0])
615: .intValue();
616: String warning = "yGuard detected a duplicate class definition "
617: + "for \n "
618: + Conversion.toJavaClass(cf
619: .getName())
620: + "\n ["
621: + createJarName(inJar[jarIndex],
622: old[1].toString())
623: + "] in \n ["
624: + createJarName(inJar[i], name)
625: + "]";
626: log.write("<!-- \n" + warning + "\n-->\n");
627: if (jarIndex == i) {
628: throw new IOException(
629: warning
630: + "\nPlease remove inappropriate duplicates first!");
631: } else {
632: if (pedantic) {
633: throw new IOException(
634: warning
635: + "\nMake sure these files are of the same version!");
636: }
637: }
638: } else {
639: parsedClasses
640: .put(cf.getName(), new Object[] {
641: new Integer(i), name });
642: }
643:
644: // Check the classfile for references to 'dangerous' methods
645: cf.logDangerousMethods(log,
646: replaceClassNameStrings);
647: classTree.addClassFile(cf);
648: }
649:
650: }
651: }
652: }
653:
654: // set the java access modifiers from the containing class (muellese)
655: final ClassTree ct = classTree;
656: ct.walkTree(new TreeAction() {
657: public void classAction(Cl cl) {
658: if (cl.isInnerClass()) {
659: Cl parent = (Cl) cl.getParent();
660: cl.access = parent.getInnerClassModifier(cl
661: .getInName());
662: }
663: }
664: });
665: }
666:
667: private static String createJarName(JarFile jar, String name) {
668: return "jar:" + jar.getName() + "|" + name;
669: }
670:
671: // Generate a mapping table for obfuscation.
672: private void createMap(PrintWriter log)
673: throws ClassNotFoundException {
674: // Traverse the class tree, generating obfuscated names within
675: // package and class namespaces
676: classTree.generateNames();
677:
678: // Resolve the polymorphic dependencies of each class, generating
679: // non-private method and field names for each namespace
680: classTree.resolveClasses();
681:
682: // Signal that the namespace maps have been created
683: hasMap = true;
684:
685: // Write the memory usage at this point to the log file
686: Runtime rt = Runtime.getRuntime();
687: rt.gc();
688: log.println("<!--");
689: log.println(LOG_MEMORY_USED
690: + Long.toString(rt.totalMemory() - rt.freeMemory())
691: + LOG_MEMORY_BYTES);
692: log.println(LOG_MEMORY_TOTAL + Long.toString(rt.totalMemory())
693: + LOG_MEMORY_BYTES);
694: log.println("-->");
695:
696: }
697:
698: protected void fireParsingJar(String jar) {
699: if (listenerList == null)
700: return;
701: for (int i = 0, j = listenerList.size(); i < j; i++) {
702: ((ObfuscationListener) listenerList.get(i)).parsingJar(jar);
703: }
704: }
705:
706: protected void fireParsingClass(String className) {
707: if (listenerList == null)
708: return;
709: for (int i = 0, j = listenerList.size(); i < j; i++) {
710: ((ObfuscationListener) listenerList.get(i))
711: .parsingClass(className);
712: }
713: }
714:
715: protected void fireObfuscatingJar(String inJar, String outJar) {
716: if (listenerList == null)
717: return;
718: for (int i = 0, j = listenerList.size(); i < j; i++) {
719: ((ObfuscationListener) listenerList.get(i)).obfuscatingJar(
720: inJar, outJar);
721: }
722: }
723:
724: protected void fireObfuscatingClass(String className) {
725: if (listenerList == null)
726: return;
727: for (int i = 0, j = listenerList.size(); i < j; i++) {
728: ((ObfuscationListener) listenerList.get(i))
729: .obfuscatingClass(className);
730: }
731: }
732:
733: /** Registers Listener to receive events.
734: * @param listener The listener to register.
735: */
736: public synchronized void addListener(
737: com.yworks.yguard.ObfuscationListener listener) {
738: if (listenerList == null) {
739: listenerList = new java.util.ArrayList();
740: }
741: listenerList.add(listener);
742: }
743:
744: /** Removes Listener from the list of listeners.
745: * @param listener The listener to remove.
746: */
747: public synchronized void removeListener(
748: com.yworks.yguard.ObfuscationListener listener) {
749: if (listenerList != null) {
750: listenerList.remove(listener);
751: }
752: }
753:
754: /** Getter for property replaceClassNameStrings.
755: * @return Value of property replaceClassNameStrings.
756: *
757: */
758: public boolean isReplaceClassNameStrings() {
759: return this .replaceClassNameStrings;
760: }
761:
762: /** Setter for property replaceClassNameStrings.
763: * @param replaceClassNameStrings New value of property replaceClassNameStrings.
764: *
765: */
766: public void setReplaceClassNameStrings(
767: boolean replaceClassNameStrings) {
768: this .replaceClassNameStrings = replaceClassNameStrings;
769: }
770:
771: /** Getter for property pedantic.
772: * @return Value of property pedantic.
773: *
774: */
775: public boolean isPedantic() {
776: return this .pedantic;
777: }
778:
779: /** Setter for property pedantic.
780: * @param pedantic New value of property pedantic.
781: *
782: */
783: public void setPedantic(boolean pedantic) {
784: this .pedantic = pedantic;
785: Cl.setPedantic(pedantic);
786: }
787:
788: /**
789: * Returns the obfuscated file name of the java class.
790: * The ending ".class" is omitted.
791: * @param javaClass the fully qualified name of an unobfuscated class.
792: */
793: public String translateJavaFile(String javaClass) {
794: Cl cl = classTree.findClassForName(javaClass.replace('/', '.'));
795: if (cl != null) {
796: return cl.getFullOutName();
797: } else {
798: return javaClass;
799: }
800: }
801:
802: public String translateJavaClass(String javaClass) {
803: Cl cl = classTree.findClassForName(javaClass);
804: if (cl != null) {
805: return cl.getFullOutName().replace('/', '.');
806: } else {
807: return javaClass;
808: }
809: }
810:
811: public void setDigests(String[] digestStrings) {
812: this.digestStrings = digestStrings;
813: }
814: }
|