0001: /**
0002: * JavaGuard -- an obfuscation package for Java classfiles.
0003: *
0004: * Copyright (c) 1999 Mark Welsh (markw@retrologic.com)
0005: * Copyright (c) 2002 Thorsten Heit (theit@gmx.de)
0006: *
0007: * This library is free software; you can redistribute it and/or
0008: * modify it under the terms of the GNU Lesser General Public
0009: * License as published by the Free Software Foundation; either
0010: * version 2 of the License, or (at your option) any later version.
0011: *
0012: * This library is distributed in the hope that it will be useful,
0013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0015: * Lesser General Public License for more details.
0016: *
0017: * You should have received a copy of the GNU Lesser General Public
0018: * License along with this library; if not, write to the Free Software
0019: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0020: *
0021: * The author may be contacted at theit@gmx.de.
0022: *
0023: *
0024: * $Id: ClassTree.java,v 1.20 2002/05/28 17:26:45 glurk Exp $
0025: */package net.sf.javaguard;
0026:
0027: import java.io.*;
0028: import java.lang.reflect.*;
0029: import java.util.*;
0030: import net.sf.javaguard.classfile.*;
0031: import net.sf.javaguard.log.*;
0032:
0033: import org.apache.oro.text.regex.*;
0034:
0035: /** Tree structure of package levels, classes, methods and fields used for
0036: * obfuscation.
0037: *
0038: * @author <a href="mailto:markw@retrologic.com">Mark Welsh</a>
0039: * @author <a href="mailto:theit@gmx.de">Thorsten Heit</a>
0040: */
0041: public class ClassTree implements NameMapper {
0042: /** Package separator character. */
0043: public static final char PACKAGE_LEVEL = '/';
0044: /** Inner class separator character. */
0045: public static final char CLASS_LEVEL = '$';
0046: /** Method and field separator character. */
0047: public static final char METHOD_FIELD_LEVEL = '/';
0048:
0049: /** Holds the class path of the java.io.Serializable interface. */
0050: public static final String SERIALIZABLE_INTERFACE = "java/io/Serializable";
0051: /** Holds the class path of the java.io.Serializable interface. */
0052: public static final String SERIALIZABLE_INTERFACE_CLASS = "java.io.Serializable";
0053:
0054: /** Holds the class path of the java.rmi.Remote interface. */
0055: public static final String REMOTE_INTERFACE = "java/rmi/Remote";
0056: /** Holds the class path ofthe java.rmi.Remote interface. */
0057: public static final String REMOTE_INTERFACE_CLASS = "java.rmi.Remote";
0058: /** Holds the class path of the java.rmi.server.Skeleton interface. */
0059: public static final String SKELETON_INTERFACE = "java/rmi/server/Skeleton";
0060: /** Holds the class path of the java.rmi.server.Skeleton interface. */
0061: public static final String SKELETON_INTERFACE_CLASS = "java.rmi.server.Skeleton";
0062:
0063: /** Holds the class name suffix for stub classes generated by rmic. */
0064: private static final String STR_STUB = "_Stub";
0065: /** Holds the class name suffix for skeleton classes generated by rmic. */
0066: private static final String STR_SKEL = "_Skel";
0067:
0068: /** Stores tree items of serializable field items that may not be obfuscated. */
0069: private Vector serializableFields;
0070: /** Stores pairs (fully-qualified name, descriptor) of methods that may
0071: * not be obfuscated. */
0072: private Vector serializableMethods;
0073: /** Stores classes that implement the Remote or Skeleton interface. */
0074: private Vector remoteClasses;
0075:
0076: /** Holds classes that are used in hardcoded references. */
0077: private Set hardcodedReferences;
0078:
0079: /** Holds the compiler for regular expressions. */
0080: private PatternCompiler patternCompiler;
0081: /** Holds the regular expression matcher class. */
0082: private PatternMatcher patternMatcher;
0083:
0084: /** Holds a map of compiled default regular expressions. */
0085: private Map patternsIgnoreDefault;
0086: /** Holds a map of compiled regular expressions for package names. */
0087: private Map patternsIgnorePackage;
0088: /** Holds a map of compiled regular expressions for class names. */
0089: private Map patternsIgnoreClass;
0090: /** Holds a map of compiled regular expressions for method names. */
0091: private Map patternsIgnoreMethod;
0092: /** Holds a map of compiled regular expressions for field names. */
0093: private Map patternsIgnoreField;
0094:
0095: /** Holds a map of compiled default regular expressions. */
0096: private Map patternsObfuscateDefault;
0097: /** Holds a map of compiled regular expressions for package names. */
0098: private Map patternsObfuscatePackage;
0099: /** Holds a map of compiled regular expressions for class names. */
0100: private Map patternsObfuscateClass;
0101: /** Holds a map of compiled regular expressions for method names. */
0102: private Map patternsObfuscateMethod;
0103: /** Holds a map of compiled regular expressions for field names. */
0104: private Map patternsObfuscateField;
0105:
0106: /** Holds a list of attributes to retain. */
0107: private Vector retainAttrs = new Vector();
0108: /** Holds the root package in the database (Java default package). */
0109: private Pk root;
0110:
0111: /** Hold whether the obfuscator should respect remote classes. */
0112: private static boolean useRmicClasses = false;
0113: /** Holds whether resource file names should be obfuscated according to
0114: * their corresponding class files. */
0115: private static boolean obfuscateResources = false;
0116: /** Holds whether we should automatically prevent hardcoded class names from
0117: * being obfuscated. */
0118: private static boolean autoCorrectClassNames = false;
0119:
0120: /** Holds the log file. */
0121: private FileLogger logfile;
0122: /** Holds the logger. */
0123: private ScreenLogger logger;
0124:
0125: /** Constructor that initializes the class tree.
0126: */
0127: public ClassTree() {
0128: setRootPackage(Pk.createRoot(this ));
0129:
0130: serializableFields = new Vector();
0131: serializableMethods = new Vector();
0132: remoteClasses = new Vector();
0133:
0134: hardcodedReferences = new TreeSet();
0135:
0136: patternCompiler = new Perl5Compiler();
0137: patternMatcher = new Perl5Matcher();
0138:
0139: // Initialize the maps that store the ignore directives taken from the
0140: // script file
0141: patternsIgnoreDefault = new HashMap();
0142: patternsIgnorePackage = new HashMap();
0143: patternsIgnoreClass = new HashMap();
0144: patternsIgnoreMethod = new HashMap();
0145: patternsIgnoreField = new HashMap();
0146:
0147: // Initialize the maps that store the obfuscate directives taken from the
0148: // script file
0149: patternsObfuscateDefault = new HashMap();
0150: patternsObfuscatePackage = new HashMap();
0151: patternsObfuscateClass = new HashMap();
0152: patternsObfuscateMethod = new HashMap();
0153: patternsObfuscateField = new HashMap();
0154:
0155: logfile = FileLogger.getInstance();
0156: logger = ScreenLogger.getInstance();
0157: }
0158:
0159: /** Sets the root package level.
0160: * @param pk the root package
0161: * @see #getRootPackage
0162: */
0163: protected void setRootPackage(Pk pk) {
0164: root = pk;
0165: }
0166:
0167: /** Returns the root package.
0168: * @return root package
0169: * @see #setRootPackage
0170: */
0171: public Pk getRootPackage() {
0172: return root;
0173: }
0174:
0175: /** Defines whether or not the obfuscator should respect the names of stub
0176: * and skeleton classes generated by rmic.
0177: * @param canUse a string holding "true", "yes" or "on" if the obfuscator
0178: * should respect the names
0179: * @see #setUseRmicClasses(boolean)
0180: */
0181: public void setUseRmicClasses(String canUse) {
0182: setUseRmicClasses(parseBooleanValue(canUse));
0183: }
0184:
0185: /** Defines whether or not the obfuscator should respect the names of stub
0186: * and skeleton classes generated by rmic.
0187: * @param canUse true if the obfuscator should respect class names; false else
0188: * false else
0189: * @see #canUseRmicClasses
0190: */
0191: public void setUseRmicClasses(boolean canUse) {
0192: useRmicClasses = canUse;
0193: }
0194:
0195: /** Returns true if the obfuscator should respect stub and skeleton class
0196: * names.
0197: * @return true if the obfuscator should respect stub and skeleton class
0198: * names; false else
0199: * @see #setUseRmicClasses(boolean)
0200: */
0201: public boolean canUseRmicClasses() {
0202: return useRmicClasses;
0203: }
0204:
0205: /** Defines whether resource file names should be obfuscated according to
0206: * their corresponding class files or not.
0207: * @param obfuscate a string holding "true", "yes" or "on" if the obfuscator
0208: * rename resource files
0209: * @see #setObfuscateResources(boolean)
0210: */
0211: public void setObfuscateResources(String obfuscate) {
0212: setObfuscateResources(parseBooleanValue(obfuscate));
0213: }
0214:
0215: /** Defines whether resource file names should be obfuscated according to
0216: * their corresponding class files or not.
0217: * @param obfuscate true if resource file names should be obfuscated; false
0218: * else
0219: * @see #canObfuscateResources
0220: */
0221: public void setObfuscateResources(boolean obfuscate) {
0222: obfuscateResources = obfuscate;
0223: }
0224:
0225: /** Returns whether resource file names should be obfuscated according to
0226: * their corresponding class files or not.
0227: * @return true if resource file names should be obfuscated; false else
0228: * @see #setObfuscateResources(boolean)
0229: */
0230: public boolean canObfuscateResources() {
0231: return obfuscateResources;
0232: }
0233:
0234: /** Defines whether we should automatically prevent hardcoded class names from
0235: * being obfuscated.
0236: * @param autoCorrect a string holding "true", "yes" or "on" if the obfuscator
0237: * should prevent hardcoded class names from being obfuscated
0238: * @see #setAutoCorrectClassNames(boolean)
0239: */
0240: public void setAutoCorrectClassNames(String autoCorrect) {
0241: setAutoCorrectClassNames(parseBooleanValue(autoCorrect));
0242: }
0243:
0244: /** Defines whether we should automatically prevent hardcoded class names from
0245: * being obfuscated.
0246: * @param autoCorrect true if hardcoded class names should not be obfuscated;
0247: * false else
0248: * @see #canAutoCorrectClassNames
0249: */
0250: public void setAutoCorrectClassNames(boolean autoCorrect) {
0251: autoCorrectClassNames = autoCorrect;
0252: }
0253:
0254: /** Returns whether we prevent hardcoded class names from being obfuscated.
0255: * @return true if hardcoded class names should not be obfuscated; false else
0256: * @see #setAutoCorrectClassNames
0257: */
0258: public boolean canAutoCorrectClassNames() {
0259: return autoCorrectClassNames;
0260: }
0261:
0262: /** Parses the string and creates a boolean value from it.
0263: * @param str the string to parse
0264: * @return true if the string is "true", "yes" or "on"; false else
0265: */
0266: private boolean parseBooleanValue(String str) {
0267: if (str.equalsIgnoreCase("true") || str.equalsIgnoreCase("yes")
0268: || str.equalsIgnoreCase("on")) {
0269: return true;
0270: }
0271: return false;
0272: }
0273:
0274: /** Splits a fully qualified name into package and class name segments.
0275: * @param name fully qualified name
0276: * @return iterator that contains package/class name segments.
0277: * @throws IllegalStateException if the name is invalid
0278: */
0279: private Iterator splitName(String name)
0280: throws IllegalStateException {
0281: Vector vec = new Vector();
0282: String nameOrig = name;
0283: while (!name.equals("")) {
0284: int posP = name.indexOf(PACKAGE_LEVEL);
0285: int posC = name.indexOf(CLASS_LEVEL);
0286: KeyValue kv = null;
0287: // the current string only consists of a class name
0288: if (posP == -1 && posC == -1) {
0289: kv = new KeyValue(new Character(CLASS_LEVEL), name);
0290: name = "";
0291: }
0292: // the current string only consists of a class and a subclass name
0293: if (posP == -1 && posC != -1) {
0294: kv = new KeyValue(new Character(CLASS_LEVEL), name
0295: .substring(0, posC));
0296: name = name.substring(posC + 1, name.length());
0297: }
0298: // the current string contains a package name, but no subclass name
0299: if (posP != -1 && posC == -1) {
0300: kv = new KeyValue(new Character(PACKAGE_LEVEL), name
0301: .substring(0, posP));
0302: name = name.substring(posP + 1, name.length());
0303: }
0304: // the current string contains of both a package and a subclass name
0305: if (posP != -1 && posC != -1) {
0306: if (posP < posC) {
0307: kv = new KeyValue(new Character(PACKAGE_LEVEL),
0308: name.substring(0, posP));
0309: name = name.substring(posP + 1, name.length());
0310: } else {
0311: throw new IllegalStateException(
0312: "Invalid fully qualified name (a): "
0313: + nameOrig);
0314: }
0315: }
0316: if (((String) kv.getValue()).equals("")) {
0317: throw new IllegalStateException(
0318: "Invalid fully qualified name (b): " + nameOrig);
0319: }
0320: vec.addElement(kv);
0321: }
0322: return vec.iterator();
0323: }
0324:
0325: /** Update the path of the given filename. All package and (inner) class
0326: * names are replaced by their obfuscated output names. If the input name
0327: * specifies a resource file its output name can also be obfuscated if the
0328: * corresponding class is found in the class tree.
0329: * @param inName the file name to update
0330: * @return updated output file name
0331: */
0332: public String getOutputFileName(String inName) {
0333: boolean isClass = false;
0334: // if the file name denotes a class remove the ".class" extension
0335: if (inName.endsWith(ClassConstants.CLASS_EXT)) {
0336: isClass = true;
0337: inName = inName.substring(0, inName
0338: .lastIndexOf(ClassConstants.CLASS_EXT));
0339: }
0340:
0341: TreeItem ti = getRootPackage();
0342: StringBuffer sb = new StringBuffer();
0343: Iterator iter = splitName(inName);
0344: while (iter.hasNext()) {
0345: KeyValue nameSegment = (KeyValue) iter.next();
0346: char tag = ((Character) nameSegment.getKey()).charValue();
0347: String name = (String) nameSegment.getValue();
0348: switch (tag) {
0349: case PACKAGE_LEVEL:
0350: // try to search the child list of the current tree item for a
0351: // subpackage named <name>
0352: if (null != ti) {
0353: // find the named package in the current tree item
0354: ti = ((Pk) ti).getPackage(name);
0355: if (null != ti) {
0356: // tree item found -> append its (obfuscated) output name
0357: sb.append(ti.getOutName());
0358: } else {
0359: // tree item not found -> use the original name
0360: sb.append(name);
0361: }
0362: } else {
0363: // we don't have a tree item -> use the original name
0364: sb.append(name);
0365: }
0366: sb.append(PACKAGE_LEVEL);
0367: break;
0368:
0369: case CLASS_LEVEL:
0370: // check whether the current name belongs to a (inner) class name
0371: if (isClass
0372: || (iter.hasNext() && canObfuscateResources())) {
0373: // current name belongs to an inner class
0374: ti = ((PkCl) ti).getClass(name);
0375: if (null != ti) {
0376: // tree item found -> append its (obfuscated) output name
0377: sb.append(ti.getOutName());
0378: } else {
0379: // tree item not found -> use the original name
0380: sb.append(name);
0381: }
0382: // if there's at least one more inner class add the inner class
0383: // separator character
0384: if (iter.hasNext()) {
0385: sb.append(CLASS_LEVEL);
0386: }
0387: } else {
0388: // the remaining name belongs to a resource file
0389: if (!canObfuscateResources()) {
0390: sb.append(name);
0391: if (iter.hasNext()) {
0392: sb.append(CLASS_LEVEL);
0393: }
0394: } else {
0395: // find the (localized) class whose name equals the actual name
0396: ti = findInnerClass((PkCl) ti, name);
0397: if (null == ti) {
0398: sb.append(name);
0399: } else {
0400: // the returned tree item has the same input name as the actual
0401: // name (apart from a localized ending such as _de_DE)
0402: String suffix = name.substring(ti
0403: .getInName().length());
0404: sb.append(ti.getOutName());
0405: sb.append(suffix);
0406: }
0407: }
0408: }
0409: break;
0410:
0411: default:
0412: throw new InternalError(
0413: "Internal error: illegal package/class name tag");
0414: }
0415: }
0416: return sb.toString();
0417: }
0418:
0419: /** Tries find the inner class whose name equals the (localized) given
0420: * resource name.
0421: * @param pkcl the class to search; may be null
0422: * @param name the name of a resource file; may be null
0423: * @return the inner class whose name matches the given resource file name;
0424: * null if no class class is found or one of the input parameters is null
0425: */
0426: private Cl findInnerClass(PkCl pkcl, String name) {
0427: if (null == pkcl || null == name)
0428: return null;
0429:
0430: // the remaining name belongs to a resource file
0431: String prefix = null;
0432: String suffix = null;
0433: int pos = name.lastIndexOf('.');
0434: // extract the file name prefix and suffix
0435: if (pos > 0) {
0436: prefix = name.substring(0, pos);
0437: suffix = name.substring(pos + 1);
0438: } else if (0 == pos) {
0439: // extract the suffix only if the file name is not "."
0440: suffix = name.substring(pos + 1);
0441: } else {
0442: // we don't have a suffix -> name = prefix
0443: prefix = name;
0444: }
0445:
0446: // try to find the class that corresponds to the actual name
0447: try {
0448: while (null != prefix) {
0449: Cl cl = pkcl.getClass(prefix);
0450: if (null != cl) {
0451: return cl;
0452: }
0453: prefix = prefix.substring(0, prefix.lastIndexOf('_'));
0454: }
0455: } catch (IndexOutOfBoundsException e) {
0456: // exception only occurs if the remaining prefix isn't localized anymore
0457: }
0458:
0459: // if the given resource file name doesn't have a prefix return null
0460: return null;
0461: }
0462:
0463: /** Add a classfile's package, class, method and field entries to the internal
0464: * database.
0465: * @param cf the class file to add
0466: */
0467: public void addClassFile(ClassFile cf) {
0468: // Add the fully qualified class name
0469: TreeItem ti = getRootPackage();
0470: char parentTag = PACKAGE_LEVEL;
0471: for (Iterator iter = splitName(cf.getName()); iter.hasNext();) {
0472: KeyValue nameSegment = (KeyValue) iter.next();
0473: char tag = ((Character) nameSegment.getKey()).charValue();
0474: String name = (String) nameSegment.getValue();
0475: switch (tag) {
0476: case PACKAGE_LEVEL:
0477: // insert a new subpackage with the current name if it doesn't exist
0478: ti = ((Pk) ti).addPackage(name);
0479: break;
0480:
0481: case CLASS_LEVEL:
0482: // If this is an inner class, just add placeholder classes up the tree
0483: if (iter.hasNext()) {
0484: ti = ((PkCl) ti).addPlaceholderClass(name);
0485: } else {
0486: ti = ((PkCl) ti).addClass(name, cf.getSuper(), cf
0487: .getInterfaceNames());
0488: }
0489: break;
0490:
0491: default:
0492: throw new InternalError(
0493: "Internal error: illegal package/class name tag");
0494: }
0495: parentTag = tag;
0496: }
0497:
0498: // We must have a class before adding methods and fields
0499: if (ti instanceof Cl) {
0500: Cl cl = (Cl) ti;
0501:
0502: // Add the class's methods to the database
0503: for (int i = 0; i < cf.getMethodsCount(); i++) {
0504: MethodInfo mi = cf.getMethodInfo(i);
0505: cl.addMethod(mi.isSynthetic(), mi.getName(), mi
0506: .getDescriptor(), mi.getAccessFlags());
0507: }
0508:
0509: // Add the class's fields to the database
0510: for (int i = 0; i < cf.getFieldsCount(); i++) {
0511: FieldInfo fi = cf.getFieldInfo(i);
0512: cl.addField(fi.isSynthetic(), fi.getName(), fi
0513: .getDescriptor(), fi.getAccessFlags());
0514: }
0515: } else {
0516: throw new InternalError(
0517: "Internal error: invalid class file!");
0518: }
0519:
0520: // add the class names that are used in hardcoded references to the internal
0521: // list so we can process them later
0522: hardcodedReferences.addAll(cf.getHardcodedClassNames());
0523: }
0524:
0525: /** Mark an attribute type for retention.
0526: * @param entry the script file attribute entry to retain
0527: * @see #setUseRmicClasses(String)
0528: * @see #setUseRmicClasses(boolean)
0529: * @see #setAutoCorrectClassNames(String)
0530: * @see #setAutoCorrectClassNames(boolean)
0531: * @see #setObfuscateResources(String)
0532: * @see #setObfuscateResources(boolean)
0533: */
0534: public void retainAttribute(ScriptEntry entry) {
0535: logger.println("retaining attribute " + entry.getName());
0536:
0537: if (entry.getName().equals(ScriptConstants.RENAME_RMIC)) {
0538: // turn on/off special processing for RMIC-generated classes
0539: if (null != entry.getInfo()) {
0540: logger.log(Log.VERBOSE, "-> getInfo(): "
0541: + entry.getInfo());
0542: setUseRmicClasses(entry.getInfo());
0543: } else {
0544: setUseRmicClasses(true);
0545: }
0546: } else if (entry.getName().equals(
0547: ScriptConstants.PRESERVE_REFERENCES)) {
0548: // turn on/off autocorrection of classes used in hardcoded references
0549: if (null != entry.getInfo()) {
0550: logger.log(Log.VERBOSE, "-> getInfo(): "
0551: + entry.getInfo());
0552: setAutoCorrectClassNames(entry.getInfo());
0553: } else {
0554: setAutoCorrectClassNames(true);
0555: }
0556: } else if (entry.getName().equals(
0557: ScriptConstants.RENAME_RESOURCES)) {
0558: // turn on/off renaming of resource files
0559: if (null != entry.getInfo()) {
0560: logger.log(Log.VERBOSE, "-> getInfo(): "
0561: + entry.getInfo());
0562: setObfuscateResources(entry.getInfo());
0563: } else {
0564: setObfuscateResources(true);
0565: }
0566: } else {
0567: retainAttrs.addElement(entry.getName());
0568: }
0569: }
0570:
0571: /** Marks an item in the tree for retention.
0572: * @param ti the tree item
0573: */
0574: private void retainItem(TreeItem ti) {
0575: ti.setOutName(ti.getInName());
0576: ti.setFromScript();
0577: }
0578:
0579: /** Marks a list of items in the tree for retention.
0580: * @param iter an iterator for the items to retain
0581: * @ee #retainItem
0582: */
0583: private void retainItems(Iterator iter) {
0584: while (iter.hasNext()) {
0585: retainItem((TreeItem) iter.next());
0586: }
0587: }
0588:
0589: /** Mark an item for retention, and specify its new name.
0590: * @param ti the tree item
0591: * @param obfName the obfuscated name of the item
0592: */
0593: private void retainItemMap(TreeItem ti, String obfName) {
0594: ti.setOutName(obfName);
0595: ti.setFromScriptMap();
0596: }
0597:
0598: /** Mark a tree item and all its parents for retention.
0599: * @param ti the tree item
0600: */
0601: private void retainHierarchy(TreeItem ti) {
0602: if (null != ti) {
0603: retainItem(ti);
0604: retainHierarchy(ti.getParent());
0605: }
0606: }
0607:
0608: /** Traverse the class tree and generate obfuscated names within each
0609: * namespace.
0610: */
0611: public void generateNames() {
0612: logger.log(Log.VERBOSE, "Generate obfuscated names");
0613: walkTree(new TreeAction() {
0614: public void packageAction(Pk pk) {
0615: pk.generateNames();
0616: }
0617:
0618: public void classAction(Cl cl) {
0619: cl.generateNames();
0620: }
0621:
0622: public void methodAction(Md md) {
0623: }
0624:
0625: public void fieldAction(Fd fd) {
0626: }
0627: });
0628: }
0629:
0630: /** Resolve the polymorphic dependencies of each class.
0631: */
0632: public void resolveClasses() {
0633: logger.log(Log.VERBOSE, "Resolve polymorphic dependencies...");
0634: walkTree(new TreeAction() {
0635: public void classAction(Cl cl) {
0636: cl.resetResolve();
0637: }
0638:
0639: public void packageAction(Pk pk) {
0640: }
0641:
0642: public void methodAction(Md md) {
0643: }
0644:
0645: public void fieldAction(Fd fd) {
0646: }
0647: });
0648:
0649: walkTree(new TreeAction() {
0650: public void classAction(Cl cl) {
0651: cl.setupNameListDowns();
0652: }
0653:
0654: public void packageAction(Pk pk) {
0655: }
0656:
0657: public void methodAction(Md md) {
0658: }
0659:
0660: public void fieldAction(Fd fd) {
0661: }
0662: });
0663:
0664: Cl.nameSpace = 0;
0665: walkTree(new TreeAction() {
0666: public void classAction(Cl cl) {
0667: cl.resolveOptimally();
0668: }
0669:
0670: public void packageAction(Pk pk) {
0671: }
0672:
0673: public void methodAction(Md md) {
0674: }
0675:
0676: public void fieldAction(Fd fd) {
0677: }
0678: });
0679: }
0680:
0681: /** Return a list of attributes marked to keep.
0682: * @return array with attributes to keep
0683: */
0684: public String[] getAttrsToKeep() {
0685: String[] attrs = new String[retainAttrs.size()];
0686: for (int i = 0; i < attrs.length; i++) {
0687: attrs[i] = (String) retainAttrs.elementAt(i);
0688: }
0689: return attrs;
0690: }
0691:
0692: /** Find all packages in the tree that match a given pattern.
0693: * @param pattern a regular expression containing the search pattern
0694: * @return an iterator for all packages that match the search pattern
0695: */
0696: public Iterator findPackages(final Pattern pattern) {
0697: final Vector vec = new Vector();
0698: walkTree(new TreeAction() {
0699: public void packageAction(Pk pk) {
0700: if (patternMatcher
0701: .contains(pk.getFullInName(), pattern)
0702: || patternMatcher.contains(pk.getInName(),
0703: pattern)) {
0704: vec.addElement(pk);
0705: }
0706: }
0707:
0708: public void classAction(Cl cl) {
0709: }
0710:
0711: public void methodAction(Md md) {
0712: }
0713:
0714: public void fieldAction(Fd fd) {
0715: }
0716: });
0717: if (vec.isEmpty()) {
0718: logger.log("Warning: No package found for search pattern '"
0719: + pattern.getPattern() + "'");
0720: }
0721: return vec.iterator();
0722: }
0723:
0724: /** Find all classes in the tree that match a given pattern.
0725: * @param pattern a regular expression containing the search pattern
0726: * @return an iterator for all classes that match the search pattern
0727: */
0728: public Iterator findClasses(final Pattern pattern) {
0729: final Vector vec = new Vector();
0730: walkTree(new TreeAction() {
0731: public void classAction(Cl cl) {
0732: if (patternMatcher
0733: .contains(cl.getFullInName(), pattern)
0734: || patternMatcher.contains(cl.getInName(),
0735: pattern)) {
0736: vec.addElement(cl);
0737: }
0738: }
0739:
0740: public void packageAction(Pk pk) {
0741: }
0742:
0743: public void methodAction(Md md) {
0744: }
0745:
0746: public void fieldAction(Fd fd) {
0747: }
0748: });
0749: if (vec.isEmpty()) {
0750: logger.log("Warning: No class found for search pattern '"
0751: + pattern.getPattern() + "'");
0752: }
0753: return vec.iterator();
0754: }
0755:
0756: /** Find all methods in the tree that match a given pattern.
0757: * @param pattern a regular expression containing the search pattern
0758: * @param descr an optional descriptor; may be null
0759: * @return an iterator for all methods that match the search pattern
0760: */
0761: public Iterator findMethods(final Pattern pattern,
0762: final String descr) {
0763: final Vector vec = new Vector();
0764: walkTree(new TreeAction() {
0765: public void methodAction(Md md) {
0766: if (patternMatcher
0767: .contains(md.getFullInName(), pattern)
0768: || patternMatcher.contains(md.getInName(),
0769: pattern)) {
0770: if (null == descr
0771: || descr.equals(md.getDescriptor())) {
0772: vec.addElement(md);
0773: }
0774: }
0775: }
0776:
0777: public void packageAction(Pk pk) {
0778: }
0779:
0780: public void classAction(Cl cl) {
0781: }
0782:
0783: public void fieldAction(Fd fd) {
0784: }
0785: });
0786: if (vec.isEmpty()) {
0787: logger.log("Warning: No method found for search pattern '"
0788: + pattern.getPattern() + "'");
0789: }
0790: return vec.iterator();
0791: }
0792:
0793: /** Find all methods in the tree that match a given pattern.
0794: * @param pattern a regular expression containing the search pattern
0795: * @param descr an optional descriptor; may be null
0796: * @return an iterator for all methods that match the search pattern
0797: */
0798: public Iterator findFields(final Pattern pattern, final String descr) {
0799: final Vector vec = new Vector();
0800: walkTree(new TreeAction() {
0801: public void fieldAction(Fd fd) {
0802: if (patternMatcher
0803: .contains(fd.getFullInName(), pattern)
0804: || patternMatcher.contains(fd.getInName(),
0805: pattern)) {
0806: if (null == descr
0807: || descr.equals(fd.getDescriptor())) {
0808: vec.addElement(fd);
0809: }
0810: }
0811: }
0812:
0813: public void packageAction(Pk pk) {
0814: }
0815:
0816: public void classAction(Cl cl) {
0817: }
0818:
0819: public void methodAction(Md md) {
0820: }
0821: });
0822: if (vec.isEmpty()) {
0823: logger.log("Warning: No field found for search pattern '"
0824: + pattern.getPattern() + "'");
0825: }
0826: return vec.iterator();
0827: }
0828:
0829: /** Get a class in the tree from the fully qualified name, returning null if
0830: * the name is not found.
0831: * @param fullName fully qualified class name
0832: * @return class stored in the tree; null if the name is not found.
0833: * @throws IllegalStateException if an error exists inside the tree
0834: */
0835: public Cl getCl(String fullName) throws IllegalStateException {
0836: TreeItem ti = getRootPackage();
0837: for (Iterator iter = splitName(fullName); iter.hasNext();) {
0838: KeyValue nameSegment = (KeyValue) iter.next();
0839: char tag = ((Character) nameSegment.getKey()).charValue();
0840: String name = (String) nameSegment.getValue();
0841: switch (tag) {
0842: case PACKAGE_LEVEL:
0843: ti = ((Pk) ti).getPackage(name);
0844: break;
0845:
0846: case CLASS_LEVEL:
0847: ti = ((PkCl) ti).getClass(name);
0848: break;
0849:
0850: default:
0851: throw new IllegalStateException(
0852: "Internal error: illegal package/class name tag");
0853: }
0854:
0855: // If the name is not in the database, return null
0856: if (ti == null) {
0857: return null;
0858: }
0859: }
0860:
0861: // It is an error if we do not end up with a class or interface
0862: if (!(ti instanceof Cl)) {
0863: throw new IllegalStateException(
0864: "Inconsistent class or interface name.");
0865: }
0866: return (Cl) ti;
0867: }
0868:
0869: /** Find a class in the tree from the fully qualified name. If the class isn't
0870: * found directly try to check whether it is an inner class.
0871: * the name is not found.
0872: * @param fullName fully qualified class name
0873: * @return class stored in the tree; null if the name is not found.
0874: * @throws IllegalStateException if an error exists inside the tree
0875: * @see #getCl
0876: */
0877: public Cl findClass(String fullName) throws IllegalStateException {
0878: // first replace all '.' separator characters by the one used in the
0879: // internal representation
0880: fullName = fullName.replace('.', ClassFile.SEP_REGULAR
0881: .charAt(0));
0882: Cl cl = getCl(fullName);
0883: // if we cannot find the item check whether it's an inner class
0884: if (null == cl) {
0885: int pos;
0886: String str = fullName;
0887: while (null == cl
0888: && (pos = str.lastIndexOf(ClassFile.SEP_REGULAR)) >= 0) {
0889: str = str.substring(0, pos) + ClassFile.SEP_INNER
0890: + str.substring(pos + 1);
0891: cl = getCl(str);
0892: }
0893: }
0894: return cl;
0895: }
0896:
0897: /** Get a package in tree from the fully qualified name.
0898: * @param fullName the fully qualified name of the package
0899: * @return package in the tree; null if name not found.
0900: * @throws IllegalStateException if an error exists inside the tree
0901: */
0902: public Pk getPk(String fullName) throws IllegalStateException {
0903: TreeItem ti = getRootPackage();
0904: for (Iterator iter = splitName(fullName); iter.hasNext();) {
0905: KeyValue nameSegment = (KeyValue) iter.next();
0906: String name = (String) nameSegment.getValue();
0907: ti = ((Pk) ti).getPackage(name);
0908:
0909: // If the name is not in the database, return null
0910: if (ti == null) {
0911: return null;
0912: }
0913: // It is an error if we do not end up with a package
0914: if (!(ti instanceof Pk)) {
0915: throw new IllegalStateException("Inconsistent package.");
0916: }
0917: }
0918: return (Pk) ti;
0919: }
0920:
0921: /** Get method in tree from the fully qualified name.
0922: * @param fullName the fully qualified method name
0923: * @param descriptor an optional descriptor for the method
0924: * @return method item in the tree; null if the element is not found
0925: * @throws IllegalStateException if an error exists inside the tree
0926: */
0927: public Md getMd(String fullName, String descriptor)
0928: throws IllegalStateException {
0929: // Split into class and method names
0930: int pos = fullName.lastIndexOf(METHOD_FIELD_LEVEL);
0931: Cl cl = getCl(fullName.substring(0, pos));
0932: if (null != cl) {
0933: return cl
0934: .getMethod(fullName.substring(pos + 1), descriptor);
0935: }
0936: return null;
0937: }
0938:
0939: /** Get field in tree from the fully qualified name.
0940: * @param fullName the fully qualified field name
0941: * @return field item in the tree; null if the element is not found
0942: * @throws IllegalStateException if an error exists inside the tree
0943: */
0944: public Fd getFd(String fullName) throws IllegalStateException {
0945: // Split into class and field names
0946: int pos = fullName.lastIndexOf(METHOD_FIELD_LEVEL);
0947: Cl cl = getCl(fullName.substring(0, pos));
0948: if (null != cl) {
0949: return cl.getField(fullName.substring(pos + 1));
0950: }
0951: return null;
0952: }
0953:
0954: /** Mapping for fully qualified class name.
0955: * @param className the fully qualified class name to map
0956: * @return the mapped class name
0957: * @see NameMapper#mapClass
0958: */
0959: public String mapClass(String className) {
0960: // Check for array -- requires special handling
0961: if (className.length() > 0 && className.charAt(0) == '[') {
0962: StringBuffer newName = new StringBuffer();
0963: int i = 0;
0964: while (i < className.length()) {
0965: char ch = className.charAt(i++);
0966: switch (ch) {
0967: case '[':
0968: case ';':
0969: newName.append(ch);
0970: break;
0971:
0972: case 'L':
0973: newName.append(ch);
0974: int pos = className.indexOf(';', i);
0975: if (pos < 0) {
0976: throw new IllegalStateException(
0977: "Invalid class name encountered: "
0978: + className);
0979: }
0980: newName
0981: .append(mapClass(className
0982: .substring(i, pos)));
0983: i = pos;
0984: break;
0985:
0986: default:
0987: return className;
0988: }
0989: }
0990: return newName.toString();
0991: } else {
0992: Cl cl = getCl(className);
0993: return cl != null ? cl.getFullOutName() : className;
0994: }
0995: }
0996:
0997: /** Mapping for method name, of fully qualified class.
0998: * @param className the class name
0999: * @param methodName the method name
1000: * @param descriptor the method descriptor
1001: * @return mapped name; null if no mapping is found
1002: * @see NameMapper#mapMethod
1003: */
1004: public String mapMethod(String className, String methodName,
1005: String descriptor) {
1006: if (null != className && !className.equals("")) {
1007: Cl cl = getCl(className);
1008: if (null != cl) {
1009: Md md = cl.getMethod(methodName, descriptor);
1010: if (null != md) {
1011: return md.getOutName();
1012: } else {
1013: // Check whether the method is defined in the super class, necessary
1014: // when classes are compiled with "-target 1.[234]"
1015: // "-target 1.1", the default setting for the javac compiler from
1016: // JDK <1.4, lets the method reference directly point to the super
1017: // class where it is defined, whereby the others use a reference
1018: // to the class itself where the method is used.
1019: String retVal = mapMethod(cl.getSuperclass(),
1020: methodName, descriptor);
1021: if (null != retVal)
1022: return retVal;
1023: // try to search whether the method is defined in implemented
1024: // interfaces
1025: String[] interfaces = cl.getSuperInterfaces();
1026: if (null != interfaces) {
1027: for (int i = 0; i < interfaces.length; i++) {
1028: retVal = mapMethod(interfaces[i],
1029: methodName, descriptor);
1030: if (null != retVal)
1031: return retVal;
1032: }
1033: }
1034: }
1035: }
1036: }
1037: return null;
1038: }
1039:
1040: /** Mapping for field name, of fully qualified class.
1041: * @param className the class name
1042: * @param fieldName the field name
1043: * @return mapped field name; null if no mapping is found
1044: * @see NameMapper#mapField
1045: */
1046: public String mapField(String className, String fieldName) {
1047: if (null != className && !className.equals("")) {
1048: Cl cl = getCl(className);
1049: if (null != cl) {
1050: Fd fd = cl.getField(fieldName);
1051: if (null != fd) {
1052: return fd.getOutName();
1053: } else {
1054: // Check whether the field is defined in the super class, necessary
1055: // when classes are compiled with "-target 1.[234]"
1056: // "-target 1.1", the default setting for the javac compiler from
1057: // JDK <1.4, lets the field reference directly point to the super
1058: // class where it is defined, whereby the others use a reference
1059: // to the class itself where the field is used.
1060: String retVal = mapField(cl.getSuperclass(),
1061: fieldName);
1062: if (null != retVal)
1063: return retVal;
1064: // try to search whether the field is defined in implemented
1065: // interfaces
1066: String[] interfaces = cl.getSuperInterfaces();
1067: if (null != interfaces) {
1068: for (int i = 0; i < interfaces.length; i++) {
1069: retVal = mapField(interfaces[i], fieldName);
1070: if (null != retVal)
1071: return retVal;
1072: }
1073: }
1074: }
1075: }
1076: }
1077: return null;
1078: }
1079:
1080: /** Mapping for descriptor of field or method.
1081: * @param descriptor the descriptor to map
1082: * @return mapped descriptor string
1083: * @see NameMapper#mapDescriptor
1084: */
1085: public String mapDescriptor(String descriptor) {
1086: // Pass everything through unchanged, except for the String between
1087: // 'L' and ';' -- this is passed through mapClass(String)
1088: StringBuffer newDesc = new StringBuffer();
1089: int i = 0;
1090: while (i < descriptor.length()) {
1091: char ch = descriptor.charAt(i++);
1092: switch (ch) {
1093: case '[':
1094: case 'B':
1095: case 'C':
1096: case 'D':
1097: case 'F':
1098: case 'I':
1099: case 'J':
1100: case 'S':
1101: case 'Z':
1102: case 'V':
1103: case '(':
1104: case ')':
1105: case ';':
1106: newDesc.append(ch);
1107: break;
1108:
1109: case 'L':
1110: newDesc.append(ch);
1111: int pos = descriptor.indexOf(';', i);
1112: if (pos < 0) {
1113: throw new IllegalStateException(
1114: "Invalid descriptor string encountered.");
1115: }
1116: newDesc.append(mapClass(descriptor.substring(i, pos)));
1117: i = pos;
1118: break;
1119:
1120: default:
1121: throw new IllegalStateException(
1122: "Invalid descriptor string encountered.");
1123: }
1124: }
1125: return newDesc.toString();
1126: }
1127:
1128: /** Dump the content of the class tree to the specified file (used for
1129: * logging).
1130: */
1131: public void dump() {
1132: logger.log(Log.INFO, "Dumping class tree...");
1133: dumpFrequencyCount();
1134: dumpIgnoreStatements();
1135: dumpRemoteClasses();
1136: dumpAutoCorrectedClasses();
1137: dumpSerializables();
1138: dumpReservedNames();
1139: dumpNameMappings();
1140: }
1141:
1142: /** Dupms the name frequency count to the log file.
1143: */
1144: private void dumpFrequencyCount() {
1145: logger.log(Log.VERBOSE, "Calculating name frequency count");
1146: logfile.println();
1147: logfile.println();
1148: logfile.println();
1149: logfile.println("#");
1150: logfile.println("# Obfuscated name overloading frequency:");
1151: logfile.println("#");
1152: // Compute total use count
1153: int totalUseCount = 0;
1154: for (Enumeration useCountEnum = NameMaker.getUseCounts(); useCountEnum.hasMoreElements(); ) {
1155: totalUseCount += ((Integer)useCountEnum.nextElement()).intValue() + 1;
1156: }
1157:
1158: // Log the individual use counts for names
1159: if (totalUseCount != 0) {
1160: int sumPercent = 0;
1161: int sumUseCount = 0;
1162: Vector sort = new Vector();
1163: for (Enumeration nameEnum = NameMaker.getNames(), useCountEnum = NameMaker.getUseCounts(); nameEnum.hasMoreElements(); ) {
1164: String name = (String)nameEnum.nextElement();
1165: int useCount = ((Integer)useCountEnum.nextElement()).intValue() + 1;
1166: int percent = 100 * useCount / totalUseCount;
1167: if (percent != 0) {
1168: sort.addElement(new SortElement(name, useCount));
1169: sumUseCount += useCount;
1170: sumPercent += percent;
1171: }
1172: }
1173: while (sort.size() != 0) {
1174: SortElement se = null;
1175: int largestUseCount = 0;
1176: for (Enumeration enum = sort.elements(); enum.hasMoreElements(); ) {
1177: SortElement this Se = (SortElement)enum.nextElement();
1178: if (this Se.useCount >= largestUseCount) {
1179: se = this Se;
1180: largestUseCount = se.useCount;
1181: }
1182: }
1183: sort.removeElement(se);
1184: logfile.println("# '" + se.name + "' \tused " + se.useCount + " times\t(" + Integer.toString(100 * se.useCount / totalUseCount) + "%)");
1185: }
1186:
1187: // Log the remainder percentage
1188: logfile.println("# Other names (each used in <1% of mappings) used a total of "
1189: + Integer.toString(totalUseCount - sumUseCount)
1190: + " times (" + Integer.toString(100 - sumPercent) + "%)");
1191: logfile.println("#");
1192: }
1193: }
1194:
1195: class SortElement {
1196: int useCount;
1197: String name;
1198:
1199: SortElement(String name, int useCount) {
1200: this .useCount = useCount;
1201: this .name = name;
1202: }
1203: }
1204:
1205: /** Write all ignore statements to the log file.
1206: */
1207: private void dumpIgnoreStatements() {
1208: // check if there are ".ignore" statements
1209: if (!patternsIgnoreDefault.isEmpty()
1210: || !patternsIgnorePackage.isEmpty()
1211: || !patternsIgnoreClass.isEmpty()
1212: || !patternsIgnoreMethod.isEmpty()
1213: || !patternsIgnoreField.isEmpty()) {
1214: logger.log(Log.VERBOSE,
1215: "Creating list with ignore statements");
1216: // if yes write them to the log file
1217: logfile.println();
1218: logfile.println();
1219: logfile.println();
1220: logfile.println("#");
1221: logfile.println("# Ignored from obfuscation:");
1222: logfile.println("#");
1223:
1224: // write all tree elements that should be ignored to the log file
1225: walkTree(new TreeAction() {
1226: public void packageAction(Pk pk) {
1227: defaultAction(pk);
1228: }
1229:
1230: public void classAction(Cl cl) {
1231: defaultAction(cl);
1232: }
1233:
1234: public void methodAction(Md md) {
1235: defaultAction(md);
1236: }
1237:
1238: public void fieldAction(Fd fd) {
1239: defaultAction(fd);
1240: }
1241:
1242: public void defaultAction(TreeItem ti) {
1243: if (ti.canIgnoreElement()) {
1244: if (null == ti.getParent()
1245: || !ti.getParent().canIgnoreElement()) {
1246: if (ti instanceof Pk) {
1247: logfile
1248: .println(ScriptConstants.DIRECTIVE_IGNORE_PACKAGE
1249: + " "
1250: + ti.getFullInName());
1251: } else if (ti instanceof Cl) {
1252: logfile
1253: .println(ScriptConstants.DIRECTIVE_IGNORE_CLASS
1254: + " "
1255: + ti.getFullInName());
1256: } else if (ti instanceof Md) {
1257: logfile
1258: .println(ScriptConstants.DIRECTIVE_IGNORE_METHOD
1259: + " "
1260: + ti.getFullInName()
1261: + " "
1262: + ((Md) ti)
1263: .getDescriptor());
1264: } else if (ti instanceof Fd) {
1265: logfile
1266: .println(ScriptConstants.DIRECTIVE_IGNORE_FIELD
1267: + " "
1268: + ti.getFullInName()
1269: + " "
1270: + ((Fd) ti)
1271: .getDescriptor());
1272: } else {
1273: // should never happen, only if there's an error in the internal
1274: // data structures
1275: logfile
1276: .println(ScriptConstants.DIRECTIVE_IGNORE
1277: + " "
1278: + ti.getFullInName());
1279: }
1280: }
1281: }
1282: }
1283: });
1284: } else {
1285: logger
1286: .log(Log.VERBOSE,
1287: "Skipping creating list with ignore statements (no elements available)");
1288: }
1289: }
1290:
1291: /** Write the list of processed remote classes to the log file.
1292: */
1293: private void dumpRemoteClasses() {
1294: if (canUseRmicClasses()) {
1295: logger.log(Log.VERBOSE,
1296: "Generating list of processed remote classes");
1297: logfile.println();
1298: logfile.println();
1299: logfile.println();
1300: logfile.println("#");
1301: logfile.println("# remote classes:");
1302: logfile.println("#");
1303:
1304: for (int i = 0; i < remoteClasses.size(); i++) {
1305: Cl classItem = (Cl) remoteClasses.elementAt(i);
1306: logfile.println("# class " + classItem.getFullInName());
1307: logfile.println("# -> " + classItem.getOutName());
1308: }
1309: } else {
1310: logger
1311: .log(Log.VERBOSE,
1312: "Skipping generating list of processed remote classes");
1313: }
1314: }
1315:
1316: /** Write the list of classes that were used in hardcoded references to the
1317: * log file.
1318: */
1319: private void dumpAutoCorrectedClasses() {
1320: if (canAutoCorrectClassNames()) {
1321: logger
1322: .log(Log.VERBOSE,
1323: "Generating list of classes used in hardcoded references");
1324: logfile.println();
1325: logfile.println();
1326: logfile.println();
1327: logfile.println("#");
1328: logfile
1329: .println("# The following classes are used in hardcoded references:");
1330: logfile.println("#");
1331: Iterator iter = hardcodedReferences.iterator();
1332: while (iter.hasNext()) {
1333: String str = (String) iter.next();
1334: if (null != str) {
1335: TreeItem ti = findClass(str);
1336: if (null == ti) {
1337: logfile.log("# Warning: cannot find class "
1338: + str);
1339: } else {
1340: logfile.println("# retaining class "
1341: + ti.getFullInName());
1342: }
1343: }
1344: }
1345: } else {
1346: logger
1347: .log(Log.VERBOSE,
1348: "Skipping generating list of classes used in hardcoded references");
1349: }
1350: }
1351:
1352: /** Write the list of serializable methods and fields found to the log file.
1353: */
1354: private void dumpSerializables() {
1355: logger.log(Log.VERBOSE,
1356: "Writing list of serializable methods and fields");
1357: logfile.println();
1358: logfile.println();
1359: logfile.println();
1360: logfile.println("#");
1361: logfile
1362: .println("# The following fields and methods are used in serializable classes");
1363: logfile
1364: .println("# and are checked whether they should be marked for retention or not:");
1365: logfile.println("#");
1366:
1367: int i;
1368: // log all serializable fields
1369: for (i = 0; i < serializableFields.size(); i++) {
1370: Fd fd = (Fd) serializableFields.elementAt(i);
1371: if (isSerializableClass((Cl) fd.getParent())) {
1372: logfile.print("# field marked: ");
1373: } else {
1374: logfile.print("# field not marked: ");
1375: }
1376: logfile.println(fd.getFullInName() + " "
1377: + fd.getDescriptor());
1378: }
1379:
1380: // add all additional methods
1381: for (i = 0; i < serializableMethods.size(); i++) {
1382: Md md = (Md) serializableMethods.elementAt(i);
1383: if (isSerializableClass((Cl) md.getParent())) {
1384: logfile.print("# method marked: ");
1385: } else {
1386: logfile.print("# method not marked: ");
1387: }
1388: logfile.println(md.getFullInName() + " "
1389: + md.getDescriptor());
1390: }
1391: }
1392:
1393: /** Write the list of names prevented from being obfuscated to the log file.
1394: */
1395: private void dumpReservedNames() {
1396: logger.log(Log.VERBOSE, "Writing list of reserved names");
1397: // write all names that should be reserved from obfuscation to the log file
1398: logfile.println();
1399: logfile.println();
1400: logfile.println();
1401: logfile.println("#");
1402: logfile
1403: .println("# Full list of names reserved from obfuscation:");
1404: logfile.println("#");
1405:
1406: walkTree(new TreeAction() {
1407: public void classAction(Cl cl) {
1408: if (!cl.canIgnoreElement() && cl.isFromScript()) {
1409: logfile.println(ScriptConstants.DIRECTIVE_CLASS
1410: + " " + cl.getFullInName());
1411: }
1412: }
1413:
1414: public void methodAction(Md md) {
1415: if (!md.canIgnoreElement() && md.isFromScript()) {
1416: logfile.println(ScriptConstants.DIRECTIVE_METHOD
1417: + " " + md.getFullInName() + " "
1418: + md.getDescriptor());
1419: }
1420: }
1421:
1422: public void fieldAction(Fd fd) {
1423: if (!fd.canIgnoreElement() && fd.isFromScript()) {
1424: logfile.println(ScriptConstants.DIRECTIVE_FIELD
1425: + " " + fd.getFullInName() + " "
1426: + fd.getDescriptor());
1427: }
1428: }
1429:
1430: public void packageAction(Pk pk) {
1431: // No action
1432: }
1433: });
1434: }
1435:
1436: /** Write the name mapping table to the log file.
1437: */
1438: private void dumpNameMappings() {
1439: logger.log(Log.VERBOSE, "Writing the name mapping table");
1440: logfile.println();
1441: logfile.println();
1442: logfile.println();
1443: logfile.println("#");
1444: logfile
1445: .println("# Obfuscated name mappings (some of these may be unchanged due to polymorphism");
1446: logfile.println("# constraints):");
1447: logfile.println("#");
1448:
1449: // now write the obfuscated names to the log file
1450: walkTree(new TreeAction() {
1451: public void classAction(Cl cl) {
1452: if (!cl.canIgnoreElement() && !cl.isFromScript()) {
1453: logfile.println(ScriptConstants.DIRECTIVE_CLASS_MAP
1454: + " " + cl.getFullInName() + " "
1455: + cl.getOutName());
1456: }
1457: }
1458:
1459: public void methodAction(Md md) {
1460: if (!md.canIgnoreElement() && !md.isFromScript()) {
1461: logfile
1462: .println(ScriptConstants.DIRECTIVE_METHOD_MAP
1463: + " "
1464: + md.getFullInName()
1465: + " "
1466: + md.getDescriptor()
1467: + " "
1468: + md.getOutName());
1469: }
1470: }
1471:
1472: public void fieldAction(Fd fd) {
1473: if (!fd.canIgnoreElement() && !fd.isFromScript()) {
1474: logfile.println(ScriptConstants.DIRECTIVE_FIELD_MAP
1475: + " " + fd.getFullInName() + " "
1476: + fd.getOutName());
1477: }
1478: }
1479:
1480: public void packageAction(Pk pk) {
1481: if (!pk.canIgnoreElement() && !pk.isFromScript()
1482: && pk.getFullInName().length() > 0) {
1483: logfile.println();
1484: logfile
1485: .println(ScriptConstants.DIRECTIVE_PACKAGE_MAP
1486: + " "
1487: + pk.getFullInName()
1488: + " "
1489: + pk.getOutName());
1490: }
1491: }
1492: });
1493: }
1494:
1495: /** Walk the whole tree taking action once only on each package level, class,
1496: * method and field.
1497: * @param ta set of actions to be performed on tree elements
1498: */
1499: public void walkTree(TreeAction ta) {
1500: walkTree(ta, getRootPackage());
1501: }
1502:
1503: /** Walk the given subtree taking action once only on each package level,
1504: * class, method and field.
1505: * @param ta set of actions to be performed on tree elements
1506: * @param ti the head of the subtree
1507: */
1508: public void walkTree(TreeAction ta, TreeItem ti) {
1509: if (ti instanceof Pk) {
1510: Iterator packageIter = ((Pk) ti).getPackageIterator();
1511: ta.packageAction((Pk) ti);
1512: while (packageIter.hasNext()) {
1513: walkTree(ta, (TreeItem) packageIter.next());
1514: }
1515: }
1516: if (ti instanceof Cl) {
1517: Iterator fieldIter = ((Cl) ti).getFieldIterator();
1518: Iterator methodIter = ((Cl) ti).getMethodIterator();
1519: ta.classAction((Cl) ti);
1520: while (fieldIter.hasNext()) {
1521: ta.fieldAction((Fd) fieldIter.next());
1522: }
1523: while (methodIter.hasNext()) {
1524: ta.methodAction((Md) methodIter.next());
1525: }
1526: }
1527: if (ti instanceof PkCl) {
1528: Iterator classIter = ((PkCl) ti).getClassIterator();
1529: while (classIter.hasNext()) {
1530: walkTree(ta, (TreeItem) classIter.next());
1531: }
1532: }
1533: }
1534:
1535: /** Adds a regular expression to the name pattern list which is used to
1536: * prevent packages, classes etc. from being obfuscated.
1537: * @param regex a regular expression
1538: * @throws MalformedPatternException if an error occurs during regex compilation
1539: */
1540: public void addIgnoreDefaultRegex(String regex)
1541: throws MalformedPatternException {
1542: addRegex(patternsIgnoreDefault, regex, null);
1543: }
1544:
1545: /** Adds a regular expression to the package name pattern list which is used
1546: * to prevent packages from being obfuscated.
1547: * @param regex a regular expression
1548: * @throws MalformedPatternException if an error occurs during regex compilation
1549: */
1550: public void addIgnorePackageRegex(String regex)
1551: throws MalformedPatternException {
1552: addRegex(patternsIgnorePackage, regex, null);
1553: }
1554:
1555: /** Adds a regular expression to the class name pattern list which is used
1556: * to prevent classes from being obfuscated.
1557: * @param regex a regular expression
1558: * @throws MalformedPatternException if an error occurs during regex compilation
1559: */
1560: public void addIgnoreClassRegex(String regex)
1561: throws MalformedPatternException {
1562: addRegex(patternsIgnoreClass, regex, null);
1563: }
1564:
1565: /** Adds a regular expression to the package name pattern list which is used
1566: * to prevent method names from being obfuscated.
1567: * @param entry a script file directive that contains the regular expression
1568: * @throws MalformedPatternException if an error occurs during regex compilation
1569: */
1570: public void addIgnoreMethodRegex(ScriptEntry entry)
1571: throws MalformedPatternException {
1572: addRegex(patternsIgnoreMethod, entry.getName(), entry);
1573: }
1574:
1575: /** Adds a regular expression to the package name pattern list which is used
1576: * to prevent field names from being obfuscated.
1577: * @param entry script file entry that contains the regular expression
1578: * @throws MalformedPatternException if an error occurs during regex compilation
1579: */
1580: public void addIgnoreFieldRegex(ScriptEntry entry)
1581: throws MalformedPatternException {
1582: addRegex(patternsIgnoreField, entry.getName(), entry);
1583: }
1584:
1585: /** Adds a regular expression to the name pattern list which is used to
1586: * force packages, classes etc. to get obfuscated.
1587: * @param regex a regular expression
1588: * @throws MalformedPatternException if an error occurs during regex compilation
1589: */
1590: public void addObfuscateDefaultRegex(String regex)
1591: throws MalformedPatternException {
1592: addRegex(patternsObfuscateDefault, regex, null);
1593: }
1594:
1595: /** Adds a regular expression to the package name pattern list which is used
1596: * to force packages to get obfuscated.
1597: * @param regex a regular expression
1598: * @throws MalformedPatternException if an error occurs during regex compilation
1599: */
1600: public void addObfuscatePackageRegex(String regex)
1601: throws MalformedPatternException {
1602: addRegex(patternsObfuscatePackage, regex, null);
1603: }
1604:
1605: /** Adds a regular expression to the class name pattern list which is used
1606: * to force classes to get obfuscated.
1607: * @param regex a regular expression
1608: * @throws MalformedPatternException if an error occurs during regex compilation
1609: */
1610: public void addObfuscateClassRegex(String regex)
1611: throws MalformedPatternException {
1612: addRegex(patternsObfuscateClass, regex, null);
1613: }
1614:
1615: /** Adds a regular expression to the package name pattern list which is used
1616: * to force method names to get obfuscated.
1617: * @param entry a script file directive that contains the regular expression
1618: * @throws MalformedPatternException if an error occurs during regex compilation
1619: */
1620: public void addObfuscateMethodRegex(ScriptEntry entry)
1621: throws MalformedPatternException {
1622: addRegex(patternsIgnoreMethod, entry.getName(), entry);
1623: }
1624:
1625: /** Adds a regular expression to the package name pattern list which is used
1626: * to force field names to get obfuscated.
1627: * @param entry script file entry that contains the regular expression
1628: * @throws MalformedPatternException if an error occurs during regex compilation
1629: */
1630: public void addObfuscateFieldRegex(ScriptEntry entry)
1631: throws MalformedPatternException {
1632: addRegex(patternsObfuscateField, entry.getName(), entry);
1633: }
1634:
1635: /** Adds a regular expression to the given map if it isn't already contained
1636: * in it.
1637: * @param map the map that holds precompiled regular expressions
1638: * @param regex a regular expression
1639: * @param info an object that holds additional informations
1640: * @throws MalformedPatternException if an error occurs during regex compilation
1641: * @see #compileRegex
1642: */
1643: private void addRegex(Map map, String regex, Object info)
1644: throws MalformedPatternException {
1645: if (!map.containsKey(regex)) {
1646: map.put(regex, new KeyValue(compileRegex(regex), info));
1647: } else {
1648: logger.log("Warning: Duplicate regex pattern: '" + regex
1649: + "'");
1650: }
1651: }
1652:
1653: /** Corrects the regular expression and compiles it into a data structure
1654: * that can be used by a pattern matcher implementation to perform pattern
1655: * matching.
1656: * @param regex a regular expression
1657: * @return a pattern instance constituting the compiled regular expression
1658: * @throws MalformedPatternException If the compiled expression does not
1659: * conform to the grammar understood by the PatternCompiler or if some other
1660: * error in the expression is encountered.
1661: */
1662: private Pattern compileRegex(String regex)
1663: throws MalformedPatternException {
1664: StringBuffer sb = new StringBuffer();
1665: // Fix the regular expression so that it always has the form ^...$
1666: // To be compatible with RetroGuard and to form valid regular expressions
1667: // strings that end with ".../*" are corrected so they end with ".../.*",
1668: // and occurrences of a '$' sign inside a string are replaced by '\$'
1669: // to correctly match inner classes/elements.
1670:
1671: // let the regex always start with '^'
1672: if (!regex.startsWith("^")) {
1673: sb.append('^');
1674: }
1675:
1676: // replace dollar signs by '\$'
1677: int i = 0;
1678: int pos = regex.indexOf('$');
1679: while (pos > 0) {
1680: if (regex.charAt(pos - 1) != '\\') {
1681: sb.append(regex.substring(i, pos));
1682: sb.append("\\$");
1683: i = pos + 1;
1684: }
1685: pos = regex.indexOf('$', pos + 1);
1686: }
1687:
1688: // replace "/*" string end by "/.*"
1689: if (regex.endsWith("/*")) {
1690: sb.append(regex.substring(i, regex.length() - 2));
1691: sb.append("/.*");
1692: } else {
1693: sb.append(regex.substring(i));
1694: }
1695:
1696: // let the regex end with '$' to match the line end
1697: if (!regex.endsWith("$")) {
1698: sb.append('$');
1699: }
1700: return patternCompiler.compile(sb.toString());
1701: }
1702:
1703: /** Checks whether there is a regular expression in the default pattern list
1704: * that matches a given string.
1705: * @param str a string to check
1706: * @return true if at least one regular expression matches the given string;
1707: * false else
1708: * @see #addIgnoreDefaultRegex
1709: */
1710: private boolean matchesIgnoreDefaultRegex(String str) {
1711: return matchesRegex(patternsIgnoreDefault, str);
1712: }
1713:
1714: /** Checks whether there is a regular expression in the package name pattern
1715: * list that matches a given string.
1716: * @param str a package name to check
1717: * @return true if at least one regular expression matches the given string;
1718: * false else
1719: * @see #addIgnorePackageRegex
1720: */
1721: private boolean matchesIgnorePackageRegex(String str) {
1722: return matchesRegex(patternsIgnorePackage, str);
1723: }
1724:
1725: /** Checks whether there is a regular expression in the class name pattern
1726: * list that matches a given string.
1727: * @param str a class name to check
1728: * @return true if at least one regular expression matches the given string;
1729: * false else
1730: * @see #addIgnoreClassRegex
1731: */
1732: private boolean matchesIgnoreClassRegex(String str) {
1733: return matchesRegex(patternsIgnoreClass, str);
1734: }
1735:
1736: /** Checks whether there is a regular expression in the method name pattern
1737: * list that matches a given string.
1738: * @param str a method name to check
1739: * @return true if at least one regular expression matches the given string;
1740: * false else
1741: * @see #addIgnoreMethodRegex
1742: */
1743: private boolean matchesIgnoreMethodRegex(String str) {
1744: return matchesRegex(patternsIgnoreMethod, str);
1745: }
1746:
1747: /** Checks whether there is a regular expression in the field name pattern
1748: * list that matches a given string.
1749: * @param str a field name to check
1750: * @return true if at least one regular expression matches the given string;
1751: * false else
1752: * @see #addIgnoreFieldRegex
1753: */
1754: private boolean matchesIgnoreFieldRegex(String str) {
1755: return matchesRegex(patternsIgnoreField, str);
1756: }
1757:
1758: /** Checks whether there is a regular expression in the default pattern list
1759: * that matches a given string.
1760: * @param str a string to check
1761: * @return true if at least one regular expression matches the given string;
1762: * false else
1763: * @see #addObfuscateDefaultRegex
1764: */
1765: private boolean matchesObfuscateDefaultRegex(String str) {
1766: return matchesRegex(patternsObfuscateDefault, str);
1767: }
1768:
1769: /** Checks whether there is a regular expression in the package name pattern
1770: * list that matches a given string.
1771: * @param str a package name to check
1772: * @return true if at least one regular expression matches the given string;
1773: * false else
1774: * @see #addObfuscatePackageRegex
1775: */
1776: private boolean matchesObfuscatePackageRegex(String str) {
1777: return matchesRegex(patternsObfuscatePackage, str);
1778: }
1779:
1780: /** Checks whether there is a regular expression in the class name pattern
1781: * list that matches a given string.
1782: * @param str a class name to check
1783: * @return true if at least one regular expression matches the given string;
1784: * false else
1785: * @see #addObfuscateClassRegex
1786: */
1787: private boolean matchesObfuscateClassRegex(String str) {
1788: return matchesRegex(patternsObfuscateClass, str);
1789: }
1790:
1791: /** Checks whether there is a regular expression in the method name pattern
1792: * list that matches a given string.
1793: * @param str a method name to check
1794: * @return true if at least one regular expression matches the given string;
1795: * false else
1796: * @see #addObfuscateMethodRegex
1797: */
1798: private boolean matchesObfuscateMethodRegex(String str) {
1799: return matchesRegex(patternsObfuscateMethod, str);
1800: }
1801:
1802: /** Checks whether there is a regular expression in the field name pattern
1803: * list that matches a given string.
1804: * @param str a field name to check
1805: * @return true if at least one regular expression matches the given string;
1806: * false else
1807: * @see #addObfuscateFieldRegex
1808: */
1809: private boolean matchesObfuscateFieldRegex(String str) {
1810: return matchesRegex(patternsObfuscateField, str);
1811: }
1812:
1813: /** Checks whether a string is matched by a regular expression in the given
1814: * map.
1815: * @param map the map that holds precompiled regular expressions
1816: * @param str the string to verify
1817: * @return true if a regular expression matches the given string; false else
1818: */
1819: private boolean matchesRegex(Map map, String str) {
1820: Iterator iter = map.values().iterator();
1821: while (iter.hasNext()) {
1822: KeyValue kv = (KeyValue) iter.next();
1823: Pattern regex = (Pattern) kv.getKey();
1824: if (patternMatcher.contains(str, regex)) {
1825: return true;
1826: }
1827: }
1828: return false;
1829: }
1830:
1831: /** Walk through the tree and mark all elements that must be obfuscated and
1832: * all elements that should be completely ignored in the obfuscation step.
1833: */
1834: public void parseObfuscateAndIgnoreList() {
1835: logger
1836: .log(Log.VERBOSE,
1837: "Parsing list of elements to obfuscate and to ignore...");
1838: walkTree(new TreeAction() {
1839: public void packageAction(Pk pk) {
1840: defaultAction(pk);
1841: }
1842:
1843: public void classAction(Cl cl) {
1844: defaultAction(cl);
1845: }
1846:
1847: public void methodAction(Md md) {
1848: defaultAction(md);
1849: }
1850:
1851: public void fieldAction(Fd fd) {
1852: defaultAction(fd);
1853: }
1854:
1855: public void defaultAction(TreeItem ti) {
1856: String name = ti.getFullInName();
1857: if (null != name && !name.equals("")) {
1858: if ((null != ti.getParent() && ti.getParent()
1859: .canForceObfuscate())
1860: || (ti instanceof Cl && matchesObfuscateClassRegex(name))
1861: || (ti instanceof Pk && matchesObfuscatePackageRegex(name))
1862: || (ti instanceof Md && matchesObfuscateMethodRegex(name))
1863: || (ti instanceof Fd && matchesObfuscateFieldRegex(name))
1864: || matchesObfuscateDefaultRegex(name)) {
1865: ti.setForceObfuscate(true);
1866: ti.setIgnoreElement(false);
1867: } else {
1868: ti.setForceObfuscate(false);
1869: }
1870: // a tree element can only be ignored if it can be obfuscated
1871: if (!ti.canForceObfuscate()) {
1872: if ((null != ti.getParent() && ti.getParent()
1873: .canIgnoreElement())
1874: || (ti instanceof Cl && matchesIgnoreClassRegex(name))
1875: || (ti instanceof Pk && matchesIgnorePackageRegex(name))
1876: || (ti instanceof Md && matchesIgnoreMethodRegex(name))
1877: || (ti instanceof Fd && matchesIgnoreFieldRegex(name))
1878: || matchesIgnoreDefaultRegex(name)) {
1879: ti.setIgnoreElement(true);
1880: retainItem(ti);
1881: } else {
1882: ti.setIgnoreElement(false);
1883: }
1884: // if the current tree element is a class or a packege that should
1885: // retain its name also preserve the names of all super elements
1886: if (ti.canIgnoreElement()
1887: && (ti instanceof Pk || ti instanceof Cl)) {
1888: retainHierarchy(ti.getParent());
1889: }
1890: }
1891: }
1892: }
1893: });
1894: }
1895:
1896: /** Walk through the tree and check which classes implement java.rmi.Remote
1897: * or java.rmi.server.Skeleton.
1898: */
1899: public void markRemoteClasses() {
1900: if (canUseRmicClasses()) {
1901: logger.log(Log.VERBOSE, "Mark remote classes...");
1902: walkTree(new TreeAction() {
1903: public void classAction(Cl cl) {
1904: if (isRemoteClass(cl)) {
1905: logger.log(Log.DEBUG, "Remote class found: "
1906: + cl.getFullInName());
1907: remoteClasses.addElement(cl);
1908: }
1909: }
1910:
1911: public void packageAction(Pk pk) {
1912: }
1913:
1914: public void methodAction(Md md) {
1915: }
1916:
1917: public void fieldAction(Fd fd) {
1918: }
1919: });
1920: } else {
1921: logger.log(Log.VERBOSE, "Skipping marking remote classes");
1922: }
1923: }
1924:
1925: /** Checks whether the given class or one of its super classes implement the
1926: * java.rmi.Remote or java.rmi.server.Skeleton interface.
1927: * @param cl the class to verify
1928: * @return true if the class implements the Remote or Skeleton interface;
1929: * false else
1930: */
1931: private boolean isRemoteClass(Cl cl) {
1932: if (null == cl)
1933: return false;
1934: if (cl.isRemoteClass())
1935: return true;
1936:
1937: // check the implemented interfaces
1938: String[] interfaces = cl.getSuperInterfaces();
1939: boolean result = false;
1940: int i = 0;
1941: while (i < interfaces.length) {
1942: // check whether the current interface name is one of the remote interfaces
1943: if (interfaces[i].equals(REMOTE_INTERFACE)
1944: || interfaces[i].equals(SKELETON_INTERFACE)) {
1945: // yes -> mark the class as being a remote class
1946: result = true;
1947: break;
1948: }
1949: // check whether the referenced interface exists in the tree
1950: Cl interfaceCl = getCl(interfaces[i]);
1951: if (null != interfaceCl) {
1952: // the interface exists in the tree -> check whether it is a remote one
1953: if (isRemoteClass(interfaceCl)) {
1954: // yes -> mark the class as being a remote class
1955: result = true;
1956: break;
1957: }
1958: } else {
1959: // the class doesn't exist in the tree
1960: // -> check whether the class is accessible via the JVM
1961: if (isRemoteClass(interfaces[i])) {
1962: // the JVM-accessible class is a remote class -> mark the current class
1963: result = true;
1964: break;
1965: }
1966: }
1967: i++;
1968: }
1969: // If we don't have a result so far check whether the super class is a
1970: // remote class
1971: if (!result) {
1972: Cl super Cl = getCl(cl.getSuperclass());
1973: if (null == super Cl) {
1974: // the super class doesn't exist in the tree -> query the JVM
1975: result = isRemoteClass(cl.getSuperclass());
1976: } else {
1977: // otherwise query the internal name database
1978: result = isRemoteClass(super Cl);
1979: }
1980: }
1981: // if the class implements the Remote or Skeleton interface mark it as
1982: // being a remote class
1983: if (true == result) {
1984: cl.setRemoteClass(true);
1985: }
1986: return result;
1987: }
1988:
1989: /** Checks whether the given class or one of its super classes implement the
1990: * java.rmi.Remote or java.rmi.server.Skeleton interface.
1991: * @param name the name of the class to verify
1992: * @return true if the class implements the Remote or Skeleton interface;
1993: * false else
1994: */
1995: private boolean isRemoteClass(String name) {
1996: // try to receive a Class object from the running JVM
1997: try {
1998: Class cls = Class.forName(name.replace('/', '.'), false,
1999: getClass().getClassLoader());
2000: return isRemoteClass(cls);
2001: } catch (ClassNotFoundException cnfex) {
2002: // if the referenced class doesn't exist we don't care so far
2003: }
2004: return false;
2005: }
2006:
2007: /** Checks whether the given class or one of its super classes implement the
2008: * java.rmi.Remote or java.rmi.server.Skeleton interface.
2009: * @param cls the class to verify
2010: * @return true if the class implements the Remote or Skeleton interface;
2011: * false else
2012: */
2013: private boolean isRemoteClass(Class cls) {
2014: if (null == cls) {
2015: return false;
2016: }
2017:
2018: if (cls.isInterface()
2019: && (cls.getName().equals(REMOTE_INTERFACE_CLASS) || cls
2020: .getName().equals(SKELETON_INTERFACE_CLASS))) {
2021: return true;
2022: }
2023: // check whether the super class implements the Remote or Skeleton interface
2024: if (isRemoteClass(cls.getSuperclass())) {
2025: return true;
2026: }
2027: // check the implemented interfaces
2028: Class[] interfaces = cls.getInterfaces();
2029: for (int i = 0; i < interfaces.length; i++) {
2030: if (isRemoteClass(interfaces[i])) {
2031: return true;
2032: }
2033: }
2034: return false;
2035: }
2036:
2037: /** Obfuscates the names of special classes (such as classes created by rmic)
2038: * so that they correspond to their original classes.
2039: */
2040: public void retainRemoteClasses() {
2041: if (canUseRmicClasses()) {
2042: logger.log(Log.VERBOSE, "Processing remote classes...");
2043: for (int i = 0; i < remoteClasses.size(); i++) {
2044: Cl classItem = (Cl) remoteClasses.elementAt(i);
2045: String className = classItem.getFullInName();
2046:
2047: // only continue if we have a non-empty class name
2048: if (null != className && !className.equals("")) {
2049: String str = null;
2050: if (className.endsWith(STR_STUB)) {
2051: str = className.substring(0, className.length()
2052: - STR_STUB.length());
2053: } else if (className.endsWith(STR_SKEL)) {
2054: str = className.substring(0, className.length()
2055: - STR_SKEL.length());
2056: }
2057: // strip the rmic-generated suffix from the class name to receive the
2058: // original name
2059: if (null != str) {
2060: Cl originalClass = getCl(str);
2061: if (null != originalClass) {
2062: String outName = originalClass.getOutName();
2063: // now assign a new name to the rmic-generated class
2064: if (className.endsWith(STR_STUB)) {
2065: // if it's a stub class append "_Stub"
2066: classItem
2067: .setOutName(outName + STR_STUB);
2068: } else {
2069: // it it's a skeleton class append "_Skel"
2070: classItem
2071: .setOutName(outName + STR_SKEL);
2072: }
2073: // if the original class is assigned a new name by a script file
2074: // mapping entry mark the remote class
2075: if (originalClass.isFromScript()) {
2076: classItem.setFromScript();
2077: }
2078: if (originalClass.isFromScriptMap()) {
2079: classItem.setFromScriptMap();
2080: }
2081: } else {
2082: logger.log(Log.VERBOSE, "Cannot rename "
2083: + className);
2084: logger.log(Log.VERBOSE,
2085: "(base class not found)");
2086: }
2087: }
2088: }
2089: }
2090: } else {
2091: logger.log(Log.VERBOSE,
2092: "Skipping processing remote classes");
2093: }
2094: }
2095:
2096: /** Retain all available package, class, method and field mappings thar are
2097: * specified in the given vector.
2098: * @param mappings vector that holds the mapping entries
2099: * @throws MalformedPatternException If the compiled expression does not
2100: * conform to the grammar understood by the PatternCompiler or if some other
2101: * error in the expression is encountered.
2102: */
2103: public void retainMappings(Vector mappings)
2104: throws MalformedPatternException {
2105: logger.log(Log.INFO, "Retaining all available mappings...");
2106:
2107: Iterator iter = mappings.iterator();
2108: while (iter.hasNext()) {
2109: ScriptEntry entry = (ScriptEntry) iter.next();
2110:
2111: switch (entry.getType()) {
2112: case ScriptConstants.TYPE_PACKAGE:
2113: case ScriptConstants.TYPE_PACKAGE_MAP:
2114: retainPackageMapping(entry);
2115: break;
2116:
2117: case ScriptConstants.TYPE_CLASS:
2118: case ScriptConstants.TYPE_CLASS_MAP:
2119: retainClassMapping(entry);
2120: break;
2121:
2122: case ScriptConstants.TYPE_METHOD:
2123: case ScriptConstants.TYPE_METHOD_MAP:
2124: retainMethodMapping(entry);
2125: break;
2126:
2127: case ScriptConstants.TYPE_FIELD:
2128: case ScriptConstants.TYPE_FIELD_MAP:
2129: retainFieldMapping(entry);
2130: break;
2131:
2132: default:
2133: // should never happen because we've already parsed the script
2134: // and created the vector correctly
2135: throw new IllegalArgumentException(
2136: "Illegal type in script file");
2137: }
2138: }
2139: }
2140:
2141: /** Retain the given package mapping entry.
2142: * @param entry a package mapping entry; may not be null
2143: * @throws MalformedPatternException If the compiled expression does not
2144: * conform to the grammar understood by the PatternCompiler or if some other
2145: * error in the expression is encountered.
2146: */
2147: private void retainPackageMapping(ScriptEntry entry)
2148: throws MalformedPatternException {
2149: Pattern pattern = compileRegex(entry.getName());
2150: Iterator packages = findPackages(pattern);
2151: // process all packages found
2152: while (packages.hasNext()) {
2153: Pk pk = (Pk) packages.next();
2154: // only process the package if it wasn't processed earlier
2155: if (pk.isScriptEntryMatch() || pk.canForceObfuscate()
2156: || pk.canIgnoreElement())
2157: continue;
2158: pk.setScriptEntryMatch(true);
2159:
2160: // set the output name if one is specified
2161: if (ScriptConstants.TYPE_PACKAGE_MAP == entry.getType()) {
2162: retainItemMap(pk, entry.getMappedName());
2163: }
2164: }
2165: }
2166:
2167: /** Retain the given class mapping entry.
2168: * @param entry a class mapping entry; may not be null
2169: * @throws MalformedPatternException If the compiled expression does not
2170: * conform to the grammar understood by the PatternCompiler or if some other
2171: * error in the expression is encountered.
2172: */
2173: private void retainClassMapping(ScriptEntry entry)
2174: throws MalformedPatternException {
2175: Pattern pattern = compileRegex(entry.getName());
2176: // for each available class mapping find all classes that match the
2177: // search pattern
2178: Iterator classes = findClasses(pattern);
2179: while (classes.hasNext()) {
2180: // process all classes that match the search pattern
2181: Cl cl = (Cl) classes.next();
2182: // only process the class if it wasn't processed earlier
2183: if (cl.isScriptEntryMatch() || cl.canForceObfuscate()
2184: || cl.canIgnoreElement())
2185: continue;
2186: cl.setScriptEntryMatch(true);
2187:
2188: // if the "protected" modifier is given automatically assume "public"
2189: if (entry.canRetainProtected()) {
2190: entry.setRetainPublic(true);
2191: }
2192:
2193: switch (entry.getType()) {
2194: case ScriptConstants.TYPE_CLASS:
2195: // the entry specifies a ".class" directive
2196: // -> retain the class name
2197: retainItem(cl);
2198: // if a class should preserve its name also preserve the names of
2199: // all super elements in the tree
2200: retainHierarchy(cl.getParent());
2201: break;
2202:
2203: case ScriptConstants.TYPE_CLASS_MAP:
2204: // the entry specifies a ".class_map" directive
2205: // -> set the output name
2206: retainItemMap(cl, entry.getMappedName());
2207: // if a class should preserve its name also preserve the names of
2208: // all super elements in the tree
2209: retainHierarchy(cl.getParent());
2210: break;
2211:
2212: default:
2213: // should never happen
2214: throw new InternalError("Illegal class type mapping!");
2215: }
2216: // process the additional class options
2217:
2218: // if no "public" or "protected" modifier is given check whether
2219: // fields or methods should be retained
2220: if (!entry.canRetainPublic() && !entry.canRetainProtected()) {
2221: // if the "field" or "method" option is given mark all of them as
2222: // being visible
2223: if (entry.canRetainMethods()) {
2224: retainItems(cl.getMethodIterator());
2225: }
2226: if (entry.canRetainFields()) {
2227: retainItems(cl.getFieldIterator());
2228: }
2229: } else {
2230: // at least one of the "public" and "protected" modifiers is given
2231: Vector vec = new Vector();
2232: // check whether we should retain methods
2233: if (entry.canRetainMethods()
2234: || !entry.canRetainFields()) {
2235: Iterator mdIter = cl.getMethodIterator();
2236: while (mdIter.hasNext()) {
2237: Md md = (Md) mdIter.next();
2238: // check whether we should retain public or protected methods
2239: if ((entry.canRetainPublic() && Modifier
2240: .isPublic(md.getModifiers()))
2241: || (entry.canRetainProtected() && Modifier
2242: .isProtected(md.getModifiers()))) {
2243: vec.addElement(md);
2244: }
2245: }
2246: }
2247: // check whether we should retain fields
2248: if (entry.canRetainFields()
2249: || !entry.canRetainMethods()) {
2250: Iterator fdIter = cl.getFieldIterator();
2251: while (fdIter.hasNext()) {
2252: Fd fd = (Fd) fdIter.next();
2253: if ((entry.canRetainPublic() && Modifier
2254: .isPublic(fd.getModifiers()))
2255: || (entry.canRetainProtected() && Modifier
2256: .isProtected(fd.getModifiers()))) {
2257: vec.addElement(fd);
2258: }
2259: }
2260: }
2261: // retain all filtered out elements
2262: retainItems(vec.iterator());
2263: }
2264: }
2265: }
2266:
2267: /** Retain the given method mapping entry.
2268: * @param entry a method mapping entry; may not be null
2269: * @throws MalformedPatternException If the compiled expression does not
2270: * conform to the grammar understood by the PatternCompiler or if some other
2271: * error in the expression is encountered.
2272: */
2273: private void retainMethodMapping(ScriptEntry entry)
2274: throws MalformedPatternException {
2275: Pattern pattern = compileRegex(entry.getName());
2276: // find all methods that match the search pattern and the optional
2277: // descriptor specified in the entry
2278: Iterator methods = findMethods(pattern, entry.getDescriptor());
2279: // process all method mapping entries found
2280: while (methods.hasNext()) {
2281: Md md = (Md) methods.next();
2282: // only process the method if it wasn't processed earlier
2283: if (md.isScriptEntryMatch() || md.canForceObfuscate()
2284: || md.canIgnoreElement())
2285: continue;
2286: md.setScriptEntryMatch(true);
2287:
2288: // only continue if the descriptor (if available) matches
2289: switch (entry.getType()) {
2290: case ScriptConstants.TYPE_METHOD:
2291: // the entry specifies a ".method" directive
2292: // -> retain the method name
2293: retainItem(md);
2294: break;
2295:
2296: case ScriptConstants.TYPE_METHOD_MAP:
2297: // the entry specifies a ".method_map" directive
2298: // -> set the output name
2299: retainItemMap(md, entry.getMappedName());
2300: break;
2301:
2302: default:
2303: // should never happen
2304: throw new InternalError("Illegal method type mapping!");
2305: }
2306: }
2307: }
2308:
2309: /** Retain the given field mapping entry.
2310: * @param entry a field mapping entry; may not be null
2311: * @throws MalformedPatternException If the compiled expression does not
2312: * conform to the grammar understood by the PatternCompiler or if some other
2313: * error in the expression is encountered.
2314: */
2315: private void retainFieldMapping(ScriptEntry entry)
2316: throws MalformedPatternException {
2317: Pattern pattern = compileRegex(entry.getName());
2318: // find all fields that match the search pattern and the optional
2319: // descriptor specified in the entry
2320: Iterator fields = findMethods(pattern, entry.getDescriptor());
2321: // process all field mapping entries found
2322: while (fields.hasNext()) {
2323: Fd fd = (Fd) fields.next();
2324: // only process the field if it wasn't processed earlier
2325: if (fd.isScriptEntryMatch() || fd.canForceObfuscate()
2326: || fd.canIgnoreElement())
2327: continue;
2328: fd.setScriptEntryMatch(true);
2329:
2330: // only continue if the descriptor (if available) matches
2331: switch (entry.getType()) {
2332: case ScriptConstants.TYPE_FIELD:
2333: // the entry specifies a ".field" directive
2334: // -> retain the field name
2335: retainItem(fd);
2336: break;
2337:
2338: case ScriptConstants.TYPE_FIELD_MAP:
2339: // the entry specifies a ".field_map" directive
2340: // -> set the output name
2341: retainItemMap(fd, entry.getMappedName());
2342: break;
2343:
2344: default:
2345: // should never happen
2346: throw new InternalError("Illegal field type mapping!");
2347: }
2348: }
2349: }
2350:
2351: /** Prevent the names of classes that are used in hardcoded references from
2352: * being obfuscated.
2353: */
2354: public void retainHardcodedReferences() {
2355: // if we should prevent hardcoded class names from being obfuscated
2356: // retain their names
2357: if (canAutoCorrectClassNames()) {
2358: logger.log(Log.VERBOSE,
2359: "Retaining hardcoded class names...");
2360: Iterator iter = hardcodedReferences.iterator();
2361: while (iter.hasNext()) {
2362: String str = (String) iter.next();
2363: if (null != str) {
2364: // replace all "." in the name by the separator char "/" used in the
2365: // internal class database
2366: TreeItem ti = findClass(str);
2367: if (null == ti) {
2368: logger.log("# Warning: cannot find class "
2369: + str);
2370: } else {
2371: logger.log(Log.DEBUG, "# retaining class "
2372: + ti.getFullInName());
2373: }
2374: retainHierarchy(ti);
2375: }
2376: }
2377: } else {
2378: logger.log(Log.VERBOSE,
2379: "Skipping retaining hardcoded class names");
2380: }
2381: }
2382:
2383: /** Prevent the names of serializable fields and methods from being
2384: * obfuscated.
2385: * @see #addSerializableMethod
2386: * @see #addSerializableField
2387: */
2388: public void retainSerializableElements() {
2389: int i;
2390: // add all serializable fields
2391: logger.log(Log.VERBOSE,
2392: "Processing possible serializable fields...");
2393: logger.log(Log.DEBUG, "Number of fields available: "
2394: + serializableFields.size());
2395: int sfieldsRetained = 0;
2396: for (i = 0; i < serializableFields.size(); i++) {
2397: Fd fd = (Fd) serializableFields.elementAt(i);
2398: if (isSerializableClass((Cl) fd.getParent())) {
2399: retainItem(fd);
2400: sfieldsRetained++;
2401: }
2402: }
2403: logger.log(Log.DEBUG, "Number of fields retained: "
2404: + sfieldsRetained);
2405:
2406: // add all additional methods
2407: logger.log(Log.VERBOSE,
2408: "Processing possible serializable methods...");
2409: logger.log(Log.DEBUG, "Number of methods available: "
2410: + serializableMethods.size());
2411: int smethodsRetained = 0;
2412: for (i = 0; i < serializableMethods.size(); i++) {
2413: Md md = (Md) serializableMethods.elementAt(i);
2414: if (isSerializableClass((Cl) md.getParent())) {
2415: retainItem(md);
2416: smethodsRetained++;
2417: }
2418: }
2419: logger.log(Log.DEBUG, "Number of methods retained: "
2420: + smethodsRetained);
2421: }
2422:
2423: /** Adds a method item which could be a serializable method item inside the
2424: * tree to the list of methods that may not be obfuscated.
2425: * @param md method item in the tree
2426: */
2427: public void addSerializableMethod(Md md) {
2428: serializableMethods.add(md);
2429: }
2430:
2431: /** Adds a field item which could be a serializable field item inside the tree
2432: * to the list of fields that may not be obfuscated.
2433: * @param fd field item in the tree
2434: */
2435: public void addSerializableField(Fd fd) {
2436: serializableFields.add(fd);
2437: }
2438:
2439: /** Checks whether the given class or one of its super classes implement the
2440: * java.io.Serializable interface.
2441: * @param cl the class to verify
2442: * @return true if the class implements java.io.Serializable; false else
2443: */
2444: private boolean isSerializableClass(Cl cl) {
2445: if (null == cl)
2446: return false;
2447: if (cl.isSerializable())
2448: return true;
2449:
2450: // check the implemented interfaces
2451: String[] interfaces = cl.getSuperInterfaces();
2452: boolean result = false;
2453: int i = 0;
2454: while (i < interfaces.length) {
2455: // check whether the current interface name is java.io.Serializable
2456: if (interfaces[i].equals(SERIALIZABLE_INTERFACE)) {
2457: // yes -> mark the class as being serializable
2458: result = true;
2459: break;
2460: }
2461: // check whether the referenced interface exists in the tree
2462: Cl interfaceCl = getCl(interfaces[i]);
2463: if (null != interfaceCl) {
2464: // the interface exists in the tree -> check whether it is serializable
2465: if (isSerializableClass(interfaceCl)) {
2466: // yes -> mark the class as being serializable
2467: result = true;
2468: break;
2469: }
2470: } else {
2471: // the class doesn't exist in the tree
2472: // -> check whether the class is accessible via the JVM
2473: if (isSerializableClass(interfaces[i])) {
2474: // the JVM-accessible class is serializable -> mark the current class
2475: result = true;
2476: break;
2477: }
2478: }
2479: i++;
2480: }
2481: // If we don't have a result so far check whether the super class is
2482: // serializable
2483: if (!result) {
2484: Cl super Cl = getCl(cl.getSuperclass());
2485: if (null == super Cl) {
2486: // the super class doesn't exist in the tree -> query the JVM
2487: result = isSerializableClass(cl.getSuperclass());
2488: } else {
2489: // otherwise query the internal name database
2490: result = isSerializableClass(super Cl);
2491: }
2492: }
2493: if (true == result) {
2494: cl.setSerializable(true);
2495: }
2496: return result;
2497: }
2498:
2499: /** Checks whether the given class or one of its super classes implement the
2500: * java.io.Serializable interface.
2501: * @param name the name of the class to verify
2502: * @return true if the class implements java.io.Serializable; false else
2503: */
2504: private boolean isSerializableClass(String name) {
2505: // try to receive a Class object from the running JVM
2506: try {
2507: Class cls = Class.forName(name.replace('/', '.'), false,
2508: getClass().getClassLoader());
2509: return isSerializableClass(cls);
2510: } catch (ClassNotFoundException cnfex) {
2511: // if the referenced class doesn't exist we don't care so far
2512: }
2513: return false;
2514: }
2515:
2516: /** Checks whether the given class or one of its super classes implement the
2517: * java.io.Serializable interface.
2518: * @param cls the class to verify
2519: * @return true if the class implements java.io.Serializable; false else
2520: */
2521: private boolean isSerializableClass(Class cls) {
2522: if (null == cls) {
2523: return false;
2524: }
2525:
2526: if (cls.isInterface()
2527: && cls.getName().equals(SERIALIZABLE_INTERFACE_CLASS)) {
2528: return true;
2529: }
2530: // check whether the super class implements java.io.Serializable
2531: if (isSerializableClass(cls.getSuperclass())) {
2532: return true;
2533: }
2534: // check the implemented interfaces
2535: Class[] interfaces = cls.getInterfaces();
2536: for (int i = 0; i < interfaces.length; i++) {
2537: if (isSerializableClass(interfaces[i])) {
2538: return true;
2539: }
2540: }
2541: return false;
2542: }
2543: }
|