0001: /**
0002: * YGuard -- an obfuscation library for Java(TM) classfiles.
0003: *
0004: * Original Copyright (c) 1999 Mark Welsh (markw@retrologic.com)
0005: * Modifications Copyright (c) 2002 yWorks GmbH (yguard@yworks.com)
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 yguard@yworks.com
0022: *
0023: * Java and all Java-based marks are trademarks or registered
0024: * trademarks of Sun Microsystems, Inc. in the U.S. and other countries.
0025: */package com.yworks.yguard.obf;
0026:
0027: import com.yworks.yguard.Conversion;
0028: import com.yworks.yguard.ParseException;
0029: import com.yworks.yguard.obf.classfile.ClassConstants;
0030: import com.yworks.yguard.obf.classfile.ClassFile;
0031: import com.yworks.yguard.obf.classfile.LineNumberTableAttrInfo;
0032: import com.yworks.yguard.obf.classfile.Logger;
0033: import com.yworks.yguard.obf.classfile.NameMapper;
0034:
0035: import java.io.PrintWriter;
0036: import java.lang.reflect.Modifier;
0037: import java.util.ArrayList;
0038: import java.util.Enumeration;
0039: import java.util.HashSet;
0040: import java.util.List;
0041: import java.util.Set;
0042: import java.util.StringTokenizer;
0043: import java.util.Vector;
0044:
0045: /**
0046: * Tree structure of package levels, classes, methods and fields used for obfuscation.
0047: *
0048: * @author Mark Welsh
0049: */
0050: public class ClassTree implements NameMapper {
0051: // Constants -------------------------------------------------------------
0052: public static final char PACKAGE_LEVEL = '/';
0053: public static final char CLASS_LEVEL = '$';
0054: public static final char METHOD_FIELD_LEVEL = '/';
0055:
0056: // Fields ----------------------------------------------------------------
0057: private Vector retainAttrs = new Vector(); // List of attributes to retain
0058: private Pk root = null; // Root package in database (Java default package)
0059:
0060: // Class methods ---------------------------------------------------------
0061: /** Return a fully qualified name broken into package/class segments. */
0062: public static Enumeration getNameEnum(String name) {
0063: Vector vec = new Vector();
0064: String nameOrig = name;
0065: while (!name.equals("")) {
0066: int posP = name.indexOf(PACKAGE_LEVEL);
0067: int posC = name.indexOf(CLASS_LEVEL);
0068: Cons cons = null;
0069: if (posP == -1 && posC == -1) {
0070: cons = new Cons(new Character(CLASS_LEVEL), name);
0071: name = "";
0072: }
0073: if (posP == -1 && posC != -1) {
0074: cons = new Cons(new Character(CLASS_LEVEL), name
0075: .substring(0, posC));
0076: //fixes retroguard bug, where
0077: // 'ClassName$$InnerClassName' leads to a runtimeerror
0078: while ((posC + 1 < name.length())
0079: && (name.charAt(posC + 1) == CLASS_LEVEL))
0080: posC++;
0081: name = name.substring(posC + 1, name.length());
0082: }
0083: if (posP != -1 && posC == -1) {
0084: cons = new Cons(new Character(PACKAGE_LEVEL), name
0085: .substring(0, posP));
0086: name = name.substring(posP + 1, name.length());
0087: }
0088: if (posP != -1 && posC != -1) {
0089: if (posP < posC) {
0090: cons = new Cons(new Character(PACKAGE_LEVEL), name
0091: .substring(0, posP));
0092: name = name.substring(posP + 1, name.length());
0093: } else {
0094: throw new IllegalArgumentException(
0095: "Invalid fully qualified name (a): "
0096: + nameOrig);
0097: }
0098: }
0099: if (((String) cons.cdr).equals("")) {
0100: throw new IllegalArgumentException(
0101: "Invalid fully qualified name (b): " + nameOrig);
0102: }
0103: vec.addElement(cons);
0104: }
0105: return vec.elements();
0106: }
0107:
0108: // Instance Methods ------------------------------------------------------
0109: /** Ctor. */
0110: public ClassTree() {
0111: root = Pk.createRoot(this );
0112: }
0113:
0114: /** Return the root node. */
0115: public Pk getRoot() {
0116: return root;
0117: }
0118:
0119: /**
0120: * finds tree items by looking for name components only...
0121: */
0122: public TreeItem findTreeItem(String[] nameParts) {
0123: TreeItem tmp = root;
0124: for (int i = 0; tmp != null && i < nameParts.length; i++) {
0125: String name = nameParts[i];
0126: tmp = findSubItem(tmp, name);
0127: }
0128: return tmp;
0129: }
0130:
0131: /**
0132: * walks the tree of TreeItems in order to find a class forName
0133: */
0134: public Cl findClassForName(String name) {
0135: int dindex = name.indexOf('$');
0136: String innerClass = null;
0137: if (dindex > 0) {
0138: innerClass = name.substring(dindex + 1);
0139: name = name.substring(0, dindex);
0140: }
0141: int pindex = name.lastIndexOf('.');
0142: String packageName = null;
0143: if (pindex > 0) {
0144: packageName = name.substring(0, pindex);
0145: name = name.substring(pindex + 1);
0146: }
0147: Pk pk = root;
0148: if (packageName != null) {
0149: for (StringTokenizer st = new StringTokenizer(packageName,
0150: ".", false); st.hasMoreTokens();) {
0151: String token = st.nextToken();
0152: pk = findPackage(pk, token);
0153: if (pk == null)
0154: return null;
0155: }
0156: }
0157: Cl cl = findClass(pk, name);
0158: if (cl != null && innerClass != null) {
0159: for (StringTokenizer st = new StringTokenizer(innerClass,
0160: "$", false); st.hasMoreTokens();) {
0161: String token = st.nextToken();
0162: cl = findClass(cl, token);
0163: if (cl == null)
0164: return null;
0165: }
0166: }
0167: return cl;
0168: }
0169:
0170: private Pk findPackage(TreeItem parent, String pName) {
0171: if (parent instanceof Pk) {
0172: for (Enumeration enumeration = ((Pk) parent)
0173: .getPackageEnum(); enumeration.hasMoreElements();) {
0174: Pk subPk = (Pk) enumeration.nextElement();
0175: if (subPk.getInName().equals(pName)) {
0176: return subPk;
0177: }
0178: }
0179: }
0180: return null;
0181: }
0182:
0183: private Cl findClass(PkCl parent, String pName) {
0184: for (Enumeration enumeration = ((PkCl) parent).getClassEnum(); enumeration
0185: .hasMoreElements();) {
0186: Cl cl = (Cl) enumeration.nextElement();
0187: if (cl.getInName().equals(pName)) {
0188: return cl;
0189: }
0190: }
0191: return null;
0192: }
0193:
0194: private TreeItem findSubItem(TreeItem parent, String childName) {
0195: if (parent instanceof Pk) {
0196: for (Enumeration enumeration = ((Pk) parent)
0197: .getPackageEnum(); enumeration.hasMoreElements();) {
0198: Pk subPk = (Pk) enumeration.nextElement();
0199: if (subPk.getInName().equals(childName)) {
0200: return subPk;
0201: }
0202: }
0203: for (Enumeration enumeration = ((Pk) parent).getClassEnum(); enumeration
0204: .hasMoreElements();) {
0205: Cl cl = (Cl) enumeration.nextElement();
0206: if (cl.getInName().equals(childName)) {
0207: return cl;
0208: }
0209: }
0210: }
0211: if (parent instanceof Cl) {
0212: for (Enumeration enumeration = ((Cl) parent).getClassEnum(); enumeration
0213: .hasMoreElements();) {
0214: Cl cl = (Cl) enumeration.nextElement();
0215: if (cl.getInName().equals(childName)) {
0216: return cl;
0217: }
0218: }
0219: return null;
0220: }
0221: return null;
0222: }
0223:
0224: /** Update the path of the passed filename, if that path corresponds to a package. */
0225: public String getOutName(String inName) {
0226: try {
0227: TreeItem ti = root;
0228: StringBuffer sb = new StringBuffer();
0229: for (Enumeration nameEnum = getNameEnum(inName); nameEnum
0230: .hasMoreElements();) {
0231: Cons nameSegment = (Cons) nameEnum.nextElement();
0232: char tag = ((Character) nameSegment.car).charValue();
0233: String name = (String) nameSegment.cdr;
0234: switch (tag) {
0235: case PACKAGE_LEVEL:
0236: if (ti != null) {
0237: ti = ((Pk) ti).getPackage(name);
0238: if (ti != null) {
0239: sb.append(ti.getOutName());
0240: } else {
0241: sb.append(name);
0242: }
0243: } else {
0244: sb.append(name);
0245: }
0246: sb.append(PACKAGE_LEVEL);
0247: break;
0248:
0249: case CLASS_LEVEL:
0250: sb.append(name);
0251: return sb.toString();
0252:
0253: default:
0254: throw new RuntimeException(
0255: "Internal error: illegal package/class name tag");
0256: }
0257: }
0258: } catch (Exception e) {
0259: // Just drop through and return the original name
0260: }
0261: return inName;
0262: }
0263:
0264: /** Add a classfile's package, class, method and field entries to database. */
0265: public void addClassFile(ClassFile cf) {
0266: // Add the fully qualified class name
0267: TreeItem ti = root;
0268: char parentTag = PACKAGE_LEVEL;
0269: for (Enumeration nameEnum = getNameEnum(cf.getName()); nameEnum
0270: .hasMoreElements();) {
0271: Cons nameSegment = (Cons) nameEnum.nextElement();
0272: char tag = ((Character) nameSegment.car).charValue();
0273: String name = (String) nameSegment.cdr;
0274: switch (tag) {
0275: case PACKAGE_LEVEL:
0276: ti = ((Pk) ti).addPackage(name);
0277: break;
0278:
0279: case CLASS_LEVEL:
0280: // If this is an inner class, just add placeholder classes up the tree
0281: if (nameEnum.hasMoreElements()) {
0282: ti = ((PkCl) ti).addPlaceholderClass(name);
0283: } else {
0284: Cl cl = ((PkCl) ti).addClass(name, cf.getSuper(),
0285: cf.getInterfaces(), cf.getModifiers());
0286: cl.setInnerClassModifiers(cf
0287: .getInnerClassModifiers());
0288: cl.setClassFileAccess(cf.getClassFileAccess());
0289: ti = cl;
0290: }
0291: break;
0292:
0293: default:
0294: throw new ParseException(
0295: "Internal error: illegal package/class name tag");
0296: }
0297: parentTag = tag;
0298: }
0299:
0300: // We must have a class before adding methods and fields
0301: if (ti instanceof Cl) {
0302: Cl cl = (Cl) ti;
0303: cl.access = cf.getModifiers();
0304:
0305: // Add the class's methods to the database
0306: for (Enumeration enumeration = cf.getMethodEnum(); enumeration
0307: .hasMoreElements();) {
0308: Cons cons = (Cons) enumeration.nextElement();
0309: cl.addMethod(((Boolean) ((Cons) cons.car).car)
0310: .booleanValue(),
0311: (String) ((Cons) cons.car).cdr,
0312: (String) ((Cons) cons.cdr).car,
0313: ((Integer) ((Cons) cons.cdr).cdr).intValue());
0314: }
0315:
0316: // Add the class's fields to the database
0317: for (Enumeration enumeration = cf.getFieldEnum(); enumeration
0318: .hasMoreElements();) {
0319: Cons cons = (Cons) enumeration.nextElement();
0320: cl.addField(((Boolean) ((Cons) cons.car).car)
0321: .booleanValue(),
0322: (String) ((Cons) cons.car).cdr,
0323: (String) ((Cons) cons.cdr).car,
0324: ((Integer) ((Cons) cons.cdr).cdr).intValue());
0325: }
0326: } else {
0327: throw new ParseException("Inconsistent class file.");
0328: }
0329: }
0330:
0331: /** Mark an attribute type for retention. */
0332: public void retainAttribute(String name) {
0333: retainAttrs.addElement(name);
0334: }
0335:
0336: private boolean modifierMatch(int level, int mods) {
0337: if (level == YGuardRule.LEVEL_NONE)
0338: return false;
0339: if (Modifier.isPublic(mods)) {
0340: return (level & YGuardRule.PUBLIC) == YGuardRule.PUBLIC;
0341: }
0342: if (Modifier.isProtected(mods)) {
0343: return (level & YGuardRule.PROTECTED) == YGuardRule.PROTECTED;
0344: }
0345: if (Modifier.isPrivate(mods)) {
0346: return (level & YGuardRule.PRIVATE) == YGuardRule.PRIVATE;
0347: }
0348: // package friendly left only
0349: return (level & YGuardRule.FRIENDLY) == YGuardRule.FRIENDLY;
0350: }
0351:
0352: /** Mark a class/interface type (and possibly methods and fields defined in class) for retention. */
0353: public void retainClass(String name, int classLevel,
0354: int methodLevel, int fieldLevel, boolean retainHierarchy) {
0355:
0356: // Mark the class (or classes, if this is a wildcarded specifier)
0357: for (Enumeration clEnum = getClEnum(name, classLevel); clEnum
0358: .hasMoreElements();) {
0359: Cl classItem = (Cl) clEnum.nextElement();
0360: if (retainHierarchy && classLevel != YGuardRule.LEVEL_NONE)
0361: retainHierarchy(classItem);
0362: // Retain methods if requested
0363: if (methodLevel != YGuardRule.LEVEL_NONE) {
0364: for (Enumeration enumeration = classItem
0365: .getMethodEnum(); enumeration.hasMoreElements();) {
0366: Md md = (Md) enumeration.nextElement();
0367: if (modifierMatch(methodLevel, md.getModifiers())) {
0368: md.setOutName(md.getInName());
0369: md.setFromScript();
0370: }
0371: }
0372: // do super classes and interfaces...
0373: if ((methodLevel & (YGuardRule.PUBLIC
0374: | YGuardRule.PROTECTED | YGuardRule.FRIENDLY)) != 0
0375: || (fieldLevel & (YGuardRule.PUBLIC
0376: | YGuardRule.PROTECTED | YGuardRule.FRIENDLY)) != 0) {
0377: int mask = YGuardRule.PRIVATE;
0378: int ml = methodLevel & ~mask;
0379: int fl = fieldLevel & ~mask;
0380: int cl = classLevel & ~mask;
0381: String[] interfaces = classItem.getInterfaces();
0382: if (interfaces != null) {
0383: for (int i = 0; i < interfaces.length; i++) {
0384: String interfaceClass = interfaces[i];
0385: retainClass(interfaceClass, cl, ml, fl,
0386: false);
0387: }
0388: }
0389: String super Class = classItem.getSuperClass();
0390: if (super Class != null) {
0391: // staying in package?!
0392: if (!super Class.startsWith(classItem
0393: .getParent().getFullInName())) {
0394: mask |= YGuardRule.FRIENDLY;
0395: ml = methodLevel & ~mask;
0396: fl = fieldLevel & ~mask;
0397: cl = classLevel & ~mask;
0398: }
0399: retainClass(super Class, cl, ml, fl, false);
0400: }
0401: }
0402: }
0403:
0404: // Retain fields if requested
0405: if (fieldLevel != YGuardRule.LEVEL_NONE) {
0406: for (Enumeration enumeration = classItem.getFieldEnum(); enumeration
0407: .hasMoreElements();) {
0408: Fd fd = (Fd) enumeration.nextElement();
0409: if (modifierMatch(fieldLevel, fd.getModifiers())) {
0410: fd.setOutName(fd.getInName());
0411: fd.setFromScript();
0412: }
0413: }
0414: }
0415: }
0416: }
0417:
0418: /** Mark a method type for retention. */
0419: public void retainMethod(String name, String descriptor) {
0420: for (Enumeration enumeration = getMdEnum(name, descriptor); enumeration
0421: .hasMoreElements();) {
0422: Md md = (Md) enumeration.nextElement();
0423: md.setOutName(md.getInName());
0424: md.setFromScript();
0425: }
0426: }
0427:
0428: /** Mark a field type for retention. */
0429: public void retainField(String name) {
0430: for (Enumeration enumeration = getFdEnum(name); enumeration
0431: .hasMoreElements();) {
0432: Fd fd = (Fd) enumeration.nextElement();
0433: fd.setOutName(fd.getInName());
0434: fd.setFromScript();
0435: }
0436: }
0437:
0438: /** Mark a package for retention, and specify its new name. */
0439: public void retainPackageMap(String name, String obfName) {
0440: retainItemMap(getPk(name), obfName);
0441: }
0442:
0443: /** Mark a class/interface type for retention, and specify its new name. */
0444: public void retainClassMap(String name, String obfName) {
0445: retainItemMap(getCl(name), obfName);
0446: }
0447:
0448: /** Mark a method type for retention, and specify its new name. */
0449: public void retainMethodMap(String name, String descriptor,
0450: String obfName) {
0451: retainItemMap(getMd(name, descriptor), obfName);
0452: }
0453:
0454: /** Mark a field type for retention, and specify its new name. */
0455: public void retainFieldMap(String name, String obfName) {
0456: retainItemMap(getFd(name), obfName);
0457: }
0458:
0459: // Mark an item for retention, and specify its new name.
0460: private void retainItemMap(TreeItem item, String obfName) {
0461: if (!item.isFixed()) {
0462: item.setOutName(obfName);
0463: item.setFromScriptMap();
0464: } else {
0465: if (!item.getOutName().equals(obfName)) {
0466: item.setOutName(obfName);
0467: item.setFromScriptMap();
0468: // do warning
0469: Logger.getInstance().warning(
0470: "'" + item.getFullInName()
0471: + "' will be remapped to '" + obfName
0472: + "' according to mapping rule!");
0473: }
0474: }
0475: // if (!item.isFixed())
0476: // {
0477: // item.setFromScriptMap();
0478: // }
0479: // item.setOutName(obfName);
0480: }
0481:
0482: /** Traverse the class tree, generating obfuscated names within each namespace. */
0483: public void generateNames() {
0484: walkTree(new TreeAction() {
0485: public void packageAction(Pk pk) {
0486: pk.generateNames();
0487: }
0488:
0489: public void classAction(Cl cl) {
0490: cl.generateNames();
0491: }
0492: });
0493: }
0494:
0495: /** Resolve the polymorphic dependencies of each class. */
0496: public void resolveClasses() throws ClassNotFoundException {
0497: walkTree(new TreeAction() {
0498: public void classAction(Cl cl) {
0499: cl.resetResolve();
0500: }
0501: });
0502: walkTree(new TreeAction() {
0503: public void classAction(Cl cl) {
0504: cl.setupNameListDowns();
0505: }
0506: });
0507: Cl.nameSpace = 0;
0508: final ClassNotFoundException[] ex = new ClassNotFoundException[1];
0509: try {
0510: walkTree(new TreeAction() {
0511: public void classAction(Cl cl) {
0512: try {
0513: cl.resolveOptimally();
0514: } catch (ClassNotFoundException cnfe) {
0515: ex[0] = cnfe;
0516: throw new RuntimeException();
0517: }
0518: }
0519: });
0520: } catch (RuntimeException rte) {
0521: if (ex[0] != null) {
0522: throw ex[0];
0523: } else {
0524: throw rte;
0525: }
0526: }
0527: }
0528:
0529: /** Return a list of attributes marked to keep. */
0530: public String[] getAttrsToKeep() {
0531: String[] attrs = new String[retainAttrs.size()];
0532: for (int i = 0; i < attrs.length; i++) {
0533: attrs[i] = (String) retainAttrs.elementAt(i);
0534: }
0535: return attrs;
0536: }
0537:
0538: /** Get classes in tree from the fully qualified name
0539: (can be wildcarded). */
0540: public Enumeration getClEnum(String fullName) {
0541: return getClEnum(fullName, YGuardRule.LEVEL_PRIVATE);
0542: }
0543:
0544: /** Get classes in tree from the fully qualified name
0545: (can be wildcarded). */
0546: public Enumeration getClEnum(String fullName, final int classMode) {
0547: final Vector vec = new Vector();
0548:
0549: // Wildcarded?
0550: // Then return list of all classes (including inner classes) in package
0551: if (fullName.indexOf('*') != -1) {
0552: // Recursive?
0553: if (fullName.indexOf('!') == 0) {
0554: final String fName = fullName.substring(1);
0555: walkTree(new TreeAction() {
0556: public void classAction(Cl cl) {
0557: if (cl.isWildcardMatch(fName)
0558: && modifierMatch(classMode, cl
0559: .getModifiers())) {
0560: vec.addElement(cl);
0561: }
0562: }
0563: });
0564: } else {
0565: // non-recursive
0566: final String fName = fullName;
0567: walkTree(new TreeAction() {
0568: public void classAction(Cl cl) {
0569: if (cl.isNRWildcardMatch(fName)
0570: && modifierMatch(classMode, cl
0571: .getModifiers())) {
0572: vec.addElement(cl);
0573: }
0574: }
0575: });
0576: }
0577: } else {
0578: // Single class
0579: Cl cl = getCl(fullName);
0580: if (cl != null) {
0581: int mods = cl.getModifiers();
0582: if (cl.isInnerClass()) {
0583: Cl outer = (Cl) cl.getParent();
0584: }
0585: boolean match = modifierMatch(classMode, cl
0586: .getModifiers());
0587: if (match || classMode == YGuardRule.LEVEL_NONE) { //(RW)
0588: vec.addElement(cl);
0589: }
0590: }
0591: }
0592: return vec.elements();
0593: }
0594:
0595: /** Get methods in tree from the fully qualified, and possibly
0596: wildcarded, name. */
0597: public Enumeration getMdEnum(String fullName, String descriptor) {
0598: final Vector vec = new Vector();
0599: final String fDesc = descriptor;
0600: if (fullName.indexOf('*') != -1
0601: || descriptor.indexOf('*') != -1) {
0602: // Recursive?
0603: if (fullName.indexOf('!') == 0) {
0604: final String fName = fullName.substring(1);
0605: // recursive wildcarding
0606: walkTree(new TreeAction() {
0607: public void methodAction(Md md) {
0608: if (md.isWildcardMatch(fName, fDesc)) {
0609: vec.addElement(md);
0610: }
0611: }
0612: });
0613: } else {
0614: final String fName = fullName;
0615: // non-recursive wildcarding
0616: walkTree(new TreeAction() {
0617: public void methodAction(Md md) {
0618: if (md.isNRWildcardMatch(fName, fDesc)) {
0619: vec.addElement(md);
0620: }
0621: }
0622: });
0623: }
0624: } else {
0625: Md md = getMd(fullName, descriptor);
0626: if (md != null) {
0627: vec.addElement(md);
0628: }
0629: }
0630: return vec.elements();
0631: }
0632:
0633: /** Get fields in tree from the fully qualified, and possibly
0634: wildcarded, name. */
0635: public Enumeration getFdEnum(String fullName) {
0636: final Vector vec = new Vector();
0637: if (fullName.indexOf('*') != -1) {
0638: // Recursive?
0639: if (fullName.indexOf('!') == 0) {
0640: // recursive wildcarding
0641: final String fName = fullName.substring(1);
0642: walkTree(new TreeAction() {
0643: public void fieldAction(Fd fd) {
0644: if (fd.isWildcardMatch(fName)) {
0645: vec.addElement(fd);
0646: }
0647: }
0648: });
0649: } else {
0650: // non-recursive wildcarding
0651: final String fName = fullName;
0652: walkTree(new TreeAction() {
0653: public void fieldAction(Fd fd) {
0654: if (fd.isNRWildcardMatch(fName)) {
0655: vec.addElement(fd);
0656: }
0657: }
0658: });
0659: }
0660: } else {
0661: Fd fd = getFd(fullName);
0662: if (fd != null) {
0663: vec.addElement(fd);
0664: }
0665: }
0666: return vec.elements();
0667: }
0668:
0669: /** Get class in tree from the fully qualified name, returning null if name not found. */
0670: public Cl getCl(String fullName) {
0671: TreeItem ti = root;
0672: for (Enumeration nameEnum = getNameEnum(fullName); nameEnum
0673: .hasMoreElements();) {
0674: Cons nameSegment = (Cons) nameEnum.nextElement();
0675: char tag = ((Character) nameSegment.car).charValue();
0676: String name = (String) nameSegment.cdr;
0677: switch (tag) {
0678: case PACKAGE_LEVEL:
0679: ti = ((Pk) ti).getPackage(name);
0680: break;
0681:
0682: case CLASS_LEVEL:
0683: ti = ((PkCl) ti).getClass(name);
0684: break;
0685:
0686: default:
0687: throw new ParseException(
0688: "Internal error: illegal package/class name tag");
0689: }
0690:
0691: // If the name is not in the database, return null
0692: if (ti == null) {
0693: return null;
0694: }
0695: }
0696:
0697: // It is an error if we do not end up with a class or interface
0698: if (!(ti instanceof Cl)) {
0699: throw new ParseException(
0700: "Inconsistent class or interface name.");
0701: }
0702: return (Cl) ti;
0703: }
0704:
0705: /** Get package in tree from the fully qualified name, returning null if name not found. */
0706: public Pk getPk(String fullName) {
0707: TreeItem ti = root;
0708: for (Enumeration nameEnum = getNameEnum(fullName); nameEnum
0709: .hasMoreElements();) {
0710: Cons nameSegment = (Cons) nameEnum.nextElement();
0711: String name = (String) nameSegment.cdr;
0712: ti = ((Pk) ti).getPackage(name);
0713:
0714: // If the name is not in the database, return null
0715: if (ti == null) {
0716: return null;
0717: }
0718: // It is an error if we do not end up with a package
0719: if (!(ti instanceof Pk)) {
0720: throw new ParseException("Inconsistent package.");
0721: }
0722: }
0723: return (Pk) ti;
0724: }
0725:
0726: /** Get method in tree from the fully qualified name. */
0727: public Md getMd(String fullName, String descriptor) {
0728: // Split into class and method names
0729: int pos = fullName.lastIndexOf(METHOD_FIELD_LEVEL);
0730: Cl cl = getCl(fullName.substring(0, pos));
0731: return cl.getMethod(fullName.substring(pos + 1), descriptor);
0732: }
0733:
0734: /** Get field in tree from the fully qualified name. */
0735: public Fd getFd(String fullName) {
0736: // Split into class and field names
0737: int pos = fullName.lastIndexOf(METHOD_FIELD_LEVEL);
0738: Cl cl = getCl(fullName.substring(0, pos));
0739: return cl.getField(fullName.substring(pos + 1));
0740: }
0741:
0742: public String[] getAttrsToKeep(String className) {
0743: Cl cl = getCl(className);
0744: if (cl != null) {
0745: Set attrs = cl.getAttributesToKeep();
0746: if (attrs != null && attrs.size() > 0) {
0747: String[] other = getAttrsToKeep();
0748: Set tmp = new HashSet(attrs);
0749: for (int i = 0; i < other.length; i++) {
0750: tmp.add(other[i]);
0751: }
0752: return (String[]) tmp.toArray(new String[tmp.size()]);
0753: } else {
0754: return getAttrsToKeep();
0755: }
0756: } else {
0757: return getAttrsToKeep();
0758: }
0759: }
0760:
0761: public String mapLocalVariable(String this ClassName,
0762: String methodName, String descriptor, String string) {
0763: return string;
0764: }
0765:
0766: /** Mapping for fully qualified class name.
0767: * @see NameMapper#mapClass */
0768: public String mapClass(String className) {
0769: // System.out.println("map class " + className);
0770: // Check for array -- requires special handling
0771: if (className.length() > 0 && className.charAt(0) == '[') {
0772: StringBuffer newName = new StringBuffer();
0773: int i = 0;
0774: while (i < className.length()) {
0775: char ch = className.charAt(i++);
0776: switch (ch) {
0777: case '[':
0778: case ';':
0779: newName.append(ch);
0780: break;
0781:
0782: case 'L':
0783: newName.append(ch);
0784: int pos = className.indexOf(';', i);
0785: if (pos < 0) {
0786: throw new ParseException(
0787: "Invalid class name encountered: "
0788: + className);
0789: }
0790: newName
0791: .append(mapClass(className
0792: .substring(i, pos)));
0793: i = pos;
0794: break;
0795:
0796: default:
0797: return className;
0798: }
0799: }
0800: return newName.toString();
0801: } else {
0802: Cl cl = getCl(className);
0803: if (cl == null) {
0804: try {
0805: Class aClass = Cl.getClassResolver().resolve(
0806: Conversion.toJavaClass(className));
0807: // ok class exists...
0808: return className;
0809: } catch (ClassNotFoundException e) {
0810: if (pedantic) {
0811: throw new NoSuchMappingException("Class "
0812: + Conversion.toJavaClass(className));
0813: } else {
0814: Logger.getInstance().warningToLogfile(
0815: "Unresolved external dependency: "
0816: + Conversion
0817: .toJavaClass(className)
0818: + " not found!");
0819: Logger.getInstance().setUnresolved();
0820: return className;
0821: }
0822: }
0823: }
0824: return cl.getFullOutName();
0825: }
0826: }
0827:
0828: /** Mapping for method name, of fully qualified class.
0829: * @see NameMapper#mapMethod */
0830: public String mapMethod(String className, String methodName,
0831: String descriptor) {
0832: // check if the className is an array...
0833: if (className.startsWith("[") && className.endsWith(";")) {
0834: int count = 0;
0835: while (className.charAt(count) == '[') {
0836: count++;
0837: }
0838: if (className.charAt(count) == 'L') {
0839: className = className.substring(count + 1, className
0840: .length() - 1);
0841: }
0842: }
0843: Cl cl = getCl(className);
0844: if (cl != null && cl.getMethod(methodName, descriptor) != null) {
0845: return cl.getMethod(methodName, descriptor).getOutName();
0846: } else {
0847: if (cl == null) {
0848: try {
0849: Class aClass = Cl.getClassResolver().resolve(
0850: Conversion.toJavaClass(className));
0851: } catch (ClassNotFoundException e) {
0852: if (pedantic) {
0853: throw new NoSuchMappingException("Class "
0854: + Conversion.toJavaClass(className));
0855: } else {
0856: Logger
0857: .getInstance()
0858: .warningToLogfile(
0859: "No mapping found: "
0860: + Conversion
0861: .toJavaClass(className));
0862: }
0863: }
0864: // method is not in database use unobfuscated name...
0865: return methodName;
0866: } else {
0867: try {
0868: // System.out.println("Try: "+cl.getFieldObfNameUp(fieldName));
0869: String result = cl.getMethodOutNameUp(methodName,
0870: descriptor);
0871: if (result != null)
0872: return result;
0873: } catch (Exception ex) {
0874: System.out.println(ex);
0875: //System.out.println("ME: Error: Try not succeeded");
0876: }
0877: if ((!methodName.equals("<init>") && (!methodName
0878: .equals("<clinit>")))) {
0879: if (pedantic) {
0880: throw new NoSuchMappingException("Method "
0881: + Conversion.toJavaClass(className)
0882: + "." + methodName);
0883: } else {
0884: Logger
0885: .getInstance()
0886: .error(
0887: "Method "
0888: + Conversion
0889: .toJavaClass(className)
0890: + "."
0891: + methodName
0892: + " could not be mapped !\n Probably broken code! Try rebuilding from source!");
0893: return methodName;
0894: }
0895: }
0896: return methodName;
0897: }
0898: }
0899: }
0900:
0901: /** Mapping for annotation field/method name, of fully qualified class.
0902: * @see NameMapper#mapAnnotationField */
0903: public String mapAnnotationField(String className, String methodName) {
0904: Cl cl = getCl(className);
0905: if (cl != null) {
0906: for (Enumeration enumeration = cl.getMethodEnum(); enumeration
0907: .hasMoreElements();) {
0908: Md md = (Md) enumeration.nextElement();
0909: if (md.getInName().equals(methodName)) {
0910: return md.getOutName();
0911: }
0912: }
0913: // actually this should not happen - is this an exception?!
0914: return methodName;
0915: } else {
0916: // method is not in database use unobfuscated name...
0917: return methodName;
0918: }
0919: }
0920:
0921: /** Mapping for field name, of fully qualified class.
0922: * @see NameMapper#mapField */
0923: public String mapField(String className, String fieldName) {
0924: // System.out.println("Map "+className+"."+fieldName);
0925: Cl cl = getCl(className);
0926: if ((cl != null) && (cl.getField(fieldName) != null)) {
0927: //special .class construct name mapping....
0928: if (fieldName.startsWith("class$")
0929: && isReplaceClassNameStrings()) {
0930: String realClassName = fieldName.substring(6);
0931: List nameParts = new ArrayList(20);
0932: for (StringTokenizer st = new StringTokenizer(
0933: realClassName, "$", false); st.hasMoreTokens();) {
0934: nameParts.add(st.nextToken());
0935: }
0936: String[] names = new String[nameParts.size()];
0937: nameParts.toArray(names);
0938: TreeItem ti = findTreeItem(names);
0939: if (ti instanceof Cl) {
0940: Fd fd = cl.getField(fieldName);
0941: String newClassName = mapClass(ti.getFullInName());
0942: String outName = "class$"
0943: + newClassName.replace('/', '$');
0944: fd.setOutName(outName);
0945: return outName;
0946: }
0947: }
0948: // System.out.println("Standard Field Map");
0949: return cl.getField(fieldName).getOutName();
0950: } else {
0951: if (cl == null) {
0952: // System.out.println("Error: "+className+
0953: // " class not found !");
0954: return fieldName;
0955: } else {
0956: // System.out.println("ERROR: "+className+"."+fieldName+
0957: // " cannot be mapped !");
0958: try {
0959: // System.out.println("Try: "+cl.getFieldObfNameUp(fieldName));
0960: String result = cl.getFieldOutNameUp(fieldName);
0961: if (result != null)
0962: return result;
0963: } catch (Exception ex) {
0964: // System.out.println("Try not succeeded");
0965: }
0966: if (!fieldName.equals("this")) {
0967: if (pedantic) {
0968: throw new NoSuchMappingException("Field "
0969: + className + "." + fieldName);
0970: } else {
0971: Logger
0972: .getInstance()
0973: .error(
0974: "Field "
0975: + className
0976: + "."
0977: + fieldName
0978: + " could not be mapped !\n Probably broken code! Try rebuilding from source!");
0979: }
0980: }
0981: return fieldName;
0982: }
0983: }
0984:
0985: // return cl != null && cl.getField(fieldName) != null ?
0986: // cl.getField(fieldName).getOutName() :
0987: // fieldName;
0988: }
0989:
0990: /** Mapping for signatures (used for generics in 1.5).
0991: * @see NameMapper#mapSignature
0992: */
0993: public String mapSignature(String signature) {
0994: // Pass everything through unchanged, except for the String between
0995: // 'L' and ';' -- this is passed through mapClass(String)
0996:
0997: // System.out.println( "signature: "+signature );
0998:
0999: StringBuffer classString = new StringBuffer();
1000:
1001: StringBuffer newSignature = new StringBuffer();
1002: int i = 0;
1003: while (i < signature.length()) {
1004: char ch = signature.charAt(i++);
1005: switch (ch) {
1006: case '[':
1007: case 'B':
1008: case 'C':
1009: case 'D':
1010: case 'F':
1011: case 'I':
1012: case 'J':
1013: case 'S':
1014: case 'Z':
1015: case 'V':
1016: case '(':
1017: case ')':
1018: case '+':
1019: case ':':
1020: case '-':
1021: case '*':
1022: newSignature.append(ch);
1023: break;
1024: case ';':
1025: newSignature.append(ch);
1026: classString.setLength(0);
1027: break;
1028: case 'T': { // Template name
1029: newSignature.append(ch);
1030: int pos = signature.indexOf(';', i);
1031: if (pos < 0) {
1032: throw new ParseException(
1033: "Invalid signature string encountered.");
1034: }
1035: newSignature.append(signature.substring(i, pos));
1036: i = pos;
1037: break;
1038: }
1039: case '<': {
1040: // formal parameters
1041: newSignature.append(ch);
1042: while (true) {
1043: int first = i;
1044: while (signature.charAt(i) != ':') {
1045: i++;
1046: }
1047: String templateName = signature.substring(first, i);
1048: newSignature.append(templateName);
1049:
1050: while (signature.charAt(i) == ':') {
1051: newSignature.append(':');
1052: i++;
1053: int firstPos = i;
1054: int bracketCount = 0;
1055: while (!(bracketCount == 0 && signature
1056: .charAt(i) == ';')) {
1057: if (signature.charAt(i) == '<') {
1058: bracketCount++;
1059: } else if (signature.charAt(i) == '>') {
1060: bracketCount--;
1061: }
1062: i++;
1063: }
1064: i++;
1065: newSignature.append(mapSignature(signature
1066: .substring(firstPos, i)));
1067: }
1068: if (signature.charAt(i) == '>') {
1069: newSignature.append('>');
1070: i++;
1071: break;
1072: }
1073: }
1074: break;
1075: }
1076:
1077: case '^': {
1078: newSignature.append(ch);
1079: if (signature.charAt(i) == 'T') {
1080: // identifier
1081: while (signature.charAt(i) != ';') {
1082: newSignature.append(signature.charAt(i));
1083: i++;
1084: }
1085: continue;
1086: } else if (signature.charAt(i) == 'L') {
1087: // class
1088: int first = i;
1089: int bracketCount = 0;
1090: while (signature.charAt(i) != ';'
1091: || bracketCount != 0) {
1092: char c = signature.charAt(i);
1093: if (c == '<') {
1094: bracketCount++;
1095: } else if (c == '>') {
1096: bracketCount--;
1097: }
1098: i++;
1099: }
1100: i++;
1101: String classSig = signature.substring(first, i);
1102: newSignature.append(mapSignature(classSig));
1103: } else {
1104: throw new IllegalStateException(
1105: "Could not map signature " + signature);
1106: }
1107: }
1108: break;
1109: case 'L':
1110: case '.': // inner class
1111: {
1112: newSignature.append(ch);
1113: int pos = signature.indexOf(';', i);
1114: int bracketPos = signature.indexOf('<', i);
1115: if (bracketPos >= i && bracketPos < pos) {
1116: // found a bracket - find the matching one..
1117: int bracketCount = 0;
1118: int closingBracket = signature.length();
1119: for (int walker = bracketPos + 1; walker < signature
1120: .length(); walker++) {
1121: char c = signature.charAt(walker);
1122: if (c == '<') {
1123: bracketCount++;
1124: } else if (c == '>') {
1125: if (bracketCount == 0) {
1126: closingBracket = walker;
1127: break;
1128: } else {
1129: bracketCount--;
1130: }
1131: }
1132: }
1133: // TODO check!!!!
1134: pos = closingBracket + 1;
1135: // pos = signature.indexOf(';', closingBracket);
1136: String templateArg = signature.substring(
1137: bracketPos + 1, closingBracket);
1138: String classNamePart = signature.substring(i,
1139: bracketPos);
1140: if (ch == '.') { // inner class part - translate to class file name
1141: classString.append('$');
1142: classString.append(classNamePart);
1143: String className = classString.toString();
1144: String result;
1145:
1146: // do basically the same that mapClass() does, but return the last part only.
1147: Cl cl = getCl(className);
1148: if (cl == null) {
1149: try {
1150: Class aClass = Cl
1151: .getClassResolver()
1152: .resolve(
1153: Conversion
1154: .toJavaClass(className));
1155: // ok class exists...
1156: result = classNamePart;
1157: } catch (ClassNotFoundException e) {
1158: if (pedantic) {
1159: throw new NoSuchMappingException(
1160: "Class "
1161: + Conversion
1162: .toJavaClass(className));
1163: } else {
1164: Logger
1165: .getInstance()
1166: .warningToLogfile(
1167: "Unresolved external dependency: "
1168: + Conversion
1169: .toJavaClass(className)
1170: + " not found!");
1171: Logger.getInstance()
1172: .setUnresolved();
1173: result = classNamePart;
1174: }
1175: }
1176: } else {
1177: result = cl.getOutName();
1178: }
1179: newSignature.append(result);
1180: } else { // toplevel class 'L'
1181: classString.append(classNamePart);
1182: newSignature.append(mapClass(classString
1183: .toString()));
1184: }
1185: newSignature.append('<');
1186: newSignature.append(mapSignature(templateArg));
1187: newSignature.append('>');
1188: i = pos;
1189: } else {
1190: if (pos < 0) {
1191: throw new ParseException(
1192: "Invalid signature string encountered: "
1193: + signature);
1194: }
1195: String classNamePart = signature.substring(i, pos);
1196: if (ch == '.') { // inner class part - translate to class file name
1197: classString.append('$');
1198: }
1199: classString.append(classNamePart);
1200: newSignature
1201: .append(mapClass(classString.toString()));
1202: i = pos;
1203: }
1204: break;
1205: }
1206: default:
1207: throw new ParseException(
1208: "Invalid signature string encountered: "
1209: + signature + " parsing char " + ch);
1210: }
1211: }
1212: return newSignature.toString();
1213: }
1214:
1215: public String mapSourceFile(String className, String sourceFileName) {
1216: final Cl cl = getCl(className);
1217: if (cl.isSourceFileMappingSet()) {
1218: return cl.getSourceFileMapping();
1219: } else {
1220: return sourceFileName;
1221: }
1222: }
1223:
1224: public boolean mapLineNumberTable(String className,
1225: String methodName, String methodSignature,
1226: LineNumberTableAttrInfo info) {
1227: final Cl cl = getCl(className);
1228: if (cl.getLineNumberTableMapper() != null) {
1229: return cl.getLineNumberTableMapper().mapLineNumberTable(
1230: className, methodName, methodSignature, info);
1231: } else {
1232: return true;
1233: }
1234: }
1235:
1236: /** Mapping for descriptor of field or method.
1237: * @see NameMapper#mapDescriptor */
1238: public String mapDescriptor(String descriptor) {
1239: // Pass everything through unchanged, except for the String between
1240: // 'L' and ';' -- this is passed through mapClass(String)
1241: StringBuffer newDesc = new StringBuffer();
1242: int i = 0;
1243: while (i < descriptor.length()) {
1244: char ch = descriptor.charAt(i++);
1245: switch (ch) {
1246: case '[':
1247: case 'B':
1248: case 'C':
1249: case 'D':
1250: case 'F':
1251: case 'I':
1252: case 'J':
1253: case 'S':
1254: case 'Z':
1255: case 'V':
1256: case '(':
1257: case ')':
1258: case ';':
1259: newDesc.append(ch);
1260: break;
1261:
1262: case 'L':
1263: newDesc.append(ch);
1264: int pos = descriptor.indexOf(';', i);
1265: if (pos < 0) {
1266: throw new ParseException(
1267: "Invalid descriptor string encountered.");
1268: }
1269: newDesc.append(mapClass(descriptor.substring(i, pos)));
1270: i = pos;
1271: break;
1272:
1273: default:
1274: throw new ParseException(
1275: "Invalid descriptor string encountered.");
1276: }
1277: }
1278: return newDesc.toString();
1279: }
1280:
1281: /** Dump the content of the class tree to the specified file (used for logging). */
1282: public void dump(final PrintWriter log) {
1283: log.println("<expose>");
1284: walkTree(new TreeAction() {
1285: public void classAction(Cl cl) {
1286: if (cl.isFromScript()) {
1287: String cla = toUtf8XmlString(Conversion
1288: .toJavaClass(cl.getFullInName()));
1289: log.println(" <class name=\"" + cla + "\"/>");
1290: }
1291: }
1292:
1293: public void methodAction(Md md) {
1294: if (md.isFromScript()) {
1295: String cla = toUtf8XmlString(Conversion
1296: .toJavaClass(md.getParent().getFullInName()));
1297: String method = toUtf8XmlString(Conversion
1298: .toJavaMethod(md.getInName(), md
1299: .getDescriptor()));
1300: log.println(" <method class=\"" + cla
1301: + "\" name=\"" + method + "\"/>");
1302: }
1303: }
1304:
1305: public void fieldAction(Fd fd) {
1306: if (fd.isFromScript()) {
1307: String cla = toUtf8XmlString(Conversion
1308: .toJavaClass(fd.getParent().getFullInName()));
1309: log.println(" <field class=\"" + cla
1310: + "\" name=\""
1311: + toUtf8XmlString(fd.getInName()) + "\"/>");
1312: }
1313: }
1314:
1315: public void packageAction(Pk pk) {
1316: // No action
1317: }
1318: });
1319: log.println("</expose>");
1320: log.println("<map>");
1321: walkTree(new TreeAction() {
1322: public void classAction(Cl cl) {
1323: if (!cl.isFromScript()) {
1324: String cla = toUtf8XmlString(Conversion
1325: .toJavaClass(cl.getFullInName()));
1326: log
1327: .println(" <class name=\""
1328: + toUtf8XmlString(cla)
1329: + "\" map=\""
1330: + toUtf8XmlString(cl.getOutName())
1331: + "\"/>");
1332: }
1333: }
1334:
1335: public void methodAction(Md md) {
1336: if (!md.isFromScript()) {
1337: String cla = toUtf8XmlString(Conversion
1338: .toJavaClass(md.getParent().getFullInName()));
1339: String method = toUtf8XmlString(Conversion
1340: .toJavaMethod(md.getInName(), md
1341: .getDescriptor()));
1342: log
1343: .println(" <method class=\"" + cla
1344: + "\" name=\"" + method
1345: + "\" map=\""
1346: + toUtf8XmlString(md.getOutName())
1347: + "\"/>");
1348: }
1349: }
1350:
1351: public void fieldAction(Fd fd) {
1352: if (!fd.isFromScript()) {
1353: String cla = toUtf8XmlString(Conversion
1354: .toJavaClass(fd.getParent().getFullInName()));
1355: log
1356: .println(" <field class=\"" + cla
1357: + "\" name=\""
1358: + toUtf8XmlString(fd.getInName())
1359: + "\" map=\""
1360: + toUtf8XmlString(fd.getOutName())
1361: + "\"/>");
1362: }
1363: }
1364:
1365: public void packageAction(Pk pk) {
1366: if (!pk.isFromScript()
1367: && pk.getFullInName().length() > 0) {
1368: String pa = toUtf8XmlString(Conversion
1369: .toJavaClass(pk.getFullInName()));
1370: log
1371: .println(" <package name=\"" + pa
1372: + "\" map=\""
1373: + toUtf8XmlString(pk.getOutName())
1374: + "\"/>");
1375: }
1376: }
1377: });
1378: log.println("</map>");
1379: }
1380:
1381: public static final String toUtf8XmlString(String s) {
1382: boolean bad = false;
1383: for (int i = 0; i < s.length(); i++) {
1384: char c = s.charAt(i);
1385: if ((c >= 0x80) || (c == '"') || (c == '<')) {
1386: bad = true;
1387: break;
1388: }
1389: }
1390: if (bad) {
1391: StringBuffer buf = new StringBuffer(s.length());
1392: for (int i = 0; i < s.length(); i++) {
1393: buf.append(toUtf8XmlChar(s.charAt(i)));
1394: }
1395: return buf.toString();
1396: } else {
1397: return s;
1398: }
1399: }
1400:
1401: private static final String toUtf8XmlChar(char c) {
1402: if (c < 0x80) {
1403: if (c == '"') {
1404: return """;
1405: } else if (c == '<') {
1406: return "<";
1407: }
1408: return new String(new char[] { c });
1409: } else if (c < 0x800) {
1410: StringBuffer buf = new StringBuffer(8);
1411: buf.append("&#x");
1412: buf.append(hex[(c >> 8) & 0xff]);
1413: buf.append(hex[c & 0xff]);
1414: buf.append(';');
1415: return buf.toString();
1416: } else {
1417: StringBuffer buf = new StringBuffer(10);
1418: buf.append("&#x");
1419: buf.append(hex[(c >> 16) & 0xff]);
1420: buf.append(hex[(c >> 8) & 0xff]);
1421: buf.append(hex[c & 0xff]);
1422: buf.append(';');
1423: return buf.toString();
1424: }
1425: }
1426:
1427: private static final String[] hex;
1428: static {
1429: hex = new String[256];
1430: for (int i = 0; i < 256; i++) {
1431: hex[i] = toHex(i);
1432: }
1433: }
1434:
1435: private static final String hexChars = "0123456789abcdef";
1436:
1437: /** Holds value of property replaceClassNameStrings. */
1438: private boolean replaceClassNameStrings;
1439:
1440: /** Holds value of property pedantic. */
1441: private boolean pedantic;
1442:
1443: private static String toHex(int i) {
1444: StringBuffer buf = new StringBuffer(2);
1445: buf.append(hexChars.charAt((i / 16) & 15));
1446: buf.append(hexChars.charAt(i & 15));
1447: return buf.toString();
1448: }
1449:
1450: // Private Methods -------------------------------------------------------
1451: // Mark TreeItem and all parents for retention.
1452: private void retainHierarchy(TreeItem ti) {
1453: if (!ti.isFixed()) {
1454: ti.setOutName(ti.getInName());
1455: ti.setFromScript();
1456: }
1457: if (ti.parent != null) {
1458: retainHierarchy(ti.parent);
1459: }
1460: }
1461:
1462: /** Walk the whole tree taking action once only on each package level, class, method and field. */
1463: public void walkTree(TreeAction ta) {
1464: walkTree(ta, root);
1465: }
1466:
1467: // Walk the tree which has TreeItem as its root taking action once only on each
1468: // package level, class, method and field.
1469: private void walkTree(TreeAction ta, TreeItem ti) {
1470: if (ti instanceof Pk) {
1471: Enumeration packageEnum = ((Pk) ti).getPackageEnum();
1472: ta.packageAction((Pk) ti);
1473: while (packageEnum.hasMoreElements()) {
1474: walkTree(ta, (TreeItem) packageEnum.nextElement());
1475: }
1476: }
1477: if (ti instanceof PkCl) {
1478: Enumeration classEnum = ((PkCl) ti).getClassEnum();
1479: while (classEnum.hasMoreElements()) {
1480: walkTree(ta, (TreeItem) classEnum.nextElement());
1481: }
1482: }
1483: if (ti instanceof Cl) {
1484: Enumeration fieldEnum = ((Cl) ti).getFieldEnum();
1485: Enumeration methodEnum = ((Cl) ti).getMethodEnum();
1486: ta.classAction((Cl) ti);
1487: while (fieldEnum.hasMoreElements()) {
1488: ta.fieldAction((Fd) fieldEnum.nextElement());
1489: }
1490: while (methodEnum.hasMoreElements()) {
1491: ta.methodAction((Md) methodEnum.nextElement());
1492: }
1493: }
1494: }
1495:
1496: /** Getter for property replaceClassNameStrings.
1497: * @return Value of property replaceClassNameStrings.
1498: *
1499: */
1500: public boolean isReplaceClassNameStrings() {
1501: return this .replaceClassNameStrings;
1502: }
1503:
1504: /** Setter for property replaceClassNameStrings.
1505: * @param replaceClassNameStrings New value of property replaceClassNameStrings.
1506: *
1507: */
1508: public void setReplaceClassNameStrings(
1509: boolean replaceClassNameStrings) {
1510: this .replaceClassNameStrings = replaceClassNameStrings;
1511: }
1512:
1513: /** Getter for property pedantic.
1514: * @return Value of property pedantic.
1515: *
1516: */
1517: public boolean isPedantic() {
1518: return this .pedantic;
1519: }
1520:
1521: /** Setter for property pedantic.
1522: * @param pedantic New value of property pedantic.
1523: *
1524: */
1525: public void setPedantic(boolean pedantic) {
1526: this .pedantic = pedantic;
1527: }
1528:
1529: public void retainSourceFileAttributeMap(String name, String obfName) {
1530: for (Enumeration clEnum = getClEnum(name); clEnum
1531: .hasMoreElements();) {
1532: Cl classItem = (Cl) clEnum.nextElement();
1533: classItem.setSourceFileMapping(obfName);
1534: classItem.getAttributesToKeep().add(
1535: ClassConstants.ATTR_SourceFile);
1536: }
1537: }
1538:
1539: public void retainLineNumberTable(String name,
1540: final LineNumberTableMapper lineNumberTableMapper) {
1541: for (Enumeration clEnum = getClEnum(name); clEnum
1542: .hasMoreElements();) {
1543: Cl classItem = (Cl) clEnum.nextElement();
1544: classItem.setLineNumberTableMapper(lineNumberTableMapper);
1545: classItem.getAttributesToKeep().add(
1546: ClassConstants.ATTR_LineNumberTable);
1547: }
1548: }
1549:
1550: public void retainAttributeForClass(String className,
1551: String attributeDescriptor) {
1552: for (Enumeration clEnum = getClEnum(className); clEnum
1553: .hasMoreElements();) {
1554: Cl classItem = (Cl) clEnum.nextElement();
1555: final Set set = classItem.getAttributesToKeep();
1556: set.add(attributeDescriptor);
1557: }
1558: }
1559:
1560: public void retainPackage(String packageName) {
1561: retainHierarchy(getPk(packageName));
1562: }
1563: }
|