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.classfile.editor;
22
23import proguard.classfile.*;
24import proguard.classfile.attribute.annotation.*;
25
26/**
27 * This class can add and delete element values to and from a given target
28 * annotation default attribute, annotation, or array element value. Element
29 * values to be added must be filled out beforehand, including their references
30 * to the constant pool.
31 *
32 * @author Eric Lafortune
33 */
34public class ElementValuesEditor
35{
36    private final ProgramClass      targetClass;
37    private final Annotation        targetAnnotation;
38    private final ArrayElementValue targetArrayElementValue;
39    private final boolean           replaceElementValues;
40
41
42    /**
43     * Creates a new ElementValuesEditor that will edit element values in the
44     * given target annotation.
45     */
46    public ElementValuesEditor(ProgramClass targetClass,
47                               Annotation   targetAnnotation,
48                               boolean      replaceElementValues)
49    {
50        this.targetClass             = targetClass;
51        this.targetAnnotation        = targetAnnotation;
52        this.targetArrayElementValue = null;
53        this.replaceElementValues    = replaceElementValues;
54    }
55
56
57    /**
58     * Creates a new ElementValuesEditor that will edit element values in the
59     * given target array element value.
60     */
61    public ElementValuesEditor(ProgramClass      targetClass,
62                               ArrayElementValue targetArrayElementValue,
63                               boolean           replaceElementValues)
64    {
65        this.targetClass             = targetClass;
66        this.targetAnnotation        = null;
67        this.targetArrayElementValue = targetArrayElementValue;
68        this.replaceElementValues    = replaceElementValues;
69    }
70
71
72    /**
73     * Adds the given elementValue to the target.
74     */
75    public void addElementValue(ElementValue elementValue)
76    {
77        // What's the target?
78        if (targetAnnotation != null)
79        {
80            // Try to replace an existing element value.
81            if (!replaceElementValues ||
82                !replaceElementValue(targetAnnotation.u2elementValuesCount,
83                                     targetAnnotation.elementValues,
84                                     elementValue))
85            {
86                // Otherwise append the element value.
87                targetAnnotation.elementValues =
88                    addElementValue(targetAnnotation.u2elementValuesCount,
89                                    targetAnnotation.elementValues,
90                                    elementValue);
91
92                targetAnnotation.u2elementValuesCount++;
93            }
94        }
95        else
96        {
97            // Try to replace an existing element value.
98            if (!replaceElementValues ||
99                !replaceElementValue(targetArrayElementValue.u2elementValuesCount,
100                                     targetArrayElementValue.elementValues,
101                                     elementValue))
102            {
103                // Otherwise append the element value.
104                targetArrayElementValue.elementValues =
105                    addElementValue(targetArrayElementValue.u2elementValuesCount,
106                                    targetArrayElementValue.elementValues,
107                                    elementValue);
108
109                targetArrayElementValue.u2elementValuesCount++;
110            }
111        }
112    }
113
114
115    /**
116     * Deletes the given elementValue to the target.
117     */
118    public void deleteElementValue(String elementValueMethodName)
119    {
120        // What's the target?
121        if (targetAnnotation != null)
122        {
123            // Delete the element value to the target annotation.
124            targetAnnotation.u2elementValuesCount =
125                deleteElementValue(targetAnnotation.u2elementValuesCount,
126                                   targetAnnotation.elementValues,
127                                   elementValueMethodName);
128        }
129        else
130        {
131            // Delete the element value to the target array element value.
132            targetArrayElementValue.u2elementValuesCount =
133                deleteElementValue(targetArrayElementValue.u2elementValuesCount,
134                                   targetArrayElementValue.elementValues,
135                                   elementValueMethodName);
136        }
137    }
138
139
140    // Small utility methods.
141
142    /**
143     * Tries put the given element value in place of an existing element value
144     * of the same name, returning whether it was present.
145     */
146    private boolean replaceElementValue(int            elementValuesCount,
147                                        ElementValue[] elementValues,
148                                        ElementValue   elementValue)
149    {
150        // Find the element value with the same name.
151        int index = findElementValue(elementValuesCount,
152                                     elementValues,
153                                     elementValue.getMethodName(targetClass));
154        if (index < 0)
155        {
156            return false;
157        }
158
159        elementValues[index] = elementValue;
160
161        return true;
162    }
163
164
165    /**
166     * Appends the given element value to the given array of element values,
167     * creating a new array if necessary.
168     */
169    private ElementValue[] addElementValue(int            elementValuesCount,
170                                           ElementValue[] elementValues,
171                                           ElementValue   elementValue)
172    {
173        // Is the array too small to contain the additional elementValue?
174        if (elementValues.length <= elementValuesCount)
175        {
176            // Create a new array and copy the elementValues into it.
177            ElementValue[] newElementValues = new ElementValue[elementValuesCount + 1];
178            System.arraycopy(elementValues, 0,
179                             newElementValues, 0,
180                             elementValuesCount);
181            elementValues = newElementValues;
182        }
183
184        // Append the elementValue.
185        elementValues[elementValuesCount] = elementValue;
186
187        return elementValues;
188    }
189
190
191    /**
192     * Deletes the element values with the given name from the given array of
193     * element values, returning the new number of element values.
194     */
195    private int deleteElementValue(int            elementValuesCount,
196                                   ElementValue[] elementValues,
197                                   String         elementValueMethodName)
198    {
199        // Find the element value.
200        int index = findElementValue(elementValuesCount,
201                                     elementValues,
202                                     elementValueMethodName);
203        if (index < 0)
204        {
205            return elementValuesCount;
206        }
207
208        // Shift the other element values in the array.
209        System.arraycopy(elementValues, index + 1,
210                         elementValues, index,
211                         elementValuesCount - index - 1);
212
213        // Clear the last entry in the array.
214        elementValues[--elementValuesCount] = null;
215
216        return elementValuesCount;
217    }
218
219
220    /**
221     * Finds the index of the element value with the given name in the given
222     * array of element values.
223     */
224    private int findElementValue(int            elementValuesCount,
225                                 ElementValue[] elementValues,
226                                 String         elementValueName)
227    {
228        for (int index = 0; index < elementValuesCount; index++)
229        {
230            if (elementValues[index].getMethodName(targetClass).equals(elementValueName))
231            {
232                return index;
233            }
234        }
235
236        return -1;
237    }
238}