/** * <copyright> * * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Martin Taal * </copyright> * * $Id: AnnotationParser.java,v 1.3 2008/04/06 13:44:04 mtaal Exp $ */ package org.eclipse.emf.teneo.annotations.parser; import java.util.ArrayList; import java.util.List; import org.eclipse.emf.ecore.ENamedElement; /** * Parses an annotation and creates a tree of parserNodes. * * See AnnotationTokenizer for a short description on the type of parsed tokens. * * @author <a href="mailto:mtaal at elver.org">Martin Taal</a> */ public class AnnotationParser { /** The StringTokenizer being used */ private AnnotationTokenizer annotationTokenizer; /** The list of root nodes */ private List<NamedParserNode> parserNodes = new ArrayList<NamedParserNode>(); private long expectedToken = Long.MAX_VALUE; /** Parses the content and returns a list of parsernodes */ public List<NamedParserNode> parse(ENamedElement eNamedElement, String content) { annotationTokenizer = new AnnotationTokenizer(eNamedElement, content); parserNodes.clear(); int token; while (annotationTokenizer.getCurrentToken() != AnnotationTokenizer.T_EOF && (token = annotationTokenizer.nextToken()) != AnnotationTokenizer.T_EOF) { if (token != AnnotationTokenizer.T_TYPENAME) { throw new AnnotationParserException("Only typenames are allowed at the root of the " + "annotation, see _ for the error " + annotationTokenizer.getErrorText()); } parseTypeName(null); } return parserNodes; } /** Adds a child to the parent */ private void addToParent(NamedParserNode parent, NamedParserNode child) { if (parent == null) { return; } if (parent instanceof ComplexNode) { ((ComplexNode) parent).getChildren().add(child); } else if (parent instanceof ArrayValueNode) { ((ArrayValueNode) parent).getChildren().add(child); } else if (parent instanceof ReferenceValueNode) { ((ReferenceValueNode) parent).setValue(child); } } /** Parse a type name (a complex type) */ private void parseTypeName(NamedParserNode pn) { final ComplexNode cn = new ComplexNode(); cn.setName(annotationTokenizer.getLexeme()); addToParent(pn, cn); if (pn == null) { parserNodes.add(cn); } // now parse the next token final int token = annotationTokenizer.nextToken(); switch (token) { case AnnotationTokenizer.T_EOF: return; case AnnotationTokenizer.T_CONTENTSTART: parseContent(cn); break; case AnnotationTokenizer.T_TYPENAME: parseTypeName(null); // the next one break; case AnnotationTokenizer.T_COMMA: // in case of array if (!(pn instanceof ArrayValueNode)) { throw new AnnotationParserException( "Encountered comma but not within an array definition, see _ for error location " + annotationTokenizer.getErrorText()); } return; default: throw new AnnotationParserException("Unknown token, see _ for error position: " + annotationTokenizer.getErrorText()); } } /** Parse the content of a typeName */ private void parseContent(NamedParserNode pn) { // content can either be an array or a set of values expectedToken = AnnotationTokenizer.T_ARRAYSTART + AnnotationTokenizer.T_IDENTIFIER + AnnotationTokenizer.T_VALUE; boolean finished = false; while (!finished) { final int token = annotationTokenizer.nextToken(); checkToken(token); switch (token) { case AnnotationTokenizer.T_COMMA: expectedToken = AnnotationTokenizer.T_IDENTIFIER; break; case AnnotationTokenizer.T_EOF: throw new AnnotationParserException("Unexcepted end to annotation string, " + annotationTokenizer.getErrorText()); case AnnotationTokenizer.T_CONTENTEND: return; case AnnotationTokenizer.T_VALUE: final String theValue = annotationTokenizer.getLexeme(); final PrimitiveValueNode valueNode = new PrimitiveValueNode(); valueNode.setName("value"); valueNode.setValue(theValue); addToParent(pn, valueNode); if (annotationTokenizer.nextToken() != AnnotationTokenizer.T_CONTENTEND) { throw new AnnotationParserException("After this value a closing ) should follow " + annotationTokenizer.getErrorText()); } return; case AnnotationTokenizer.T_IDENTIFIER: final String identifier = annotationTokenizer.getLexeme(); // next token must be an is int nextToken = annotationTokenizer.nextToken(); // in case of simple annotations with just a value member if (nextToken == AnnotationTokenizer.T_CONTENTEND) { final PrimitiveValueNode vn = new PrimitiveValueNode(); vn.setName("value"); vn.setValue(identifier); addToParent(pn, vn); return; } if (nextToken != AnnotationTokenizer.T_IS) { throw new AnnotationParserException( "No = character after identifier, see _ for error position " + annotationTokenizer.getErrorText()); } nextToken = annotationTokenizer.nextToken(); // if (nextToken == AnnotationTokenizer.T_VALUE) { // final String value = annotationTokenizer.getLexeme(); // final PrimitiveValueNode vn = new PrimitiveValueNode(); // vn.setName(identifier); // vn.setValue(value); // addToParent(pn, vn); // } if (nextToken == AnnotationTokenizer.T_VALUE) { final String value = annotationTokenizer.getLexeme(); final PrimitiveValueNode vn = new PrimitiveValueNode(); vn.setName(identifier); vn.setValue(value); addToParent(pn, vn); } else if (nextToken == AnnotationTokenizer.T_IDENTIFIER) { final String value = annotationTokenizer.getLexeme(); final PrimitiveValueNode vn = new PrimitiveValueNode(); vn.setName(identifier); vn.setValue(value); addToParent(pn, vn); } else if (nextToken == AnnotationTokenizer.T_TYPENAME) { final ReferenceValueNode rvn = new ReferenceValueNode(); rvn.setName(identifier); parseTypeName(rvn); addToParent(pn, rvn); } else if (nextToken == AnnotationTokenizer.T_ARRAYSTART) { final ArrayValueNode avn = new ArrayValueNode(); avn.setName(identifier); parseArray(avn); addToParent(pn, avn); } else if (annotationTokenizer.nextToken() != AnnotationTokenizer.T_VALUE) { throw new AnnotationParserException("No value token after =, see _ for error position " + annotationTokenizer.getErrorText()); } expectedToken = AnnotationTokenizer.T_COMMA + AnnotationTokenizer.T_IDENTIFIER + AnnotationTokenizer.T_CONTENTEND; break; case AnnotationTokenizer.T_ARRAYSTART: // special case in which the main type is just a list of other // types // for example SecondaryTables which is just a list of // SecondaryTable parseArray(pn); ((ComplexNode) pn).setList(true); expectedToken = AnnotationTokenizer.T_COMMA + AnnotationTokenizer.T_IDENTIFIER + AnnotationTokenizer.T_CONTENTEND; break; } } } /** Parse an array */ private void parseArray(NamedParserNode pn) { // content can either be an array or a set of values final ArrayValueNode an = new ArrayValueNode(); addToParent(pn, an); boolean finished = false; expectedToken = AnnotationTokenizer.T_TYPENAME + AnnotationTokenizer.T_VALUE + AnnotationTokenizer.T_IDENTIFIER; while (!finished) { final int token = annotationTokenizer.nextToken(); checkToken(token); switch (token) { case AnnotationTokenizer.T_EOF: throw new AnnotationParserException("Unexcepted end to annotation string, " + annotationTokenizer.getErrorText()); case AnnotationTokenizer.T_TYPENAME: parseTypeName(an); expectedToken = AnnotationTokenizer.T_ARRAYEND + AnnotationTokenizer.T_COMMA; break; case AnnotationTokenizer.T_VALUE: String value = annotationTokenizer.getLexeme(); if (value != null && value.length() > 1 && value.charAt(0) == '"' && value.charAt(value.length() - 1) == '"') { value = value.substring(1, value.length() - 1); } an.getChildren().add(value); expectedToken = AnnotationTokenizer.T_ARRAYEND + AnnotationTokenizer.T_COMMA; break; case AnnotationTokenizer.T_IDENTIFIER: an.getChildren().add(annotationTokenizer.getLexeme()); expectedToken = AnnotationTokenizer.T_IS + AnnotationTokenizer.T_ARRAYEND + AnnotationTokenizer.T_COMMA; break; case AnnotationTokenizer.T_COMMA: expectedToken = AnnotationTokenizer.T_TYPENAME + AnnotationTokenizer.T_VALUE + AnnotationTokenizer.T_IDENTIFIER; break; case AnnotationTokenizer.T_ARRAYEND: expectedToken = Long.MAX_VALUE; return; } } throw new AnnotationParserException("Unexpected end of array. " + annotationTokenizer.getErrorText()); } protected void checkToken(int currentToken) { if ((currentToken & expectedToken) == 0) { final String msg = "Found " + annotationTokenizer.getCurrentTokenName() + " but expected one of : " + annotationTokenizer.getTokenNames(expectedToken); throw new AnnotationParserException(msg + ". " + annotationTokenizer.getErrorText()); } } }