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.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     * Adds the given attribute to the target.
83     */
84    public void addAttribute(Attribute attribute)
85    {
86        // What's the target?
87        if (targetAttribute != null)
88        {
89            // Try to replace an existing attribute.
90            if (!replaceAttributes ||
91                !replaceAttribute(targetAttribute.u2attributesCount,
92                                  targetAttribute.attributes,
93                                  attribute))
94            {
95                // Otherwise append the attribute.
96                targetAttribute.attributes =
97                    addAttribute(targetAttribute.u2attributesCount,
98                                 targetAttribute.attributes,
99                                 attribute);
100
101                targetAttribute.u2attributesCount++;
102            }
103        }
104        else if (targetMember != null)
105        {
106            // Try to replace an existing attribute.
107            if (!replaceAttributes ||
108                !replaceAttribute(targetMember.u2attributesCount,
109                                  targetMember.attributes,
110                                  attribute))
111            {
112                // Otherwise append the attribute.
113                targetMember.attributes =
114                    addAttribute(targetMember.u2attributesCount,
115                                 targetMember.attributes,
116                                 attribute);
117
118                targetMember.u2attributesCount++;
119            }
120        }
121        else
122        {
123            // Try to replace an existing attribute.
124            if (!replaceAttributes ||
125                !replaceAttribute(targetClass.u2attributesCount,
126                                  targetClass.attributes,
127                                  attribute))
128            {
129                // Otherwise append the attribute.
130                targetClass.attributes =
131                    addAttribute(targetClass.u2attributesCount,
132                                 targetClass.attributes,
133                                 attribute);
134
135                targetClass.u2attributesCount++;
136            }
137        }
138    }
139
140
141    /**
142     * Deletes the specified attribute from the target.
143     */
144    public void deleteAttribute(String attributeName)
145    {
146        // What's the target?
147        if (targetAttribute != null)
148        {
149            targetAttribute.u2attributesCount =
150                deleteAttribute(targetAttribute.u2attributesCount,
151                                targetAttribute.attributes,
152                                attributeName);
153        }
154        else if (targetMember != null)
155        {
156            targetMember.u2attributesCount =
157                deleteAttribute(targetMember.u2attributesCount,
158                                targetMember.attributes,
159                                attributeName);
160        }
161        else
162        {
163            targetClass.u2attributesCount =
164                deleteAttribute(targetClass.u2attributesCount,
165                                targetClass.attributes,
166                                attributeName);
167        }
168    }
169
170
171    // Small utility methods.
172
173    /**
174     * Tries put the given attribute in place of an existing attribute of the
175     * same name, returning whether it was present.
176     */
177    private boolean replaceAttribute(int         attributesCount,
178                                     Attribute[] attributes,
179                                     Attribute   attribute)
180    {
181        // Find the attribute with the same name.
182        int index = findAttribute(attributesCount,
183                                  attributes,
184                                  attribute.getAttributeName(targetClass));
185        if (index < 0)
186        {
187            return false;
188        }
189
190        attributes[index] = attribute;
191
192        return true;
193    }
194
195
196    /**
197     * Appends the given attribute to the given array of attributes, creating a
198     * new array if necessary.
199     */
200    private Attribute[] addAttribute(int         attributesCount,
201                                     Attribute[] attributes,
202                                     Attribute   attribute)
203    {
204        // Is the array too small to contain the additional attribute?
205        if (attributes.length <= attributesCount)
206        {
207            // Create a new array and copy the attributes into it.
208            Attribute[] newAttributes = new Attribute[attributesCount + 1];
209            System.arraycopy(attributes, 0,
210                             newAttributes, 0,
211                             attributesCount);
212            attributes = newAttributes;
213        }
214
215        // Append the attribute.
216        attributes[attributesCount] = attribute;
217
218        return attributes;
219    }
220
221
222    /**
223     * Deletes the attributes with the given name from the given array of
224     * attributes, returning the new number of attributes.
225     */
226    private int deleteAttribute(int         attributesCount,
227                                Attribute[] attributes,
228                                String      attributeName)
229    {
230        // Find the attribute.
231        int index = findAttribute(attributesCount,
232                                  attributes,
233                                  attributeName);
234        if (index < 0)
235        {
236            return attributesCount;
237        }
238
239        // Shift the other attributes in the array.
240        System.arraycopy(attributes, index + 1,
241                         attributes, index,
242                         attributesCount - index - 1);
243
244        // Clear the last entry in the array.
245        attributes[--attributesCount] = null;
246
247        return attributesCount;
248    }
249
250
251    /**
252     * Finds the index of the attribute with the given name in the given
253     * array of attributes.
254     */
255    private int findAttribute(int         attributesCount,
256                              Attribute[] attributes,
257                              String      attributeName)
258    {
259        for (int index = 0; index < attributesCount; index++)
260        {
261            if (attributes[index].getAttributeName(targetClass).equals(attributeName))
262            {
263                return index;
264            }
265        }
266
267        return -1;
268    }
269}
270