/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later, * or the Apache License Version 2.0. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * MODIFIED BY James Nelson of We The Internet, 2013. * Repackaged to avoid conflicts with different versions of Javassist, * and modified Javassist APIs to make them more accessible to outside code. */ package xapi.bytecode; import java.lang.reflect.Modifier; import xapi.bytecode.annotation.AnnotationsAttribute; import xapi.bytecode.attributes.AttributeInfo; public class CtField extends CtMember { static final String javaLangString = "java.lang.String"; protected FieldInfo fieldInfo; /** * Creates a <code>CtField</code> object. * The created field must be added to a class * with <code>CtClass.addField()</code>. * An initial value of the field is specified * by a <code>CtField.Initializer</code> object. * * <p>If getter and setter methods are needed, * call <code>CtNewMethod.getter()</code> and * <code>CtNewMethod.setter()</code>. * * @param type field type * @param name field name * @param declaring the class to which the field will be added. * * @see CtClass#addField(CtField) * @see CtNewMethod#getter(String,CtField) * @see CtNewMethod#setter(String,CtField) * @see CtField.Initializer */ public CtField(CtClass type, String name, CtClass declaring) throws CannotCompileException { this(Descriptor.of(type), name, declaring); } /** * Creates a copy of the given field. * The created field must be added to a class * with <code>CtClass.addField()</code>. * An initial value of the field is specified * by a <code>CtField.Initializer</code> object. * * <p>If getter and setter methods are needed, * call <code>CtNewMethod.getter()</code> and * <code>CtNewMethod.setter()</code>. * * @param src the original field * @param declaring the class to which the field will be added. * @see CtNewMethod#getter(String,CtField) * @see CtNewMethod#setter(String,CtField) * @see CtField.Initializer */ public CtField(CtField src, CtClass declaring) throws CannotCompileException { this(src.fieldInfo.getDescriptor(), src.fieldInfo.getName(), declaring); java.util.ListIterator<?> iterator = src.fieldInfo.getAttributes().listIterator(); FieldInfo fi = fieldInfo; fi.setAccessFlags(src.fieldInfo.getAccessFlags()); ConstPool cp = fi.getConstPool(); while (iterator.hasNext()) { AttributeInfo ainfo = (AttributeInfo)iterator.next(); fi.addAttribute(ainfo.copy(cp, null)); } } private CtField(String typeDesc, String name, CtClass clazz) throws CannotCompileException { super(clazz); ClassFile cf = clazz.getClassFile2(); if (cf == null) { throw new CannotCompileException("bad declaring class: " + clazz.getName()); } fieldInfo = new FieldInfo(cf.getConstPool(), name, typeDesc); } CtField(FieldInfo fi, CtClass clazz) { super(clazz); fieldInfo = fi; } /** * Returns a String representation of the object. */ @Override public String toString() { return getDeclaringClass().getName() + "." + getName() + ":" + fieldInfo.getDescriptor(); } @Override protected void extendToString(StringBuffer buffer) { buffer.append(' '); buffer.append(getName()); buffer.append(' '); buffer.append(fieldInfo.getDescriptor()); } /** * Returns the FieldInfo representing the field in the class file. */ public FieldInfo getFieldInfo() { declaringClass.checkModify(); return fieldInfo; } /** * Returns the FieldInfo representing the field in the class * file (read only). * Normal applications do not need calling this method. Use * <code>getFieldInfo()</code>. * * <p>The <code>FieldInfo</code> object obtained by this method * is read only. Changes to this object might not be reflected * on a class file generated by <code>toBytecode()</code>, * <code>toClass()</code>, etc in <code>CtClass</code>. * * <p>This method is available even if the <code>CtClass</code> * containing this field is frozen. However, if the class is * frozen, the <code>FieldInfo</code> might be also pruned. * * @see #getFieldInfo() * @see CtClass#isFrozen() * @see CtClass#prune() */ public FieldInfo getFieldInfo2() { return fieldInfo; } /** * Returns the class declaring the field. */ @Override public CtClass getDeclaringClass() { // this is redundant but for javadoc. return super.getDeclaringClass(); } /** * Returns the name of the field. */ @Override public String getName() { return fieldInfo.getName(); } /** * Changes the name of the field. */ public void setName(String newName) { declaringClass.checkModify(); fieldInfo.setName(newName); } /** * Returns the encoded modifiers of the field. * * @see Modifier */ @Override public int getModifiers() { return fieldInfo.getAccessFlags(); } /** * Sets the encoded modifiers of the field. * * @see Modifier */ @Override public void setModifiers(int mod) { declaringClass.checkModify(); fieldInfo.setAccessFlags(mod); } /** * Returns true if the class has the specified annotation class. * * @param clz the annotation class. * @return <code>true</code> if the annotation is found, otherwise <code>false</code>. * @since 3.11 */ @Override public boolean hasAnnotation(Class<?> clz) { FieldInfo fi = getFieldInfo2(); AnnotationsAttribute ainfo = (AnnotationsAttribute) fi.getAttribute(AnnotationsAttribute.invisibleTag); AnnotationsAttribute ainfo2 = (AnnotationsAttribute) fi.getAttribute(AnnotationsAttribute.visibleTag); return CtClassType.hasAnnotationType(clz, getDeclaringClass().getClassPool(), ainfo, ainfo2); } /** * Returns the annotation if the class has the specified annotation class. * For example, if an annotation <code>@Author</code> is associated * with this field, an <code>Author</code> object is returned. * The member values can be obtained by calling methods on * the <code>Author</code> object. * * @param clz the annotation class. * @return the annotation if found, otherwise <code>null</code>. * @since 3.11 */ @Override public Object getAnnotation(Class<?> clz) throws ClassNotFoundException { FieldInfo fi = getFieldInfo2(); AnnotationsAttribute ainfo = (AnnotationsAttribute) fi.getAttribute(AnnotationsAttribute.invisibleTag); AnnotationsAttribute ainfo2 = (AnnotationsAttribute) fi.getAttribute(AnnotationsAttribute.visibleTag); return CtClassType.getAnnotationType(clz, getDeclaringClass().getClassPool(), ainfo, ainfo2); } /** * Returns the annotations associated with this field. * * @return an array of annotation-type objects. * @see #getAvailableAnnotations() * @since 3.1 */ @Override public Object[] getAnnotations() throws ClassNotFoundException { return getAnnotations(false); } /** * Returns the annotations associated with this field. * If any annotations are not on the classpath, they are not included * in the returned array. * * @return an array of annotation-type objects. * @see #getAnnotations() * @since 3.3 */ @Override public Object[] getAvailableAnnotations(){ try { return getAnnotations(true); } catch (ClassNotFoundException e) { throw new RuntimeException("Unexpected exception", e); } } private Object[] getAnnotations(boolean ignoreNotFound) throws ClassNotFoundException { FieldInfo fi = getFieldInfo2(); AnnotationsAttribute ainfo = (AnnotationsAttribute) fi.getAttribute(AnnotationsAttribute.invisibleTag); AnnotationsAttribute ainfo2 = (AnnotationsAttribute) fi.getAttribute(AnnotationsAttribute.visibleTag); return CtClassType.toAnnotationType(ignoreNotFound, getDeclaringClass().getClassPool(), ainfo, ainfo2); } /** * Returns the character string representing the type of the field. * If two fields have the same type, * <code>getSignature()</code> returns the same string. * * <p>Note that the returned string is not the type signature * contained in the <code>SignatureAttirbute</code>. It is * a descriptor. To obtain a type signature, call the following * methods: * * <ul><pre>getFieldInfo().getAttribute(SignatureAttribute.tag) * </pre></ul> * * @see javassist.bytecode.Descriptor * @see javassist.bytecode.SignatureAttribute */ @Override public String getSignature() { return fieldInfo.getDescriptor(); } /** * Returns the type of the field. */ public CtClass getType() throws NotFoundException { return Descriptor.toCtClass(fieldInfo.getDescriptor(), declaringClass.getClassPool()); } /** * Sets the type of the field. */ public void setType(CtClass clazz) { declaringClass.checkModify(); fieldInfo.setDescriptor(Descriptor.of(clazz)); } /** * Returns the value of this field if it is a constant field. * This method works only if the field type is a primitive type * or <code>String</code> type. Otherwise, it returns <code>null</code>. * A constant field is <code>static</code> and <code>final</code>. * * @return a <code>Integer</code>, <code>Long</code>, <code>Float</code>, * <code>Double</code>, <code>Boolean</code>, * or <code>String</code> object * representing the constant value. * <code>null</code> if it is not a constant field * or if the field type is not a primitive type * or <code>String</code>. */ public Object getConstantValue() { // When this method is modified, // see also getConstantFieldValue() in TypeChecker. int index = fieldInfo.getConstantValue(); if (index == 0) { return null; } ConstPool cp = fieldInfo.getConstPool(); switch (cp.getTag(index)) { case ConstPool.CONST_Long : return new Long(cp.getLongInfo(index)); case ConstPool.CONST_Float : return new Float(cp.getFloatInfo(index)); case ConstPool.CONST_Double : return new Double(cp.getDoubleInfo(index)); case ConstPool.CONST_Integer : int value = cp.getIntegerInfo(index); // "Z" means boolean type. if ("Z".equals(fieldInfo.getDescriptor())) { return new Boolean(value != 0); } else { return new Integer(value); } case ConstPool.CONST_String : return cp.getStringInfo(index); default : throw new RuntimeException("bad tag: " + cp.getTag(index) + " at " + index); } } /** * Obtains an attribute with the given name. * If that attribute is not found in the class file, this * method returns null. * * <p>Note that an attribute is a data block specified by * the class file format. * See {@link javassist.bytecode.AttributeInfo}. * * @param name attribute name */ @Override public byte[] getAttribute(String name) { AttributeInfo ai = fieldInfo.getAttribute(name); if (ai == null) { return null; } else { return ai.get(); } } /** * Adds an attribute. The attribute is saved in the class file. * * <p>Note that an attribute is a data block specified by * the class file format. * See {@link javassist.bytecode.AttributeInfo}. * * @param name attribute name * @param data attribute value */ @Override public void setAttribute(String name, byte[] data) { declaringClass.checkModify(); fieldInfo.addAttribute(new AttributeInfo(fieldInfo.getConstPool(), name, data)); } }