1/* 2 * ProGuard -- shrinking, optimization, obfuscation, and preverification 3 * of Java bytecode. 4 * 5 * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu) 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License as published by the Free 9 * Software Foundation; either version 2 of the License, or (at your option) 10 * any later version. 11 * 12 * This program is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 15 * more details. 16 * 17 * You should have received a copy of the GNU General Public License along 18 * with this program; if not, write to the Free Software Foundation, Inc., 19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 */ 21package proguard.ant; 22 23import org.apache.tools.ant.BuildException; 24import org.apache.tools.ant.types.DataType; 25import proguard.*; 26import proguard.classfile.ClassConstants; 27import proguard.classfile.util.ClassUtil; 28 29import java.util.*; 30 31/** 32 * This DataType represents a class specification in Ant. 33 * 34 * @author Eric Lafortune 35 */ 36public class ClassSpecificationElement extends DataType 37{ 38 private static final String ANY_CLASS_KEYWORD = "*"; 39 40 private String access; 41 private String annotation; 42 private String type; 43 private String name; 44 private String extendsAnnotation; 45 private String extends_; 46 private List fieldSpecifications = new ArrayList(); 47 private List methodSpecifications = new ArrayList(); 48 49 50 /** 51 * Adds the contents of this class specification element to the given list. 52 * @param classSpecifications the class specifications to be extended. 53 */ 54 public void appendTo(List classSpecifications) 55 { 56 // Get the referenced file set, or else this one. 57 ClassSpecificationElement classSpecificationElement = isReference() ? 58 (ClassSpecificationElement)getCheckedRef(this.getClass(), 59 this.getClass().getName()) : 60 this; 61 62 ClassSpecification classSpecification = 63 createClassSpecification(classSpecificationElement); 64 65 // Add it to the list. 66 classSpecifications.add(classSpecification); 67 } 68 69 70 /** 71 * Creates a new class specification corresponding to the contents of this 72 * class specification element. 73 */ 74 protected ClassSpecification createClassSpecification(ClassSpecificationElement classSpecificationElement) 75 { 76 String access = classSpecificationElement.access; 77 String annotation = classSpecificationElement.annotation; 78 String type = classSpecificationElement.type; 79 String name = classSpecificationElement.name; 80 String extendsAnnotation = classSpecificationElement.extendsAnnotation; 81 String extends_ = classSpecificationElement.extends_; 82 83 // For backward compatibility, allow a single "*" wildcard to match 84 // any class. 85 if (name != null && 86 name.equals(ANY_CLASS_KEYWORD)) 87 { 88 name = null; 89 } 90 91 ClassSpecification classSpecification = 92 new ClassSpecification(null, 93 requiredAccessFlags(true, access, type), 94 requiredAccessFlags(false, access, type), 95 annotation != null ? ClassUtil.internalType(annotation) : null, 96 name != null ? ClassUtil.internalClassName(name) : null, 97 extendsAnnotation != null ? ClassUtil.internalType(extendsAnnotation) : null, 98 extends_ != null ? ClassUtil.internalClassName(extends_) : null); 99 100 for (int index = 0; index < fieldSpecifications.size(); index++) 101 { 102 classSpecification.addField((MemberSpecification)fieldSpecifications.get(index)); 103 } 104 105 for (int index = 0; index < methodSpecifications.size(); index++) 106 { 107 classSpecification.addMethod((MemberSpecification)methodSpecifications.get(index)); 108 } 109 110 return classSpecification; 111 } 112 113 114 // Ant task attributes. 115 116 public void setAccess(String access) 117 { 118 this.access = access; 119 } 120 121 122 public void setAnnotation(String annotation) 123 { 124 this.annotation = annotation; 125 } 126 127 128 public void setType(String type) 129 { 130 this.type = type; 131 } 132 133 134 public void setName(String name) 135 { 136 this.name = name; 137 } 138 139 140 public void setExtendsannotation(String extendsAnnotation) 141 { 142 this.extendsAnnotation = extendsAnnotation; 143 } 144 145 146 public void setExtends(String extends_) 147 { 148 this.extends_ = extends_; 149 } 150 151 152 public void setImplements(String implements_) 153 { 154 this.extends_ = implements_; 155 } 156 157 158 // Ant task nested elements. 159 160 public void addConfiguredField(MemberSpecificationElement memberSpecificationElement) 161 { 162 if (fieldSpecifications == null) 163 { 164 fieldSpecifications = new ArrayList(); 165 } 166 167 memberSpecificationElement.appendTo(fieldSpecifications, 168 false, 169 false); 170 } 171 172 173 public void addConfiguredMethod(MemberSpecificationElement memberSpecificationElement) 174 { 175 if (methodSpecifications == null) 176 { 177 methodSpecifications = new ArrayList(); 178 } 179 180 memberSpecificationElement.appendTo(methodSpecifications, 181 true, 182 false); 183 } 184 185 186 public void addConfiguredConstructor(MemberSpecificationElement memberSpecificationElement) 187 { 188 if (methodSpecifications == null) 189 { 190 methodSpecifications = new ArrayList(); 191 } 192 193 memberSpecificationElement.appendTo(methodSpecifications, 194 true, 195 true); 196 } 197 198 199 // Small utility methods. 200 201 private int requiredAccessFlags(boolean set, 202 String access, 203 String type) 204 throws BuildException 205 { 206 int accessFlags = 0; 207 208 if (access != null) 209 { 210 StringTokenizer tokenizer = new StringTokenizer(access, " ,"); 211 while (tokenizer.hasMoreTokens()) 212 { 213 String token = tokenizer.nextToken(); 214 215 if (token.startsWith("!") ^ set) 216 { 217 String strippedToken = token.startsWith("!") ? 218 token.substring(1) : 219 token; 220 221 int accessFlag = 222 strippedToken.equals(ClassConstants.EXTERNAL_ACC_PUBLIC) ? ClassConstants.INTERNAL_ACC_PUBLIC : 223 strippedToken.equals(ClassConstants.EXTERNAL_ACC_FINAL) ? ClassConstants.INTERNAL_ACC_FINAL : 224 strippedToken.equals(ClassConstants.EXTERNAL_ACC_ABSTRACT) ? ClassConstants.INTERNAL_ACC_ABSTRACT : 225 strippedToken.equals(ClassConstants.EXTERNAL_ACC_ANNOTATION) ? ClassConstants.INTERNAL_ACC_ANNOTATTION : 226 0; 227 228 if (accessFlag == 0) 229 { 230 throw new BuildException("Incorrect class access modifier ["+strippedToken+"]"); 231 } 232 233 accessFlags |= accessFlag; 234 } 235 } 236 } 237 238 if (type != null && (type.startsWith("!") ^ set)) 239 { 240 int accessFlag = 241 type.equals("class") ? 0 : 242 type.equals( ClassConstants.EXTERNAL_ACC_INTERFACE) || 243 type.equals("!" + ClassConstants.EXTERNAL_ACC_INTERFACE) ? ClassConstants.INTERNAL_ACC_INTERFACE : 244 type.equals( ClassConstants.EXTERNAL_ACC_ENUM) || 245 type.equals("!" + ClassConstants.EXTERNAL_ACC_ENUM) ? ClassConstants.INTERNAL_ACC_ENUM : 246 -1; 247 if (accessFlag == -1) 248 { 249 throw new BuildException("Incorrect class type ["+type+"]"); 250 } 251 252 accessFlags |= accessFlag; 253 } 254 255 return accessFlags; 256 } 257} 258