1/*
2 * reserved comment block
3 * DO NOT REMOVE OR ALTER!
4 */
5/*
6 * Licensed to the Apache Software Foundation (ASF) under one or more
7 * contributor license agreements.  See the NOTICE file distributed with
8 * this work for additional information regarding copyright ownership.
9 * The ASF licenses this file to You under the Apache License, Version 2.0
10 * (the "License"); you may not use this file except in compliance with
11 * the License.  You may obtain a copy of the License at
12 *
13 *      http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
20 */
21package com.sun.org.apache.bcel.internal.generic;
22
23import java.util.ArrayList;
24import java.util.List;
25
26import com.sun.org.apache.bcel.internal.Const;
27import com.sun.org.apache.bcel.internal.classfile.AccessFlags;
28import com.sun.org.apache.bcel.internal.classfile.AnnotationEntry;
29import com.sun.org.apache.bcel.internal.classfile.Annotations;
30import com.sun.org.apache.bcel.internal.classfile.Attribute;
31import com.sun.org.apache.bcel.internal.classfile.ConstantPool;
32import com.sun.org.apache.bcel.internal.classfile.Field;
33import com.sun.org.apache.bcel.internal.classfile.JavaClass;
34import com.sun.org.apache.bcel.internal.classfile.Method;
35import com.sun.org.apache.bcel.internal.classfile.RuntimeInvisibleAnnotations;
36import com.sun.org.apache.bcel.internal.classfile.RuntimeVisibleAnnotations;
37import com.sun.org.apache.bcel.internal.classfile.SourceFile;
38import com.sun.org.apache.bcel.internal.util.BCELComparator;
39
40/**
41 * Template class for building up a java class. May be initialized with an
42 * existing java class (file).
43 *
44 * @see JavaClass
45 * @version $Id: ClassGen.java 1749603 2016-06-21 20:50:19Z ggregory $
46 */
47public class ClassGen extends AccessFlags implements Cloneable {
48
49    /* Corresponds to the fields found in a JavaClass object.
50     */
51    private String class_name;
52    private String super_class_name;
53    private final String file_name;
54    private int class_name_index = -1;
55    private int superclass_name_index = -1;
56    private int major = Const.MAJOR;
57    private int minor = Const.MINOR;
58    private ConstantPoolGen cp; // Template for building up constant pool
59    // ArrayLists instead of arrays to gather fields, methods, etc.
60    private final List<Field> field_vec = new ArrayList<>();
61    private final List<Method> method_vec = new ArrayList<>();
62    private final List<Attribute> attribute_vec = new ArrayList<>();
63    private final List<String> interface_vec = new ArrayList<>();
64    private final List<AnnotationEntryGen> annotation_vec = new ArrayList<>();
65
66    private static BCELComparator _cmp = new BCELComparator() {
67
68        @Override
69        public boolean equals(final Object o1, final Object o2) {
70            final ClassGen THIS = (ClassGen) o1;
71            final ClassGen THAT = (ClassGen) o2;
72            return THIS.getClassName().equals(THAT.getClassName());
73        }
74
75        @Override
76        public int hashCode(final Object o) {
77            final ClassGen THIS = (ClassGen) o;
78            return THIS.getClassName().hashCode();
79        }
80    };
81
82    /**
83     * Convenience constructor to set up some important values initially.
84     *
85     * @param class_name fully qualified class name
86     * @param super_class_name fully qualified superclass name
87     * @param file_name source file name
88     * @param access_flags access qualifiers
89     * @param interfaces implemented interfaces
90     * @param cp constant pool to use
91     */
92    public ClassGen(final String class_name, final String super_class_name, final String file_name, final int access_flags,
93            final String[] interfaces, final ConstantPoolGen cp) {
94        super(access_flags);
95        this.class_name = class_name;
96        this.super_class_name = super_class_name;
97        this.file_name = file_name;
98        this.cp = cp;
99        // Put everything needed by default into the constant pool and the vectors
100        if (file_name != null) {
101            addAttribute(new SourceFile(cp.addUtf8("SourceFile"), 2, cp.addUtf8(file_name), cp
102                    .getConstantPool()));
103        }
104        class_name_index = cp.addClass(class_name);
105        superclass_name_index = cp.addClass(super_class_name);
106        if (interfaces != null) {
107            for (final String interface1 : interfaces) {
108                addInterface(interface1);
109            }
110        }
111    }
112
113    /**
114     * Convenience constructor to set up some important values initially.
115     *
116     * @param class_name fully qualified class name
117     * @param super_class_name fully qualified superclass name
118     * @param file_name source file name
119     * @param access_flags access qualifiers
120     * @param interfaces implemented interfaces
121     */
122    public ClassGen(final String class_name, final String super_class_name, final String file_name, final int access_flags,
123            final String[] interfaces) {
124        this(class_name, super_class_name, file_name, access_flags, interfaces,
125                new ConstantPoolGen());
126    }
127
128    /**
129     * Initialize with existing class.
130     *
131     * @param clazz JavaClass object (e.g. read from file)
132     */
133    public ClassGen(final JavaClass clazz) {
134        super(clazz.getAccessFlags());
135        class_name_index = clazz.getClassNameIndex();
136        superclass_name_index = clazz.getSuperclassNameIndex();
137        class_name = clazz.getClassName();
138        super_class_name = clazz.getSuperclassName();
139        file_name = clazz.getSourceFileName();
140        cp = new ConstantPoolGen(clazz.getConstantPool());
141        major = clazz.getMajor();
142        minor = clazz.getMinor();
143        final Attribute[] attributes = clazz.getAttributes();
144        // J5TODO: Could make unpacking lazy, done on first reference
145        final AnnotationEntryGen[] annotations = unpackAnnotations(attributes);
146        final Method[] methods = clazz.getMethods();
147        final Field[] fields = clazz.getFields();
148        final String[] interfaces = clazz.getInterfaceNames();
149        for (final String interface1 : interfaces) {
150            addInterface(interface1);
151        }
152        for (final Attribute attribute : attributes) {
153            if (!(attribute instanceof Annotations)) {
154                addAttribute(attribute);
155            }
156        }
157        for (final AnnotationEntryGen annotation : annotations) {
158            addAnnotationEntry(annotation);
159        }
160        for (final Method method : methods) {
161            addMethod(method);
162        }
163        for (final Field field : fields) {
164            addField(field);
165        }
166    }
167
168    /**
169     * Look for attributes representing annotations and unpack them.
170     */
171    private AnnotationEntryGen[] unpackAnnotations(final Attribute[] attrs) {
172        final List<AnnotationEntryGen> annotationGenObjs = new ArrayList<>();
173        for (final Attribute attr : attrs) {
174            if (attr instanceof RuntimeVisibleAnnotations) {
175                final RuntimeVisibleAnnotations rva = (RuntimeVisibleAnnotations) attr;
176                final AnnotationEntry[] annos = rva.getAnnotationEntries();
177                for (final AnnotationEntry a : annos) {
178                    annotationGenObjs.add(new AnnotationEntryGen(a,
179                            getConstantPool(), false));
180                }
181            } else if (attr instanceof RuntimeInvisibleAnnotations) {
182                final RuntimeInvisibleAnnotations ria = (RuntimeInvisibleAnnotations) attr;
183                final AnnotationEntry[] annos = ria.getAnnotationEntries();
184                for (final AnnotationEntry a : annos) {
185                    annotationGenObjs.add(new AnnotationEntryGen(a,
186                            getConstantPool(), false));
187                }
188            }
189        }
190        return annotationGenObjs.toArray(new AnnotationEntryGen[annotationGenObjs.size()]);
191    }
192
193    /**
194     * @return the (finally) built up Java class object.
195     */
196    public JavaClass getJavaClass() {
197        final int[] interfaces = getInterfaces();
198        final Field[] fields = getFields();
199        final Method[] methods = getMethods();
200        Attribute[] attributes;
201        if (annotation_vec.isEmpty()) {
202            attributes = getAttributes();
203        } else {
204            // TODO: Sometime later, trash any attributes called 'RuntimeVisibleAnnotations' or 'RuntimeInvisibleAnnotations'
205            final Attribute[] annAttributes = AnnotationEntryGen.getAnnotationAttributes(cp, getAnnotationEntries());
206            attributes = new Attribute[attribute_vec.size() + annAttributes.length];
207            attribute_vec.toArray(attributes);
208            System.arraycopy(annAttributes, 0, attributes, attribute_vec.size(), annAttributes.length);
209        }
210        // Must be last since the above calls may still add something to it
211        final ConstantPool _cp = this.cp.getFinalConstantPool();
212        return new JavaClass(class_name_index, superclass_name_index, file_name, major, minor,
213                super.getAccessFlags(), _cp, interfaces, fields, methods, attributes);
214    }
215
216    /**
217     * Add an interface to this class, i.e., this class has to implement it.
218     *
219     * @param name interface to implement (fully qualified class name)
220     */
221    public final void addInterface(final String name) {
222        interface_vec.add(name);
223    }
224
225    /**
226     * Remove an interface from this class.
227     *
228     * @param name interface to remove (fully qualified name)
229     */
230    public void removeInterface(final String name) {
231        interface_vec.remove(name);
232    }
233
234    /**
235     * @return major version number of class file
236     */
237    public int getMajor() {
238        return major;
239    }
240
241    /**
242     * Set major version number of class file, default value is 45 (JDK 1.1)
243     *
244     * @param major major version number
245     */
246    public void setMajor(final int major) { // TODO could be package-protected - only called by test code
247        this.major = major;
248    }
249
250    /**
251     * Set minor version number of class file, default value is 3 (JDK 1.1)
252     *
253     * @param minor minor version number
254     */
255    public void setMinor(final int minor) {  // TODO could be package-protected - only called by test code
256        this.minor = minor;
257    }
258
259    /**
260     * @return minor version number of class file
261     */
262    public int getMinor() {
263        return minor;
264    }
265
266    /**
267     * Add an attribute to this class.
268     *
269     * @param a attribute to add
270     */
271    public final void addAttribute(final Attribute a) {
272        attribute_vec.add(a);
273    }
274
275    public final void addAnnotationEntry(final AnnotationEntryGen a) {
276        annotation_vec.add(a);
277    }
278
279    /**
280     * Add a method to this class.
281     *
282     * @param m method to add
283     */
284    public final void addMethod(final Method m) {
285        method_vec.add(m);
286    }
287
288    /**
289     * Convenience method.
290     *
291     * Add an empty constructor to this class that does nothing but calling
292     * super().
293     *
294     * @param access_flags rights for constructor
295     */
296    public void addEmptyConstructor(final int access_flags) {
297        final InstructionList il = new InstructionList();
298        il.append(InstructionConst.THIS); // Push `this'
299        il.append(new INVOKESPECIAL(cp.addMethodref(super_class_name, "<init>", "()V")));
300        il.append(InstructionConst.RETURN);
301        final MethodGen mg = new MethodGen(access_flags, Type.VOID, Type.NO_ARGS, null, "<init>",
302                class_name, il, cp);
303        mg.setMaxStack(1);
304        addMethod(mg.getMethod());
305    }
306
307    /**
308     * Add a field to this class.
309     *
310     * @param f field to add
311     */
312    public final void addField(final Field f) {
313        field_vec.add(f);
314    }
315
316    public boolean containsField(final Field f) {
317        return field_vec.contains(f);
318    }
319
320    /**
321     * @return field object with given name, or null
322     */
323    public Field containsField(final String name) {
324        for (final Field f : field_vec) {
325            if (f.getName().equals(name)) {
326                return f;
327            }
328        }
329        return null;
330    }
331
332    /**
333     * @return method object with given name and signature, or null
334     */
335    public Method containsMethod(final String name, final String signature) {
336        for (final Method m : method_vec) {
337            if (m.getName().equals(name) && m.getSignature().equals(signature)) {
338                return m;
339            }
340        }
341        return null;
342    }
343
344    /**
345     * Remove an attribute from this class.
346     *
347     * @param a attribute to remove
348     */
349    public void removeAttribute(final Attribute a) {
350        attribute_vec.remove(a);
351    }
352
353    /**
354     * Remove a method from this class.
355     *
356     * @param m method to remove
357     */
358    public void removeMethod(final Method m) {
359        method_vec.remove(m);
360    }
361
362    /**
363     * Replace given method with new one. If the old one does not exist add the
364     * new_ method to the class anyway.
365     */
366    public void replaceMethod(final Method old, final Method new_) {
367        if (new_ == null) {
368            throw new ClassGenException("Replacement method must not be null");
369        }
370        final int i = method_vec.indexOf(old);
371        if (i < 0) {
372            method_vec.add(new_);
373        } else {
374            method_vec.set(i, new_);
375        }
376    }
377
378    /**
379     * Replace given field with new one. If the old one does not exist add the
380     * new_ field to the class anyway.
381     */
382    public void replaceField(final Field old, final Field new_) {
383        if (new_ == null) {
384            throw new ClassGenException("Replacement method must not be null");
385        }
386        final int i = field_vec.indexOf(old);
387        if (i < 0) {
388            field_vec.add(new_);
389        } else {
390            field_vec.set(i, new_);
391        }
392    }
393
394    /**
395     * Remove a field to this class.
396     *
397     * @param f field to remove
398     */
399    public void removeField(final Field f) {
400        field_vec.remove(f);
401    }
402
403    public String getClassName() {
404        return class_name;
405    }
406
407    public String getSuperclassName() {
408        return super_class_name;
409    }
410
411    public String getFileName() {
412        return file_name;
413    }
414
415    public void setClassName(final String name) {
416        class_name = name.replace('/', '.');
417        class_name_index = cp.addClass(name);
418    }
419
420    public void setSuperclassName(final String name) {
421        super_class_name = name.replace('/', '.');
422        superclass_name_index = cp.addClass(name);
423    }
424
425    public Method[] getMethods() {
426        return method_vec.toArray(new Method[method_vec.size()]);
427    }
428
429    public void setMethods(final Method[] methods) {
430        method_vec.clear();
431        for (final Method method : methods) {
432            addMethod(method);
433        }
434    }
435
436    public void setMethodAt(final Method method, final int pos) {
437        method_vec.set(pos, method);
438    }
439
440    public Method getMethodAt(final int pos) {
441        return method_vec.get(pos);
442    }
443
444    public String[] getInterfaceNames() {
445        final int size = interface_vec.size();
446        final String[] interfaces = new String[size];
447        interface_vec.toArray(interfaces);
448        return interfaces;
449    }
450
451    public int[] getInterfaces() {
452        final int size = interface_vec.size();
453        final int[] interfaces = new int[size];
454        for (int i = 0; i < size; i++) {
455            interfaces[i] = cp.addClass(interface_vec.get(i));
456        }
457        return interfaces;
458    }
459
460    public Field[] getFields() {
461        return field_vec.toArray(new Field[field_vec.size()]);
462    }
463
464    public Attribute[] getAttributes() {
465        return attribute_vec.toArray(new Attribute[attribute_vec.size()]);
466    }
467
468    //  J5TODO: Should we make calling unpackAnnotations() lazy and put it in here?
469    public AnnotationEntryGen[] getAnnotationEntries() {
470        return annotation_vec.toArray(new AnnotationEntryGen[annotation_vec.size()]);
471    }
472
473    public ConstantPoolGen getConstantPool() {
474        return cp;
475    }
476
477    public void setConstantPool(final ConstantPoolGen constant_pool) {
478        cp = constant_pool;
479    }
480
481    public void setClassNameIndex(final int class_name_index) {
482        this.class_name_index = class_name_index;
483        class_name = cp.getConstantPool().getConstantString(class_name_index,
484                Const.CONSTANT_Class).replace('/', '.');
485    }
486
487    public void setSuperclassNameIndex(final int superclass_name_index) {
488        this.superclass_name_index = superclass_name_index;
489        super_class_name = cp.getConstantPool().getConstantString(superclass_name_index,
490                Const.CONSTANT_Class).replace('/', '.');
491    }
492
493    public int getSuperclassNameIndex() {
494        return superclass_name_index;
495    }
496
497    public int getClassNameIndex() {
498        return class_name_index;
499    }
500
501    private List<ClassObserver> observers;
502
503    /**
504     * Add observer for this object.
505     */
506    public void addObserver(final ClassObserver o) {
507        if (observers == null) {
508            observers = new ArrayList<>();
509        }
510        observers.add(o);
511    }
512
513    /**
514     * Remove observer for this object.
515     */
516    public void removeObserver(final ClassObserver o) {
517        if (observers != null) {
518            observers.remove(o);
519        }
520    }
521
522    /**
523     * Call notify() method on all observers. This method is not called
524     * automatically whenever the state has changed, but has to be called by the
525     * user after he has finished editing the object.
526     */
527    public void update() {
528        if (observers != null) {
529            for (final ClassObserver observer : observers) {
530                observer.notify(this);
531            }
532        }
533    }
534
535    @Override
536    public Object clone() {
537        try {
538            return super.clone();
539        } catch (final CloneNotSupportedException e) {
540            throw new Error("Clone Not Supported"); // never happens
541        }
542    }
543
544    /**
545     * @return Comparison strategy object
546     */
547    public static BCELComparator getComparator() {
548        return _cmp;
549    }
550
551    /**
552     * @param comparator Comparison strategy object
553     */
554    public static void setComparator(final BCELComparator comparator) {
555        _cmp = comparator;
556    }
557
558    /**
559     * Return value as defined by given BCELComparator strategy. By default two
560     * ClassGen objects are said to be equal when their class names are equal.
561     *
562     * @see java.lang.Object#equals(java.lang.Object)
563     */
564    @Override
565    public boolean equals(final Object obj) {
566        return _cmp.equals(this, obj);
567    }
568
569    /**
570     * Return value as defined by given BCELComparator strategy. By default
571     * return the hashcode of the class name.
572     *
573     * @see java.lang.Object#hashCode()
574     */
575    @Override
576    public int hashCode() {
577        return _cmp.hashCode(this);
578    }
579}
580