1/*
2 * Javassist, a Java-bytecode translator toolkit.
3 * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
4 *
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License.  Alternatively, the contents of this file may be used under
8 * the terms of the GNU Lesser General Public License Version 2.1 or later.
9 *
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
14 */
15
16package javassist.bytecode;
17
18import java.io.DataInputStream;
19import java.io.DataOutputStream;
20import java.io.IOException;
21import java.util.ArrayList;
22import java.util.List;
23import java.util.ListIterator;
24import java.util.Map;
25import javassist.CannotCompileException;
26
27/**
28 * <code>ClassFile</code> represents a Java <code>.class</code> file, which
29 * consists of a constant pool, methods, fields, and attributes.
30 *
31 * @see javassist.CtClass#getClassFile()
32 */
33public final class ClassFile {
34    int major, minor; // version number
35    ConstPool constPool;
36    int thisClass;
37    int accessFlags;
38    int superClass;
39    int[] interfaces;
40    ArrayList fields;
41    ArrayList methods;
42    ArrayList attributes;
43    String thisclassname; // not JVM-internal name
44    String[] cachedInterfaces;
45    String cachedSuperclass;
46
47    /**
48     * The major version number of class files
49     * for JDK 1.1.
50     */
51    public static final int JAVA_1 = 45;
52
53    /**
54     * The major version number of class files
55     * for JDK 1.2.
56     */
57    public static final int JAVA_2 = 46;
58
59    /**
60     * The major version number of class files
61     * for JDK 1.3.
62     */
63    public static final int JAVA_3 = 47;
64
65    /**
66     * The major version number of class files
67     * for JDK 1.4.
68     */
69    public static final int JAVA_4 = 48;
70
71    /**
72     * The major version number of class files
73     * for JDK 1.5.
74     */
75    public static final int JAVA_5 = 49;
76
77    /**
78     * The major version number of class files
79     * for JDK 1.6.
80     */
81    public static final int JAVA_6 = 50;
82
83    /**
84     * The major version number of class files
85     * for JDK 1.7.
86     */
87    public static final int JAVA_7 = 51;
88
89    /**
90     * The major version number of class files created
91     * from scratch.  The default value is 47 (JDK 1.3)
92     * or 49 (JDK 1.5) if the JVM supports <code>java.lang.StringBuilder</code>.
93     */
94    public static int MAJOR_VERSION = JAVA_3;
95
96    static {
97        try {
98            Class.forName("java.lang.StringBuilder");
99            MAJOR_VERSION = JAVA_5;
100        }
101        catch (Throwable t) {}
102    }
103
104    /**
105     * Constructs a class file from a byte stream.
106     */
107    public ClassFile(DataInputStream in) throws IOException {
108        read(in);
109    }
110
111    /**
112     * Constructs a class file including no members.
113     *
114     * @param isInterface
115     *            true if this is an interface. false if this is a class.
116     * @param classname
117     *            a fully-qualified class name
118     * @param superclass
119     *            a fully-qualified super class name
120     */
121    public ClassFile(boolean isInterface, String classname, String superclass) {
122        major = MAJOR_VERSION;
123        minor = 0; // JDK 1.3 or later
124        constPool = new ConstPool(classname);
125        thisClass = constPool.getThisClassInfo();
126        if (isInterface)
127            accessFlags = AccessFlag.INTERFACE | AccessFlag.ABSTRACT;
128        else
129            accessFlags = AccessFlag.SUPER;
130
131        initSuperclass(superclass);
132        interfaces = null;
133        fields = new ArrayList();
134        methods = new ArrayList();
135        thisclassname = classname;
136
137        attributes = new ArrayList();
138        attributes.add(new SourceFileAttribute(constPool,
139                getSourcefileName(thisclassname)));
140    }
141
142    private void initSuperclass(String superclass) {
143        if (superclass != null) {
144            this.superClass = constPool.addClassInfo(superclass);
145            cachedSuperclass = superclass;
146        }
147        else {
148            this.superClass = constPool.addClassInfo("java.lang.Object");
149            cachedSuperclass = "java.lang.Object";
150        }
151    }
152
153    private static String getSourcefileName(String qname) {
154        int index = qname.lastIndexOf('.');
155        if (index >= 0)
156            qname = qname.substring(index + 1);
157
158        return qname + ".java";
159    }
160
161    /**
162     * Eliminates dead constant pool items. If a method or a field is removed,
163     * the constant pool items used by that method/field become dead items. This
164     * method recreates a constant pool.
165     */
166    public void compact() {
167        ConstPool cp = compact0();
168        ArrayList list = methods;
169        int n = list.size();
170        for (int i = 0; i < n; ++i) {
171            MethodInfo minfo = (MethodInfo)list.get(i);
172            minfo.compact(cp);
173        }
174
175        list = fields;
176        n = list.size();
177        for (int i = 0; i < n; ++i) {
178            FieldInfo finfo = (FieldInfo)list.get(i);
179            finfo.compact(cp);
180        }
181
182        attributes = AttributeInfo.copyAll(attributes, cp);
183        constPool = cp;
184    }
185
186    private ConstPool compact0() {
187        ConstPool cp = new ConstPool(thisclassname);
188        thisClass = cp.getThisClassInfo();
189        String sc = getSuperclass();
190        if (sc != null)
191            superClass = cp.addClassInfo(getSuperclass());
192
193        if (interfaces != null) {
194            int n = interfaces.length;
195            for (int i = 0; i < n; ++i)
196                interfaces[i]
197                    = cp.addClassInfo(constPool.getClassInfo(interfaces[i]));
198        }
199
200        return cp;
201    }
202
203    /**
204     * Discards all attributes, associated with both the class file and the
205     * members such as a code attribute and exceptions attribute. The unused
206     * constant pool entries are also discarded (a new packed constant pool is
207     * constructed).
208     */
209    public void prune() {
210        ConstPool cp = compact0();
211        ArrayList newAttributes = new ArrayList();
212        AttributeInfo invisibleAnnotations
213            = getAttribute(AnnotationsAttribute.invisibleTag);
214        if (invisibleAnnotations != null) {
215            invisibleAnnotations = invisibleAnnotations.copy(cp, null);
216            newAttributes.add(invisibleAnnotations);
217        }
218
219        AttributeInfo visibleAnnotations
220            = getAttribute(AnnotationsAttribute.visibleTag);
221        if (visibleAnnotations != null) {
222            visibleAnnotations = visibleAnnotations.copy(cp, null);
223            newAttributes.add(visibleAnnotations);
224        }
225
226        AttributeInfo signature
227            = getAttribute(SignatureAttribute.tag);
228        if (signature != null) {
229            signature = signature.copy(cp, null);
230            newAttributes.add(signature);
231        }
232
233        ArrayList list = methods;
234        int n = list.size();
235        for (int i = 0; i < n; ++i) {
236            MethodInfo minfo = (MethodInfo)list.get(i);
237            minfo.prune(cp);
238        }
239
240        list = fields;
241        n = list.size();
242        for (int i = 0; i < n; ++i) {
243            FieldInfo finfo = (FieldInfo)list.get(i);
244            finfo.prune(cp);
245        }
246
247        attributes = newAttributes;
248        constPool = cp;
249    }
250
251    /**
252     * Returns a constant pool table.
253     */
254    public ConstPool getConstPool() {
255        return constPool;
256    }
257
258    /**
259     * Returns true if this is an interface.
260     */
261    public boolean isInterface() {
262        return (accessFlags & AccessFlag.INTERFACE) != 0;
263    }
264
265    /**
266     * Returns true if this is a final class or interface.
267     */
268    public boolean isFinal() {
269        return (accessFlags & AccessFlag.FINAL) != 0;
270    }
271
272    /**
273     * Returns true if this is an abstract class or an interface.
274     */
275    public boolean isAbstract() {
276        return (accessFlags & AccessFlag.ABSTRACT) != 0;
277    }
278
279    /**
280     * Returns access flags.
281     *
282     * @see javassist.bytecode.AccessFlag
283     */
284    public int getAccessFlags() {
285        return accessFlags;
286    }
287
288    /**
289     * Changes access flags.
290     *
291     * @see javassist.bytecode.AccessFlag
292     */
293    public void setAccessFlags(int acc) {
294        if ((acc & AccessFlag.INTERFACE) == 0)
295            acc |= AccessFlag.SUPER;
296
297        accessFlags = acc;
298    }
299
300    /**
301     * Returns access and property flags of this nested class.
302     * This method returns -1 if the class is not a nested class.
303     *
304     * <p>The returned value is obtained from <code>inner_class_access_flags</code>
305     * of the entry representing this nested class itself
306     * in <code>InnerClasses_attribute</code>>.
307     */
308    public int getInnerAccessFlags() {
309        InnerClassesAttribute ica
310            = (InnerClassesAttribute)getAttribute(InnerClassesAttribute.tag);
311        if (ica == null)
312            return -1;
313
314        String name = getName();
315        int n = ica.tableLength();
316        for (int i = 0; i < n; ++i)
317            if (name.equals(ica.innerClass(i)))
318                return ica.accessFlags(i);
319
320        return -1;
321    }
322
323    /**
324     * Returns the class name.
325     */
326    public String getName() {
327        return thisclassname;
328    }
329
330    /**
331     * Sets the class name. This method substitutes the new name for all
332     * occurrences of the old class name in the class file.
333     */
334    public void setName(String name) {
335        renameClass(thisclassname, name);
336    }
337
338    /**
339     * Returns the super class name.
340     */
341    public String getSuperclass() {
342        if (cachedSuperclass == null)
343            cachedSuperclass = constPool.getClassInfo(superClass);
344
345        return cachedSuperclass;
346    }
347
348    /**
349     * Returns the index of the constant pool entry representing the super
350     * class.
351     */
352    public int getSuperclassId() {
353        return superClass;
354    }
355
356    /**
357     * Sets the super class.
358     *
359     * <p>
360     * The new super class should inherit from the old super class.
361     * This method modifies constructors so that they call constructors declared
362     * in the new super class.
363     */
364    public void setSuperclass(String superclass) throws CannotCompileException {
365        if (superclass == null)
366            superclass = "java.lang.Object";
367
368        try {
369            this.superClass = constPool.addClassInfo(superclass);
370            ArrayList list = methods;
371            int n = list.size();
372            for (int i = 0; i < n; ++i) {
373                MethodInfo minfo = (MethodInfo)list.get(i);
374                minfo.setSuperclass(superclass);
375            }
376        }
377        catch (BadBytecode e) {
378            throw new CannotCompileException(e);
379        }
380        cachedSuperclass = superclass;
381    }
382
383    /**
384     * Replaces all occurrences of a class name in the class file.
385     *
386     * <p>
387     * If class X is substituted for class Y in the class file, X and Y must
388     * have the same signature. If Y provides a method m(), X must provide it
389     * even if X inherits m() from the super class. If this fact is not
390     * guaranteed, the bytecode verifier may cause an error.
391     *
392     * @param oldname
393     *            the replaced class name
394     * @param newname
395     *            the substituted class name
396     */
397    public final void renameClass(String oldname, String newname) {
398        ArrayList list;
399        int n;
400
401        if (oldname.equals(newname))
402            return;
403
404        if (oldname.equals(thisclassname))
405            thisclassname = newname;
406
407        oldname = Descriptor.toJvmName(oldname);
408        newname = Descriptor.toJvmName(newname);
409        constPool.renameClass(oldname, newname);
410
411        AttributeInfo.renameClass(attributes, oldname, newname);
412        list = methods;
413        n = list.size();
414        for (int i = 0; i < n; ++i) {
415            MethodInfo minfo = (MethodInfo)list.get(i);
416            String desc = minfo.getDescriptor();
417            minfo.setDescriptor(Descriptor.rename(desc, oldname, newname));
418            AttributeInfo.renameClass(minfo.getAttributes(), oldname, newname);
419        }
420
421        list = fields;
422        n = list.size();
423        for (int i = 0; i < n; ++i) {
424            FieldInfo finfo = (FieldInfo)list.get(i);
425            String desc = finfo.getDescriptor();
426            finfo.setDescriptor(Descriptor.rename(desc, oldname, newname));
427            AttributeInfo.renameClass(finfo.getAttributes(), oldname, newname);
428        }
429    }
430
431    /**
432     * Replaces all occurrences of several class names in the class file.
433     *
434     * @param classnames
435     *            specifies which class name is replaced with which new name.
436     *            Class names must be described with the JVM-internal
437     *            representation like <code>java/lang/Object</code>.
438     * @see #renameClass(String,String)
439     */
440    public final void renameClass(Map classnames) {
441        String jvmNewThisName = (String)classnames.get(Descriptor
442                .toJvmName(thisclassname));
443        if (jvmNewThisName != null)
444            thisclassname = Descriptor.toJavaName(jvmNewThisName);
445
446        constPool.renameClass(classnames);
447
448        AttributeInfo.renameClass(attributes, classnames);
449        ArrayList list = methods;
450        int n = list.size();
451        for (int i = 0; i < n; ++i) {
452            MethodInfo minfo = (MethodInfo)list.get(i);
453            String desc = minfo.getDescriptor();
454            minfo.setDescriptor(Descriptor.rename(desc, classnames));
455            AttributeInfo.renameClass(minfo.getAttributes(), classnames);
456        }
457
458        list = fields;
459        n = list.size();
460        for (int i = 0; i < n; ++i) {
461            FieldInfo finfo = (FieldInfo)list.get(i);
462            String desc = finfo.getDescriptor();
463            finfo.setDescriptor(Descriptor.rename(desc, classnames));
464            AttributeInfo.renameClass(finfo.getAttributes(), classnames);
465        }
466    }
467
468    /**
469     * Internal-use only.
470     * <code>CtClass.getRefClasses()</code> calls this method.
471     */
472    public final void getRefClasses(Map classnames) {
473        constPool.renameClass(classnames);
474
475        AttributeInfo.getRefClasses(attributes, classnames);
476        ArrayList list = methods;
477        int n = list.size();
478        for (int i = 0; i < n; ++i) {
479            MethodInfo minfo = (MethodInfo)list.get(i);
480            String desc = minfo.getDescriptor();
481            Descriptor.rename(desc, classnames);
482            AttributeInfo.getRefClasses(minfo.getAttributes(), classnames);
483        }
484
485        list = fields;
486        n = list.size();
487        for (int i = 0; i < n; ++i) {
488            FieldInfo finfo = (FieldInfo)list.get(i);
489            String desc = finfo.getDescriptor();
490            Descriptor.rename(desc, classnames);
491            AttributeInfo.getRefClasses(finfo.getAttributes(), classnames);
492        }
493    }
494
495    /**
496     * Returns the names of the interfaces implemented by the class.
497     * The returned array is read only.
498     */
499    public String[] getInterfaces() {
500        if (cachedInterfaces != null)
501            return cachedInterfaces;
502
503        String[] rtn = null;
504        if (interfaces == null)
505            rtn = new String[0];
506        else {
507            int n = interfaces.length;
508            String[] list = new String[n];
509            for (int i = 0; i < n; ++i)
510                list[i] = constPool.getClassInfo(interfaces[i]);
511
512            rtn = list;
513        }
514
515        cachedInterfaces = rtn;
516        return rtn;
517    }
518
519    /**
520     * Sets the interfaces.
521     *
522     * @param nameList
523     *            the names of the interfaces.
524     */
525    public void setInterfaces(String[] nameList) {
526        cachedInterfaces = null;
527        if (nameList != null) {
528            int n = nameList.length;
529            interfaces = new int[n];
530            for (int i = 0; i < n; ++i)
531                interfaces[i] = constPool.addClassInfo(nameList[i]);
532        }
533    }
534
535    /**
536     * Appends an interface to the interfaces implemented by the class.
537     */
538    public void addInterface(String name) {
539        cachedInterfaces = null;
540        int info = constPool.addClassInfo(name);
541        if (interfaces == null) {
542            interfaces = new int[1];
543            interfaces[0] = info;
544        }
545        else {
546            int n = interfaces.length;
547            int[] newarray = new int[n + 1];
548            System.arraycopy(interfaces, 0, newarray, 0, n);
549            newarray[n] = info;
550            interfaces = newarray;
551        }
552    }
553
554    /**
555     * Returns all the fields declared in the class.
556     *
557     * @return a list of <code>FieldInfo</code>.
558     * @see FieldInfo
559     */
560    public List getFields() {
561        return fields;
562    }
563
564    /**
565     * Appends a field to the class.
566     *
567     * @throws DuplicateMemberException         when the field is already included.
568     */
569    public void addField(FieldInfo finfo) throws DuplicateMemberException {
570        testExistingField(finfo.getName(), finfo.getDescriptor());
571        fields.add(finfo);
572    }
573
574    /**
575     * Just appends a field to the class.
576     * It does not check field duplication.
577     * Use this method only when minimizing performance overheads
578     * is seriously required.
579     *
580     * @since 3.13
581     */
582    public final void addField2(FieldInfo finfo) {
583        fields.add(finfo);
584    }
585
586    private void testExistingField(String name, String descriptor)
587            throws DuplicateMemberException {
588        ListIterator it = fields.listIterator(0);
589        while (it.hasNext()) {
590            FieldInfo minfo = (FieldInfo)it.next();
591            if (minfo.getName().equals(name))
592                throw new DuplicateMemberException("duplicate field: " + name);
593        }
594    }
595
596    /**
597     * Returns all the methods declared in the class.
598     *
599     * @return a list of <code>MethodInfo</code>.
600     * @see MethodInfo
601     */
602    public List getMethods() {
603        return methods;
604    }
605
606    /**
607     * Returns the method with the specified name. If there are multiple methods
608     * with that name, this method returns one of them.
609     *
610     * @return null if no such method is found.
611     */
612    public MethodInfo getMethod(String name) {
613        ArrayList list = methods;
614        int n = list.size();
615        for (int i = 0; i < n; ++i) {
616            MethodInfo minfo = (MethodInfo)list.get(i);
617            if (minfo.getName().equals(name))
618                return minfo;
619        }
620
621        return null;
622    }
623
624    /**
625     * Returns a static initializer (class initializer), or null if it does not
626     * exist.
627     */
628    public MethodInfo getStaticInitializer() {
629        return getMethod(MethodInfo.nameClinit);
630    }
631
632    /**
633     * Appends a method to the class.
634     * If there is a bridge method with the same name and signature,
635     * then the bridge method is removed before a new method is added.
636     *
637     * @throws DuplicateMemberException         when the method is already included.
638     */
639    public void addMethod(MethodInfo minfo) throws DuplicateMemberException {
640        testExistingMethod(minfo);
641        methods.add(minfo);
642    }
643
644    /**
645     * Just appends a method to the class.
646     * It does not check method duplication or remove a bridge method.
647     * Use this method only when minimizing performance overheads
648     * is seriously required.
649     *
650     * @since 3.13
651     */
652    public final void addMethod2(MethodInfo minfo) {
653        methods.add(minfo);
654    }
655
656    private void testExistingMethod(MethodInfo newMinfo)
657        throws DuplicateMemberException
658    {
659        String name = newMinfo.getName();
660        String descriptor = newMinfo.getDescriptor();
661        ListIterator it = methods.listIterator(0);
662        while (it.hasNext())
663            if (isDuplicated(newMinfo, name, descriptor, (MethodInfo)it.next(), it))
664                throw new DuplicateMemberException("duplicate method: " + name
665                                                   + " in " + this.getName());
666    }
667
668    private static boolean isDuplicated(MethodInfo newMethod, String newName,
669                                        String newDesc, MethodInfo minfo,
670                                        ListIterator it)
671    {
672        if (!minfo.getName().equals(newName))
673            return false;
674
675        String desc = minfo.getDescriptor();
676        if (!Descriptor.eqParamTypes(desc, newDesc))
677           return false;
678
679        if (desc.equals(newDesc)) {
680            if (notBridgeMethod(minfo))
681                return true;
682            else {
683                it.remove();
684                return false;
685            }
686        }
687        else
688           return notBridgeMethod(minfo) && notBridgeMethod(newMethod);
689    }
690
691    /* For a bridge method, see Sec. 15.12.4.5 of JLS 3rd Ed.
692     */
693    private static boolean notBridgeMethod(MethodInfo minfo) {
694        return (minfo.getAccessFlags() & AccessFlag.BRIDGE) == 0;
695    }
696
697    /**
698     * Returns all the attributes.  The returned <code>List</code> object
699     * is shared with this object.  If you add a new attribute to the list,
700     * the attribute is also added to the classs file represented by this
701     * object.  If you remove an attribute from the list, it is also removed
702     * from the class file.
703     *
704     * @return a list of <code>AttributeInfo</code> objects.
705     * @see AttributeInfo
706     */
707    public List getAttributes() {
708        return attributes;
709    }
710
711    /**
712     * Returns the attribute with the specified name.  If there are multiple
713     * attributes with that name, this method returns either of them.   It
714     * returns null if the specified attributed is not found.
715     *
716     * @param name          attribute name
717     * @see #getAttributes()
718     */
719    public AttributeInfo getAttribute(String name) {
720        ArrayList list = attributes;
721        int n = list.size();
722        for (int i = 0; i < n; ++i) {
723            AttributeInfo ai = (AttributeInfo)list.get(i);
724            if (ai.getName().equals(name))
725                return ai;
726        }
727
728        return null;
729    }
730
731    /**
732     * Appends an attribute. If there is already an attribute with the same
733     * name, the new one substitutes for it.
734     *
735     * @see #getAttributes()
736     */
737    public void addAttribute(AttributeInfo info) {
738        AttributeInfo.remove(attributes, info.getName());
739        attributes.add(info);
740    }
741
742    /**
743     * Returns the source file containing this class.
744     *
745     * @return null if this information is not available.
746     */
747    public String getSourceFile() {
748        SourceFileAttribute sf
749            = (SourceFileAttribute)getAttribute(SourceFileAttribute.tag);
750        if (sf == null)
751            return null;
752        else
753            return sf.getFileName();
754    }
755
756    private void read(DataInputStream in) throws IOException {
757        int i, n;
758        int magic = in.readInt();
759        if (magic != 0xCAFEBABE)
760            throw new IOException("bad magic number: " + Integer.toHexString(magic));
761
762        minor = in.readUnsignedShort();
763        major = in.readUnsignedShort();
764        constPool = new ConstPool(in);
765        accessFlags = in.readUnsignedShort();
766        thisClass = in.readUnsignedShort();
767        constPool.setThisClassInfo(thisClass);
768        superClass = in.readUnsignedShort();
769        n = in.readUnsignedShort();
770        if (n == 0)
771            interfaces = null;
772        else {
773            interfaces = new int[n];
774            for (i = 0; i < n; ++i)
775                interfaces[i] = in.readUnsignedShort();
776        }
777
778        ConstPool cp = constPool;
779        n = in.readUnsignedShort();
780        fields = new ArrayList();
781        for (i = 0; i < n; ++i)
782            addField2(new FieldInfo(cp, in));
783
784        n = in.readUnsignedShort();
785        methods = new ArrayList();
786        for (i = 0; i < n; ++i)
787            addMethod2(new MethodInfo(cp, in));
788
789        attributes = new ArrayList();
790        n = in.readUnsignedShort();
791        for (i = 0; i < n; ++i)
792            addAttribute(AttributeInfo.read(cp, in));
793
794        thisclassname = constPool.getClassInfo(thisClass);
795    }
796
797    /**
798     * Writes a class file represened by this object into an output stream.
799     */
800    public void write(DataOutputStream out) throws IOException {
801        int i, n;
802
803        out.writeInt(0xCAFEBABE); // magic
804        out.writeShort(minor); // minor version
805        out.writeShort(major); // major version
806        constPool.write(out); // constant pool
807        out.writeShort(accessFlags);
808        out.writeShort(thisClass);
809        out.writeShort(superClass);
810
811        if (interfaces == null)
812            n = 0;
813        else
814            n = interfaces.length;
815
816        out.writeShort(n);
817        for (i = 0; i < n; ++i)
818            out.writeShort(interfaces[i]);
819
820        ArrayList list = fields;
821        n = list.size();
822        out.writeShort(n);
823        for (i = 0; i < n; ++i) {
824            FieldInfo finfo = (FieldInfo)list.get(i);
825            finfo.write(out);
826        }
827
828        list = methods;
829        n = list.size();
830        out.writeShort(n);
831        for (i = 0; i < n; ++i) {
832            MethodInfo minfo = (MethodInfo)list.get(i);
833            minfo.write(out);
834        }
835
836        out.writeShort(attributes.size());
837        AttributeInfo.writeAll(attributes, out);
838    }
839
840    /**
841     * Get the Major version.
842     *
843     * @return the major version
844     */
845    public int getMajorVersion() {
846        return major;
847    }
848
849    /**
850     * Set the major version.
851     *
852     * @param major
853     *            the major version
854     */
855    public void setMajorVersion(int major) {
856        this.major = major;
857    }
858
859    /**
860     * Get the minor version.
861     *
862     * @return the minor version
863     */
864    public int getMinorVersion() {
865        return minor;
866    }
867
868    /**
869     * Set the minor version.
870     *
871     * @param minor
872     *            the minor version
873     */
874    public void setMinorVersion(int minor) {
875        this.minor = minor;
876    }
877
878    /**
879     * Sets the major and minor version to Java 5.
880     *
881     * If the major version is older than 49, Java 5
882     * extensions such as annotations are ignored
883     * by the JVM.
884     */
885    public void setVersionToJava5() {
886        this.major = 49;
887        this.minor = 0;
888    }
889}
890