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.*;
24
25/**
26 * This class can add interfaces and class members to a given class.
27 * Elements to be added must be filled out beforehand, including their
28 * references to the constant pool.
29 *
30 * @author Eric Lafortune
31 */
32public class ClassEditor
33{
34    private static final boolean DEBUG = false;
35
36    private ProgramClass targetClass;
37
38
39    /**
40     * Creates a new ClassEditor that will edit elements in the given
41     * target class.
42     */
43    public ClassEditor(ProgramClass targetClass)
44    {
45        this.targetClass = targetClass;
46    }
47
48
49    /**
50     * Adds the given interface.
51     */
52    public void addInterface(int interfaceConstantIndex)
53    {
54        int   interfacesCount = targetClass.u2interfacesCount;
55        int[] interfaces      = targetClass.u2interfaces;
56
57        // Make sure there is enough space for the new interface.
58        if (interfaces.length <= interfacesCount)
59        {
60            targetClass.u2interfaces = new int[interfacesCount+1];
61            System.arraycopy(interfaces, 0,
62                             targetClass.u2interfaces, 0,
63                             interfacesCount);
64            interfaces = targetClass.u2interfaces;
65        }
66
67        if (DEBUG)
68        {
69            System.out.println(targetClass.getName()+": adding interface ["+targetClass.getClassName(interfaceConstantIndex)+"]");
70        }
71
72        // Add the interface.
73        interfaces[targetClass.u2interfacesCount++] = interfaceConstantIndex;
74    }
75
76    /**
77     * Removes the given interface.
78     */
79    public void removeInterface(int interfaceConstantIndex)
80    {
81        int   interfacesCount = targetClass.u2interfacesCount;
82        int[] interfaces      = targetClass.u2interfaces;
83
84        int interfaceIndex = findInterfaceIndex(interfaceConstantIndex);
85
86        // Shift the interface entries.
87        System.arraycopy(interfaces, interfaceIndex+1,
88                         interfaces, interfaceIndex,
89                         interfacesCount - interfaceIndex - 1);
90
91        // Clear the last entry.
92        interfaces[--targetClass.u2interfacesCount] = 0;
93    }
94
95
96    /**
97     * Finds the index of the given interface in the target class.
98     */
99
100    private int findInterfaceIndex(int interfaceConstantIndex)
101    {
102        int   interfacesCount = targetClass.u2interfacesCount;
103        int[] interfaces      = targetClass.u2interfaces;
104
105        for (int index = 0; index < interfacesCount; index++)
106        {
107            if (interfaces[index] == interfaceConstantIndex)
108            {
109                return index;
110            }
111        }
112
113        return interfacesCount;
114    }
115
116
117    /**
118     * Adds the given field.
119     */
120    public void addField(Field field)
121    {
122        int     fieldsCount = targetClass.u2fieldsCount;
123        Field[] fields      = targetClass.fields;
124
125        // Make sure there is enough space for the new field.
126        if (fields.length <= fieldsCount)
127        {
128            targetClass.fields = new ProgramField[fieldsCount+1];
129            System.arraycopy(fields, 0,
130                             targetClass.fields, 0,
131                             fieldsCount);
132            fields = targetClass.fields;
133        }
134
135        if (DEBUG)
136        {
137            System.out.println(targetClass.getName()+": adding field ["+field.getName(targetClass)+" "+field.getDescriptor(targetClass)+"]");
138        }
139
140        // Add the field.
141        fields[targetClass.u2fieldsCount++] = field;
142    }
143
144
145    /**
146     * Removes the given field. Note that removing a field that is still being
147     * referenced can cause unpredictable effects.
148     */
149    public void removeField(Field field)
150    {
151        int     fieldsCount = targetClass.u2fieldsCount;
152        Field[] fields      = targetClass.fields;
153
154        int fieldIndex = findFieldIndex(field);
155
156        // Shift the field entries.
157        System.arraycopy(fields, fieldIndex+1,
158                         fields, fieldIndex,
159                         fieldsCount - fieldIndex - 1);
160
161        // Clear the last entry.
162        fields[--targetClass.u2fieldsCount] = null;
163    }
164
165
166    /**
167     * Finds the index of the given field in the target class.
168     */
169
170    private int findFieldIndex(Field field)
171    {
172        int     fieldsCount = targetClass.u2fieldsCount;
173        Field[] fields      = targetClass.fields;
174
175        for (int index = 0; index < fieldsCount; index++)
176        {
177            if (fields[index].equals(field))
178            {
179                return index;
180            }
181        }
182
183        return fieldsCount;
184    }
185
186
187    /**
188     * Adds the given method.
189     */
190    public void addMethod(Method method)
191    {
192        int      methodsCount = targetClass.u2methodsCount;
193        Method[] methods      = targetClass.methods;
194
195        // Make sure there is enough space for the new method.
196        if (methods.length <= methodsCount)
197        {
198            targetClass.methods = new ProgramMethod[methodsCount+1];
199            System.arraycopy(methods, 0,
200                             targetClass.methods, 0,
201                             methodsCount);
202            methods = targetClass.methods;
203        }
204
205        if (DEBUG)
206        {
207            System.out.println(targetClass.getName()+": adding method ["+method.getName(targetClass)+method.getDescriptor(targetClass)+"]");
208        }
209
210        // Add the method.
211        methods[targetClass.u2methodsCount++] = method;
212    }
213
214
215    /**
216     * Removes the given method. Note that removing a method that is still being
217     * referenced can cause unpredictable effects.
218     */
219    public void removeMethod(Method method)
220    {
221        int      methodsCount = targetClass.u2methodsCount;
222        Method[] methods      = targetClass.methods;
223
224        int methodIndex = findMethodIndex(method);
225
226        // Shift the method entries.
227        System.arraycopy(methods, methodIndex+1,
228                         methods, methodIndex,
229                         methodsCount - methodIndex - 1);
230
231        // Clear the last entry.
232        methods[--targetClass.u2methodsCount] = null;
233    }
234
235
236    /**
237     * Finds the index of the given method in the target class.
238     */
239
240    private int findMethodIndex(Method method)
241    {
242        int      methodsCount = targetClass.u2methodsCount;
243        Method[] methods      = targetClass.methods;
244
245        for (int index = 0; index < methodsCount; index++)
246        {
247            if (methods[index].equals(method))
248            {
249                return index;
250            }
251        }
252
253        return methodsCount;
254    }
255}
256