/* * Copyright 2007-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. */ /* * Copyright 2006 Amazon Technologies, Inc. or its affiliates. * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks * of Amazon Technologies, Inc. or its affiliates. All rights reserved. * * 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.util; import java.util.ArrayList; import java.util.List; import org.cojen.classfile.TypeDesc; import org.cojen.classfile.attribute.Annotation; // Import the tags. import static org.cojen.util.AnnotationDescPrinter.*; /** * Parses an annotation descriptor String to a Cojen Annotation definition. * * @author Brian S O'Neill * @see AnnotationDescPrinter * @since 2.1 */ public class AnnotationDescParser { private final String mStr; private int mPos; /** * @param annotationString annotation to parse */ public AnnotationDescParser(String annotationString) { mStr = annotationString; } /** * Parses the given annotation, returning the root annotation that received * the results. * * @param rootAnnotation root annotation * @return root annotation * @throws IllegalArgumentExcecption if annotation is malformed */ public Annotation parse(Annotation rootAnnotation) { mPos = 0; if (parseTag() != TAG_ANNOTATION) { throw error("Malformed"); } TypeDesc rootAnnotationType = parseTypeDesc(); if (rootAnnotation == null) { rootAnnotation = buildRootAnnotation(rootAnnotationType); } else if (!rootAnnotationType.equals(rootAnnotation.getType())) { throw new IllegalArgumentException ("Annotation type of \"" + rootAnnotationType + "\" does not match expected type of \"" + rootAnnotation.getType()); } parseAnnotation(rootAnnotation, rootAnnotationType); return rootAnnotation; } /** * Override this method if a root annotation is not provided, as it must be * built after parsing the root annotation type. By default, this method * throws UnsupportedOperationException. */ protected Annotation buildRootAnnotation(TypeDesc rootAnnotationType) { throw new UnsupportedOperationException(); } private void parseAnnotation(Annotation dest, TypeDesc annType) { while (true) { try { if (mStr.charAt(mPos) == ';') { mPos++; break; } } catch (IndexOutOfBoundsException e) { error("Missing terminator"); } if (mPos >= mStr.length()) { break; } String propName = parseName(); char propTag = peekTag(); Annotation.MemberValue mv; if (propTag == TAG_ARRAY) { mPos++; mv = parseArray(dest, peekTag(), parseTypeDesc()); } else { mv = parseProperty(dest, propTag, parseTypeDesc()); } dest.putMemberValue(propName, mv); } } private Annotation.MemberValue parseArray(Annotation dest, char compTag, TypeDesc compType) { List<Annotation.MemberValue> mvList = new ArrayList<Annotation.MemberValue>(); while (true) { try { if (mStr.charAt(mPos) == ';') { mPos++; break; } } catch (IndexOutOfBoundsException e) { error("Missing terminator"); } mvList.add(parseProperty(dest, compTag, compType)); } Annotation.MemberValue[] mvArray = new Annotation.MemberValue[mvList.size()]; return dest.makeMemberValue(mvList.toArray(mvArray)); } private Annotation.MemberValue parseProperty(Annotation dest, char propTag, TypeDesc propType) { Annotation.MemberValue mv; try { if (propTag == TAG_STRING) { StringBuilder b = new StringBuilder(); while (true) { char c = mStr.charAt(mPos++); if (c == ';') { break; } if (c == '\\') { c = mStr.charAt(mPos++); } b.append(c); } mv = dest.makeMemberValue(b.toString()); } else if (propTag == TAG_BOOLEAN) { mv = dest.makeMemberValue(mStr.charAt(mPos++) != '0'); } else if (propTag == TAG_CHAR) { mv = dest.makeMemberValue(mStr.charAt(mPos++)); } else if (propTag == TAG_ANNOTATION) { Annotation propAnn = dest.makeAnnotation(); propAnn.setType(propType); parseAnnotation(propAnn, propType); mv = dest.makeMemberValue(propAnn); } else if (propTag == TAG_CLASS) { mv = dest.makeMemberValue(parseTypeDesc()); } else { int endPos = nextTerminator(); String valueStr = mStr.substring(mPos, endPos); switch (propTag) { default: throw error("Invalid tag"); case TAG_BYTE: { mv = dest.makeMemberValue(Byte.parseByte(valueStr)); break; } case TAG_SHORT: { mv = dest.makeMemberValue(Short.parseShort(valueStr)); break; } case TAG_INT: { mv = dest.makeMemberValue(Integer.parseInt(valueStr)); break; } case TAG_LONG: { mv = dest.makeMemberValue(Long.parseLong(valueStr)); break; } case TAG_FLOAT: { mv = dest.makeMemberValue(Float.parseFloat(valueStr)); break; } case TAG_DOUBLE: { mv = dest.makeMemberValue(Double.parseDouble(valueStr)); break; } case TAG_ENUM: { mv = dest.makeMemberValue(propType, valueStr); break; } } mPos = endPos + 1; } } catch (IndexOutOfBoundsException e) { throw error("Invalid property value"); } return mv; } private TypeDesc parseTypeDesc() { try { int endPos; switch (mStr.charAt(mPos)) { default: throw error("Invalid tag"); case TAG_ARRAY: mPos++; return parseTypeDesc().toArrayType(); case TAG_STRING: mPos++; return TypeDesc.STRING; case TAG_CLASS: mPos++; return TypeDesc.forClass(Class.class); case TAG_ANNOTATION: mPos++; return parseTypeDesc(); case TAG_ENUM: mPos++; endPos = nextTerminator(); int dot = mStr.lastIndexOf('.', endPos); if (dot < mPos) { throw error("Invalid enumeration"); } TypeDesc type = TypeDesc.forClass(mStr.substring(mPos, dot)); mPos = dot + 1; return type; case TAG_BOOLEAN: case TAG_BYTE: case TAG_SHORT: case TAG_CHAR: case TAG_INT: case TAG_LONG: case TAG_FLOAT: case TAG_DOUBLE: case TAG_VOID: endPos = mPos + 1; break; case TAG_OBJECT: endPos = nextTerminator() + 1; break; } TypeDesc type = TypeDesc.forDescriptor(mStr.substring(mPos, endPos)); mPos = endPos; return type; } catch (IndexOutOfBoundsException e) { throw error("Invalid type descriptor"); } } private char peekTag() { char tag = parseTag(); mPos--; return tag; } private char parseTag() { try { return mStr.charAt(mPos++); } catch (IndexOutOfBoundsException e) { throw error("Too short"); } } private String parseName() { int index = mStr.indexOf('=', mPos); if (index < 0) { throw error("Incomplete assignment"); } String name = mStr.substring(mPos, index); mPos = index + 1; return name; } private int nextTerminator() { int index = mStr.indexOf(';', mPos); if (index < 0) { throw error("Missing terminator"); } return index; } private IllegalArgumentException error(String message) { message = "Illegal annotation descriptor: " + message + " at position " + mPos + " of " + mStr; return new IllegalArgumentException(message); } }