blob: f4ea2ff2e942e79108ca72b68e0558d8e281e2e6 [file] [log] [blame]
/*
* ProGuard -- shrinking, optimization, obfuscation, and preverification
* of Java bytecode.
*
* Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package proguard.ant;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.types.DataType;
import proguard.*;
import proguard.classfile.ClassConstants;
import proguard.classfile.util.ClassUtil;
import java.util.*;
/**
* This DataType represents a class specification in Ant.
*
* @author Eric Lafortune
*/
public class ClassSpecificationElement extends DataType
{
private static final String ANY_CLASS_KEYWORD = "*";
private String access;
private String annotation;
private String type;
private String name;
private String extendsAnnotation;
private String extends_;
private List fieldSpecifications = new ArrayList();
private List methodSpecifications = new ArrayList();
/**
* Adds the contents of this class specification element to the given list.
* @param classSpecifications the class specifications to be extended.
*/
public void appendTo(List classSpecifications)
{
// Get the referenced file set, or else this one.
ClassSpecificationElement classSpecificationElement = isReference() ?
(ClassSpecificationElement)getCheckedRef(this.getClass(),
this.getClass().getName()) :
this;
ClassSpecification classSpecification =
createClassSpecification(classSpecificationElement);
// Add it to the list.
classSpecifications.add(classSpecification);
}
/**
* Creates a new class specification corresponding to the contents of this
* class specification element.
*/
protected ClassSpecification createClassSpecification(ClassSpecificationElement classSpecificationElement)
{
String access = classSpecificationElement.access;
String annotation = classSpecificationElement.annotation;
String type = classSpecificationElement.type;
String name = classSpecificationElement.name;
String extendsAnnotation = classSpecificationElement.extendsAnnotation;
String extends_ = classSpecificationElement.extends_;
// For backward compatibility, allow a single "*" wildcard to match
// any class.
if (name != null &&
name.equals(ANY_CLASS_KEYWORD))
{
name = null;
}
ClassSpecification classSpecification =
new ClassSpecification(null,
requiredAccessFlags(true, access, type),
requiredAccessFlags(false, access, type),
annotation != null ? ClassUtil.internalType(annotation) : null,
name != null ? ClassUtil.internalClassName(name) : null,
extendsAnnotation != null ? ClassUtil.internalType(extendsAnnotation) : null,
extends_ != null ? ClassUtil.internalClassName(extends_) : null);
for (int index = 0; index < fieldSpecifications.size(); index++)
{
classSpecification.addField((MemberSpecification)fieldSpecifications.get(index));
}
for (int index = 0; index < methodSpecifications.size(); index++)
{
classSpecification.addMethod((MemberSpecification)methodSpecifications.get(index));
}
return classSpecification;
}
// Ant task attributes.
public void setAccess(String access)
{
this.access = access;
}
public void setAnnotation(String annotation)
{
this.annotation = annotation;
}
public void setType(String type)
{
this.type = type;
}
public void setName(String name)
{
this.name = name;
}
public void setExtendsannotation(String extendsAnnotation)
{
this.extendsAnnotation = extendsAnnotation;
}
public void setExtends(String extends_)
{
this.extends_ = extends_;
}
public void setImplements(String implements_)
{
this.extends_ = implements_;
}
// Ant task nested elements.
public void addConfiguredField(MemberSpecificationElement memberSpecificationElement)
{
if (fieldSpecifications == null)
{
fieldSpecifications = new ArrayList();
}
memberSpecificationElement.appendTo(fieldSpecifications,
false,
false);
}
public void addConfiguredMethod(MemberSpecificationElement memberSpecificationElement)
{
if (methodSpecifications == null)
{
methodSpecifications = new ArrayList();
}
memberSpecificationElement.appendTo(methodSpecifications,
true,
false);
}
public void addConfiguredConstructor(MemberSpecificationElement memberSpecificationElement)
{
if (methodSpecifications == null)
{
methodSpecifications = new ArrayList();
}
memberSpecificationElement.appendTo(methodSpecifications,
true,
true);
}
// Small utility methods.
private int requiredAccessFlags(boolean set,
String access,
String type)
throws BuildException
{
int accessFlags = 0;
if (access != null)
{
StringTokenizer tokenizer = new StringTokenizer(access, " ,");
while (tokenizer.hasMoreTokens())
{
String token = tokenizer.nextToken();
if (token.startsWith("!") ^ set)
{
String strippedToken = token.startsWith("!") ?
token.substring(1) :
token;
int accessFlag =
strippedToken.equals(ClassConstants.EXTERNAL_ACC_PUBLIC) ? ClassConstants.INTERNAL_ACC_PUBLIC :
strippedToken.equals(ClassConstants.EXTERNAL_ACC_FINAL) ? ClassConstants.INTERNAL_ACC_FINAL :
strippedToken.equals(ClassConstants.EXTERNAL_ACC_ABSTRACT) ? ClassConstants.INTERNAL_ACC_ABSTRACT :
strippedToken.equals(ClassConstants.EXTERNAL_ACC_ANNOTATION) ? ClassConstants.INTERNAL_ACC_ANNOTATTION :
0;
if (accessFlag == 0)
{
throw new BuildException("Incorrect class access modifier ["+strippedToken+"]");
}
accessFlags |= accessFlag;
}
}
}
if (type != null && (type.startsWith("!") ^ set))
{
int accessFlag =
type.equals("class") ? 0 :
type.equals( ClassConstants.EXTERNAL_ACC_INTERFACE) ||
type.equals("!" + ClassConstants.EXTERNAL_ACC_INTERFACE) ? ClassConstants.INTERNAL_ACC_INTERFACE :
type.equals( ClassConstants.EXTERNAL_ACC_ENUM) ||
type.equals("!" + ClassConstants.EXTERNAL_ACC_ENUM) ? ClassConstants.INTERNAL_ACC_ENUM :
-1;
if (accessFlag == -1)
{
throw new BuildException("Incorrect class type ["+type+"]");
}
accessFlags |= accessFlag;
}
return accessFlags;
}
}