1/*
2 * ProGuard -- shrinking, optimization, obfuscation, and preverification
3 *             of Java bytecode.
4 *
5 * Copyright (c) 2002-2013 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.MemberSpecification;
26import proguard.classfile.ClassConstants;
27import proguard.classfile.util.ClassUtil;
28import proguard.util.ListUtil;
29
30import java.util.*;
31
32/**
33 * This DataType represents a class member specification in Ant.
34 *
35 * @author Eric Lafortune
36 */
37public class MemberSpecificationElement extends DataType
38{
39    private String access;
40    private String annotation;
41    private String type;
42    private String name;
43    private String parameters;
44
45
46    /**
47     * Adds the contents of this class member specification element to the given
48     * list.
49     * @param memberSpecifications the class member specifications to be
50     *                                  extended.
51     * @param isMethod                  specifies whether this specification
52     *                                  refers to a method.
53     * @param isConstructor             specifies whether this specification
54     *                                  refers to a constructor.
55     */
56    public void appendTo(List    memberSpecifications,
57                         boolean isMethod,
58                         boolean isConstructor)
59    {
60        // Get the referenced file set, or else this one.
61        MemberSpecificationElement memberSpecificationElement = isReference() ?
62            (MemberSpecificationElement)getCheckedRef(this.getClass(),
63                                                      this.getClass().getName()) :
64            this;
65
66        // Create a new class member specification.
67        String access     = memberSpecificationElement.access;
68        String type       = memberSpecificationElement.type;
69        String annotation = memberSpecificationElement.annotation;
70        String name       = memberSpecificationElement.name;
71        String parameters = memberSpecificationElement.parameters;
72
73        // Perform some basic conversions and checks on the attributes.
74        if (annotation != null)
75        {
76            annotation = ClassUtil.internalType(annotation);
77        }
78
79        if (isMethod)
80        {
81            if (isConstructor)
82            {
83                if (type != null)
84                {
85                    throw new BuildException("Type attribute not allowed in constructor specification ["+type+"]");
86                }
87
88                if (parameters != null)
89                {
90                    type = ClassConstants.EXTERNAL_TYPE_VOID;
91                }
92
93                name = ClassConstants.INTERNAL_METHOD_NAME_INIT;
94            }
95            else if ((type != null) ^ (parameters != null))
96            {
97                throw new BuildException("Type and parameters attributes must always be present in combination in method specification");
98            }
99        }
100        else
101        {
102            if (parameters != null)
103            {
104                throw new BuildException("Parameters attribute not allowed in field specification ["+parameters+"]");
105            }
106        }
107
108        List parameterList = ListUtil.commaSeparatedList(parameters);
109
110        String descriptor =
111            parameters != null ? ClassUtil.internalMethodDescriptor(type, parameterList) :
112            type       != null ? ClassUtil.internalType(type)                            :
113                                 null;
114
115        MemberSpecification memberSpecification =
116            new MemberSpecification(requiredAccessFlags(true,  access),
117                                    requiredAccessFlags(false, access),
118                                    annotation,
119                                    name,
120                                    descriptor);
121
122        // Add it to the list.
123        memberSpecifications.add(memberSpecification);
124    }
125
126
127    // Ant task attributes.
128
129    public void setAccess(String access)
130    {
131        this.access = access;
132    }
133
134
135    public void setAnnotation(String annotation)
136    {
137        this.annotation = annotation;
138    }
139
140
141    public void setType(String type)
142    {
143        this.type = type;
144    }
145
146
147    public void setName(String name)
148    {
149        this.name = name;
150    }
151
152
153    public void setParameters(String parameters)
154    {
155        this.parameters = parameters;
156    }
157
158
159    /**
160     * @deprecated Use {@link #setParameters(String)} instead.
161     */
162    public void setParam(String parameters)
163    {
164        this.parameters = parameters;
165    }
166
167
168    // Small utility methods.
169
170    private int requiredAccessFlags(boolean set,
171                                    String  access)
172    throws BuildException
173    {
174        int accessFlags = 0;
175
176        if (access != null)
177        {
178            StringTokenizer tokenizer = new StringTokenizer(access, " ,");
179            while (tokenizer.hasMoreTokens())
180            {
181                String token = tokenizer.nextToken();
182
183                if (token.startsWith("!") ^ set)
184                {
185                    String strippedToken = token.startsWith("!") ?
186                        token.substring(1) :
187                        token;
188
189                    int accessFlag =
190                        strippedToken.equals(ClassConstants.EXTERNAL_ACC_PUBLIC)       ? ClassConstants.INTERNAL_ACC_PUBLIC       :
191                        strippedToken.equals(ClassConstants.EXTERNAL_ACC_PRIVATE)      ? ClassConstants.INTERNAL_ACC_PRIVATE      :
192                        strippedToken.equals(ClassConstants.EXTERNAL_ACC_PROTECTED)    ? ClassConstants.INTERNAL_ACC_PROTECTED    :
193                        strippedToken.equals(ClassConstants.EXTERNAL_ACC_STATIC)       ? ClassConstants.INTERNAL_ACC_STATIC       :
194                        strippedToken.equals(ClassConstants.EXTERNAL_ACC_FINAL)        ? ClassConstants.INTERNAL_ACC_FINAL        :
195                        strippedToken.equals(ClassConstants.EXTERNAL_ACC_SYNCHRONIZED) ? ClassConstants.INTERNAL_ACC_SYNCHRONIZED :
196                        strippedToken.equals(ClassConstants.EXTERNAL_ACC_VOLATILE)     ? ClassConstants.INTERNAL_ACC_VOLATILE     :
197                        strippedToken.equals(ClassConstants.EXTERNAL_ACC_TRANSIENT)    ? ClassConstants.INTERNAL_ACC_TRANSIENT    :
198                        strippedToken.equals(ClassConstants.EXTERNAL_ACC_BRIDGE)       ? ClassConstants.INTERNAL_ACC_BRIDGE       :
199                        strippedToken.equals(ClassConstants.EXTERNAL_ACC_VARARGS)      ? ClassConstants.INTERNAL_ACC_VARARGS      :
200                        strippedToken.equals(ClassConstants.EXTERNAL_ACC_NATIVE)       ? ClassConstants.INTERNAL_ACC_NATIVE       :
201                        strippedToken.equals(ClassConstants.EXTERNAL_ACC_ABSTRACT)     ? ClassConstants.INTERNAL_ACC_ABSTRACT     :
202                        strippedToken.equals(ClassConstants.EXTERNAL_ACC_STRICT)       ? ClassConstants.INTERNAL_ACC_STRICT       :
203                        strippedToken.equals(ClassConstants.EXTERNAL_ACC_SYNTHETIC)    ? ClassConstants.INTERNAL_ACC_SYNTHETIC    :
204                        0;
205
206                    if (accessFlag == 0)
207                    {
208                        throw new BuildException("Incorrect class member access modifier ["+strippedToken+"]");
209                    }
210
211                    accessFlags |= accessFlag;
212                }
213            }
214        }
215
216        return accessFlags;
217    }
218}
219