/* * Copyright 2005-2010 Brian S O'Neill * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.cojen.classfile.attribute; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import org.cojen.classfile.ConstantInfo; import org.cojen.classfile.ConstantPool; import org.cojen.classfile.TypeDesc; import org.cojen.classfile.constant.ConstantClassInfo; import org.cojen.classfile.constant.ConstantDoubleInfo; import org.cojen.classfile.constant.ConstantFloatInfo; import org.cojen.classfile.constant.ConstantIntegerInfo; import org.cojen.classfile.constant.ConstantLongInfo; import org.cojen.classfile.constant.ConstantUTFInfo; /** * Defines the annotation structure used by Java 5 annotations attributes. * * @author Brian S O'Neill */ public class Annotation { /** Member value is represented by a ConstantIntegerInfo */ public static final char MEMBER_TAG_BOOLEAN = 'Z'; /** Member value is represented by a ConstantIntegerInfo */ public static final char MEMBER_TAG_BYTE = 'B'; /** Member value is represented by a ConstantIntegerInfo */ public static final char MEMBER_TAG_SHORT = 'S'; /** Member value is represented by a ConstantIntegerInfo */ public static final char MEMBER_TAG_CHAR = 'C'; /** Member value is represented by a ConstantIntegerInfo */ public static final char MEMBER_TAG_INT = 'I'; /** Member value is represented by a ConstantLongInfo */ public static final char MEMBER_TAG_LONG = 'J'; /** Member value is represented by a ConstantFloatInfo */ public static final char MEMBER_TAG_FLOAT = 'F'; /** Member value is represented by a ConstantDoubleInfo */ public static final char MEMBER_TAG_DOUBLE = 'D'; /** Member value is represented by a ConstantUTFInfo */ public static final char MEMBER_TAG_STRING = 's'; /** Member value is represented by a ConstantClassInfo */ public static final char MEMBER_TAG_CLASS = 'c'; /** Member value is represented by an EnumConstValue */ public static final char MEMBER_TAG_ENUM = 'e'; /** Member value is represented by a MemberValue array */ public static final char MEMBER_TAG_ARRAY = '['; /** Member value is represented by an Annotation */ public static final char MEMBER_TAG_ANNOTATION = '@'; private final ConstantPool mCp; private ConstantUTFInfo mType; private final Map<String, MemberValue> mMemberValues; public Annotation(ConstantPool cp) { mCp = cp; mMemberValues = new LinkedHashMap<String, MemberValue>(2); } public Annotation(ConstantPool cp, DataInput din) throws IOException { mCp = cp; mType = (ConstantUTFInfo)cp.getConstant(din.readUnsignedShort()); int memberCount = din.readUnsignedShort(); mMemberValues = new LinkedHashMap<String, MemberValue>(memberCount); for (int i=0; i<memberCount; i++) { String name = ((ConstantUTFInfo)cp.getConstant(din.readUnsignedShort())).getValue(); mMemberValues.put(name, new MemberValue(cp, din)); } } public ConstantUTFInfo getTypeConstant() { return mType; } public TypeDesc getType() { return TypeDesc.forDescriptor(mType.getValue()); } public void setTypeConstant(ConstantUTFInfo type) { mType = type; } public void setType(TypeDesc type) { setTypeConstant(mCp.addConstantUTF(type.getDescriptor())); } /** * Returns an unmodifiable map of member names (String) to MemberValue * objects. */ public Map<String, MemberValue> getMemberValues() { return Collections.unmodifiableMap(mMemberValues); } public void putMemberValue(String name, MemberValue mv) { mCp.addConstantUTF(name); mMemberValues.put(name, mv); } public void putMemberValue(String name, boolean value) { mCp.addConstantUTF(name); mMemberValues.put(name, makeMemberValue(value)); } public void putMemberValue(String name, byte value) { mCp.addConstantUTF(name); mMemberValues.put(name, makeMemberValue(value)); } public void putMemberValue(String name, short value) { mCp.addConstantUTF(name); mMemberValues.put(name, makeMemberValue(value)); } public void putMemberValue(String name, char value) { mCp.addConstantUTF(name); mMemberValues.put(name, makeMemberValue(value)); } public void putMemberValue(String name, int value) { mCp.addConstantUTF(name); mMemberValues.put(name, makeMemberValue(value)); } public void putMemberValue(String name, long value) { mCp.addConstantUTF(name); mMemberValues.put(name, makeMemberValue(value)); } public void putMemberValue(String name, float value) { mCp.addConstantUTF(name); mMemberValues.put(name, makeMemberValue(value)); } public void putMemberValue(String name, double value) { mCp.addConstantUTF(name); mMemberValues.put(name, makeMemberValue(value)); } public void putMemberValue(String name, String value) { mCp.addConstantUTF(name); mMemberValues.put(name, makeMemberValue(value)); } public void putMemberValue(String name, TypeDesc value) { mCp.addConstantUTF(name); mMemberValues.put(name, makeMemberValue(value)); } public void putMemberValue(String name, MemberValue[] value) { mCp.addConstantUTF(name); mMemberValues.put(name, makeMemberValue(value)); } public void putMemberValue(String name, TypeDesc enumType, String enumName) { mCp.addConstantUTF(name); mMemberValues.put(name, makeMemberValue(enumType, enumName)); } /** * @see #makeAnnotation */ public void putMemberValue(String name, Annotation value) { mCp.addConstantUTF(name); mMemberValues.put(name, makeMemberValue(value)); } public MemberValue makeMemberValue(boolean value) { return new MemberValue(MEMBER_TAG_BOOLEAN, mCp.addConstantInteger(value ? 1 : 0)); } public MemberValue makeMemberValue(byte value) { return new MemberValue(MEMBER_TAG_BYTE, mCp.addConstantInteger(value)); } public MemberValue makeMemberValue(short value) { return new MemberValue(MEMBER_TAG_SHORT, mCp.addConstantInteger(value)); } public MemberValue makeMemberValue(char value) { return new MemberValue(MEMBER_TAG_CHAR, mCp.addConstantInteger(value)); } public MemberValue makeMemberValue(int value) { return new MemberValue(MEMBER_TAG_INT, mCp.addConstantInteger(value)); } public MemberValue makeMemberValue(long value) { return new MemberValue(MEMBER_TAG_LONG, mCp.addConstantLong(value)); } public MemberValue makeMemberValue(float value) { return new MemberValue(MEMBER_TAG_FLOAT, mCp.addConstantFloat(value)); } public MemberValue makeMemberValue(double value) { return new MemberValue(MEMBER_TAG_DOUBLE, mCp.addConstantDouble(value)); } public MemberValue makeMemberValue(String value) { return new MemberValue(MEMBER_TAG_STRING, mCp.addConstantUTF(value)); } public MemberValue makeMemberValue(TypeDesc value) { return new MemberValue(MEMBER_TAG_CLASS, mCp.addConstantUTF(value.getDescriptor())); } public MemberValue makeMemberValue(TypeDesc enumType, String enumName) { return new MemberValue(MEMBER_TAG_ENUM, new EnumConstValue(mCp.addConstantUTF(enumType.getDescriptor()), mCp.addConstantUTF(enumName))); } public MemberValue makeMemberValue(MemberValue[] value) { return new MemberValue(MEMBER_TAG_ARRAY, value); } /** * @see #makeAnnotation */ public MemberValue makeMemberValue(Annotation value) { return new MemberValue(MEMBER_TAG_ANNOTATION, value); } public Annotation makeAnnotation() { return new Annotation(mCp); } public int getLength() { int length = 4; for (MemberValue mv : mMemberValues.values()) { length += 2 + mv.getLength(); } return length; } public void writeTo(DataOutput dout) throws IOException { dout.writeShort(mType.getIndex()); int memberCount = mMemberValues.size(); dout.writeShort(memberCount); for (Map.Entry<String, MemberValue> entry : mMemberValues.entrySet()) { dout.writeShort(mCp.addConstantUTF(entry.getKey()).getIndex()); entry.getValue().writeTo(dout); } } public static class MemberValue { private final char mTag; private final Object mValue; public MemberValue(char tag, Object value) { switch (mTag = tag) { default: throw new IllegalArgumentException ("Illegal annotation member value tag: " + mTag); case MEMBER_TAG_BOOLEAN: case MEMBER_TAG_BYTE: case MEMBER_TAG_SHORT: case MEMBER_TAG_CHAR: case MEMBER_TAG_INT: if (value instanceof ConstantIntegerInfo) { mValue = value; } else { throw new IllegalArgumentException("Value must be ConstantIntegerInfo"); } break; case MEMBER_TAG_LONG: if (value instanceof ConstantLongInfo) { mValue = value; } else { throw new IllegalArgumentException("Value must be ConstantLongInfo"); } break; case MEMBER_TAG_FLOAT: if (value instanceof ConstantFloatInfo) { mValue = value; } else { throw new IllegalArgumentException("Value must be ConstantFloatInfo"); } break; case MEMBER_TAG_DOUBLE: if (value instanceof ConstantDoubleInfo) { mValue = value; } else { throw new IllegalArgumentException("Value must be ConstantDoubleInfo"); } break; case MEMBER_TAG_CLASS: if (value instanceof ConstantUTFInfo) { mValue = value; } else { throw new IllegalArgumentException("Value must be ConstantUTFInfo"); } break; case MEMBER_TAG_STRING: if (value instanceof ConstantUTFInfo) { mValue = value; } else { throw new IllegalArgumentException("Value must be ConstantUTFInfo"); } break; case MEMBER_TAG_ENUM: if (value instanceof EnumConstValue) { mValue = value; } else { throw new IllegalArgumentException("Value must be EnumConstValue"); } break; case MEMBER_TAG_ARRAY: if (value instanceof MemberValue[]) { mValue = value; } else { throw new IllegalArgumentException("Value must be MemberValue[]"); } break; case MEMBER_TAG_ANNOTATION: if (value instanceof Annotation) { mValue = value; } else { throw new IllegalArgumentException("Value must be Annotation"); } break; } } public MemberValue(ConstantPool cp, DataInput din) throws IOException { switch (mTag = (char)din.readUnsignedByte()) { default: throw new IllegalStateException ("Illegal annotation member value tag: " + mTag); case MEMBER_TAG_BOOLEAN: case MEMBER_TAG_BYTE: case MEMBER_TAG_SHORT: case MEMBER_TAG_CHAR: case MEMBER_TAG_INT: case MEMBER_TAG_LONG: case MEMBER_TAG_FLOAT: case MEMBER_TAG_DOUBLE: case MEMBER_TAG_CLASS: case MEMBER_TAG_STRING: mValue = cp.getConstant(din.readUnsignedShort()); break; case MEMBER_TAG_ENUM: mValue = new EnumConstValue(cp, din); break; case MEMBER_TAG_ARRAY: int length = din.readUnsignedShort(); MemberValue[] values = new MemberValue[length]; for (int i=0; i<length; i++) { values[i] = new MemberValue(cp, din); } mValue = values; break; case MEMBER_TAG_ANNOTATION: mValue = new Annotation(cp, din); break; } } public char getTag() { return mTag; } public Object getValue() { return mValue; } public int getLength() { switch (mTag) { default: return 1; case MEMBER_TAG_BOOLEAN: case MEMBER_TAG_BYTE: case MEMBER_TAG_SHORT: case MEMBER_TAG_CHAR: case MEMBER_TAG_INT: case MEMBER_TAG_LONG: case MEMBER_TAG_FLOAT: case MEMBER_TAG_DOUBLE: case MEMBER_TAG_CLASS: case MEMBER_TAG_STRING: return 3; case MEMBER_TAG_ENUM: return 1 + ((EnumConstValue)mValue).getLength(); case MEMBER_TAG_ARRAY: { MemberValue[] values = (MemberValue[])mValue; int length = 3; for (int i=0; i<values.length; i++) { length += values[i].getLength(); } return length; } case MEMBER_TAG_ANNOTATION: return 1 + ((Annotation)mValue).getLength(); } } public void writeTo(DataOutput dout) throws IOException { dout.writeByte(mTag); switch (mTag) { case MEMBER_TAG_BOOLEAN: case MEMBER_TAG_BYTE: case MEMBER_TAG_SHORT: case MEMBER_TAG_CHAR: case MEMBER_TAG_INT: case MEMBER_TAG_LONG: case MEMBER_TAG_FLOAT: case MEMBER_TAG_DOUBLE: case MEMBER_TAG_CLASS: case MEMBER_TAG_STRING: dout.writeShort(((ConstantInfo)mValue).getIndex()); break; case MEMBER_TAG_ENUM: ((EnumConstValue)mValue).writeTo(dout); break; case MEMBER_TAG_ARRAY: MemberValue[] values = (MemberValue[])mValue; dout.writeShort(values.length); for (int i=0; i<values.length; i++) { values[i].writeTo(dout); } break; case MEMBER_TAG_ANNOTATION: ((Annotation)mValue).writeTo(dout); break; } } } public static class EnumConstValue { private final ConstantUTFInfo mTypeName; private final ConstantUTFInfo mConstName; public EnumConstValue(ConstantUTFInfo typeName, ConstantUTFInfo constName) { mTypeName = typeName; mConstName = constName; } public EnumConstValue(ConstantPool cp, DataInput din) throws IOException { mTypeName = (ConstantUTFInfo)cp.getConstant(din.readUnsignedShort()); mConstName = (ConstantUTFInfo)cp.getConstant(din.readUnsignedShort()); } public ConstantUTFInfo getTypeName() { return mTypeName; } public ConstantUTFInfo getConstName() { return mConstName; } public int getLength() { return 4; } public void writeTo(DataOutput dout) throws IOException { dout.writeShort(mTypeName.getIndex()); dout.writeShort(mConstName.getIndex()); } } }