1/*
2 * ProGuard -- shrinking, optimization, obfuscation, and preverification
3 *             of Java bytecode.
4 *
5 * Copyright (c) 2002-2014 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.classfile.editor;
22
23import proguard.classfile.*;
24import proguard.classfile.attribute.*;
25
26/**
27 * This class can add and delete attributes to and from classes, fields,
28 * methods, and code attributes. Attributes to be added must be filled out
29 * beforehand, including their references to the constant pool. Existing
30 * attributes of the same type are always replaced.
31 *
32 * @author Eric Lafortune
33 */
34public class AttributesEditor
35{
36    private final ProgramClass  targetClass;
37    private final ProgramMember targetMember;
38    private final CodeAttribute targetAttribute;
39    private final boolean       replaceAttributes;
40
41
42    /**
43     * Creates a new AttributeAdder that will edit attributes in the given
44     * target class.
45     */
46    public AttributesEditor(ProgramClass targetClass,
47                            boolean      replaceAttributes)
48    {
49        this(targetClass, null, null, replaceAttributes);
50    }
51
52
53    /**
54     * Creates a new AttributeAdder that will edit attributes in the given
55     * target class member.
56     */
57    public AttributesEditor(ProgramClass  targetClass,
58                            ProgramMember targetMember,
59                            boolean       replaceAttributes)
60    {
61        this(targetClass, targetMember, null, replaceAttributes);
62    }
63
64
65    /**
66     * Creates a new AttributeAdder that will edit attributes in the given
67     * target code attribute.
68     */
69    public AttributesEditor(ProgramClass  targetClass,
70                            ProgramMember targetMember,
71                            CodeAttribute targetAttribute,
72                            boolean       replaceAttributes)
73    {
74        this.targetClass       = targetClass;
75        this.targetMember      = targetMember;
76        this.targetAttribute   = targetAttribute;
77        this.replaceAttributes = replaceAttributes;
78    }
79
80
81    /**
82     * Finds the specified attribute in the target.
83     */
84    public Attribute findAttribute(String attributeName)
85    {
86        // What's the target?
87        return
88            targetAttribute != null ?
89                findAttribute(targetAttribute.u2attributesCount,
90                              targetAttribute.attributes,
91                              attributeName) :
92            targetMember != null ?
93                findAttribute(targetMember.u2attributesCount,
94                              targetMember.attributes,
95                              attributeName) :
96                findAttribute(targetClass.u2attributesCount,
97                              targetClass.attributes,
98                              attributeName);
99    }
100
101
102    /**
103     * Adds the given attribute to the target.
104     */
105    public void addAttribute(Attribute attribute)
106    {
107        // What's the target?
108        if (targetAttribute != null)
109        {
110            // Try to replace an existing attribute.
111            if (!replaceAttributes ||
112                !replaceAttribute(targetAttribute.u2attributesCount,
113                                  targetAttribute.attributes,
114                                  attribute))
115            {
116                // Otherwise append the attribute.
117                targetAttribute.attributes =
118                    addAttribute(targetAttribute.u2attributesCount,
119                                 targetAttribute.attributes,
120                                 attribute);
121
122                targetAttribute.u2attributesCount++;
123            }
124        }
125        else if (targetMember != null)
126        {
127            // Try to replace an existing attribute.
128            if (!replaceAttributes ||
129                !replaceAttribute(targetMember.u2attributesCount,
130                                  targetMember.attributes,
131                                  attribute))
132            {
133                // Otherwise append the attribute.
134                targetMember.attributes =
135                    addAttribute(targetMember.u2attributesCount,
136                                 targetMember.attributes,
137                                 attribute);
138
139                targetMember.u2attributesCount++;
140            }
141        }
142        else
143        {
144            // Try to replace an existing attribute.
145            if (!replaceAttributes ||
146                !replaceAttribute(targetClass.u2attributesCount,
147                                  targetClass.attributes,
148                                  attribute))
149            {
150                // Otherwise append the attribute.
151                targetClass.attributes =
152                    addAttribute(targetClass.u2attributesCount,
153                                 targetClass.attributes,
154                                 attribute);
155
156                targetClass.u2attributesCount++;
157            }
158        }
159    }
160
161
162    /**
163     * Deletes the specified attribute from the target.
164     */
165    public void deleteAttribute(String attributeName)
166    {
167        // What's the target?
168        if (targetAttribute != null)
169        {
170            targetAttribute.u2attributesCount =
171                deleteAttribute(targetAttribute.u2attributesCount,
172                                targetAttribute.attributes,
173                                attributeName);
174        }
175        else if (targetMember != null)
176        {
177            targetMember.u2attributesCount =
178                deleteAttribute(targetMember.u2attributesCount,
179                                targetMember.attributes,
180                                attributeName);
181        }
182        else
183        {
184            targetClass.u2attributesCount =
185                deleteAttribute(targetClass.u2attributesCount,
186                                targetClass.attributes,
187                                attributeName);
188        }
189    }
190
191
192    // Small utility methods.
193
194    /**
195     * Tries to put the given attribute in place of an existing attribute of
196     * the same name, returning whether it was present.
197     */
198    private boolean replaceAttribute(int         attributesCount,
199                                     Attribute[] attributes,
200                                     Attribute   attribute)
201    {
202        // Find the attribute with the same name.
203        int index = findAttributeIndex(attributesCount,
204                                       attributes,
205                                       attribute.getAttributeName(targetClass));
206        if (index < 0)
207        {
208            return false;
209        }
210
211        attributes[index] = attribute;
212
213        return true;
214    }
215
216
217    /**
218     * Appends the given attribute to the given array of attributes, creating a
219     * new array if necessary.
220     */
221    private Attribute[] addAttribute(int         attributesCount,
222                                     Attribute[] attributes,
223                                     Attribute   attribute)
224    {
225        // Is the array too small to contain the additional attribute?
226        if (attributes.length <= attributesCount)
227        {
228            // Create a new array and copy the attributes into it.
229            Attribute[] newAttributes = new Attribute[attributesCount + 1];
230            System.arraycopy(attributes, 0,
231                             newAttributes, 0,
232                             attributesCount);
233            attributes = newAttributes;
234        }
235
236        // Append the attribute.
237        attributes[attributesCount] = attribute;
238
239        return attributes;
240    }
241
242
243    /**
244     * Deletes the attributes with the given name from the given array of
245     * attributes, returning the new number of attributes.
246     */
247    private int deleteAttribute(int         attributesCount,
248                                Attribute[] attributes,
249                                String      attributeName)
250    {
251        // Find the attribute.
252        int index = findAttributeIndex(attributesCount,
253                                       attributes,
254                                       attributeName);
255        if (index < 0)
256        {
257            return attributesCount;
258        }
259
260        // Shift the other attributes in the array.
261        System.arraycopy(attributes, index + 1,
262                         attributes, index,
263                         attributesCount - index - 1);
264
265        // Clear the last entry in the array.
266        attributes[--attributesCount] = null;
267
268        return attributesCount;
269    }
270
271
272    /**
273     * Finds the index of the attribute with the given name in the given
274     * array of attributes.
275     */
276    private int findAttributeIndex(int         attributesCount,
277                                   Attribute[] attributes,
278                                   String      attributeName)
279    {
280        for (int index = 0; index < attributesCount; index++)
281        {
282            Attribute attribute = attributes[index];
283
284            if (attribute.getAttributeName(targetClass).equals(attributeName))
285            {
286                return index;
287            }
288        }
289
290        return -1;
291    }
292
293
294    /**
295     * Finds the attribute with the given name in the given
296     * array of attributes.
297     */
298    private Attribute findAttribute(int         attributesCount,
299                                    Attribute[] attributes,
300                                    String      attributeName)
301    {
302        for (int index = 0; index < attributesCount; index++)
303        {
304            Attribute attribute = attributes[index];
305
306            if (attribute.getAttributeName(targetClass).equals(attributeName))
307            {
308                return attribute;
309            }
310        }
311
312        return null;
313    }
314}
315