You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@flex.apache.org by ah...@apache.org on 2014/04/25 08:18:43 UTC

[44/46] FlexPMD Donation from Adobe Systems Inc

http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/b0fc5f17/FlexPMD/as3-parser/src/main/java/de/bokelberg/flex/parser/AS3Scanner.java
----------------------------------------------------------------------
diff --git a/FlexPMD/as3-parser/src/main/java/de/bokelberg/flex/parser/AS3Scanner.java b/FlexPMD/as3-parser/src/main/java/de/bokelberg/flex/parser/AS3Scanner.java
new file mode 100644
index 0000000..cdd185f
--- /dev/null
+++ b/FlexPMD/as3-parser/src/main/java/de/bokelberg/flex/parser/AS3Scanner.java
@@ -0,0 +1,898 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 de.bokelberg.flex.parser;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.logging.Logger;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import com.adobe.ac.utils.StackTraceUtils;
+
+/**
+ * convert a actionscript to a stream of tokens
+ * 
+ * @author rbokel
+ * @author xagnetti
+ */
+public class AS3Scanner
+{
+   /**
+    * @author xagnetti
+    */
+   public static final class Token
+   {
+      private static Token create( final String textContent,
+                                   final int tokenLine,
+                                   final int tokenColumn )
+      {
+         return new Token( textContent, tokenLine, tokenColumn );
+      }
+
+      private final int     column;
+      private final boolean isNumeric;
+      private final int     line;
+      private final String  text;
+
+      /**
+       * @param textContent
+       * @param tokenLine
+       * @param tokenColumn
+       */
+      protected Token( final String textContent,
+                       final int tokenLine,
+                       final int tokenColumn )
+      {
+         this( textContent, tokenLine, tokenColumn, false );
+      }
+
+      /**
+       * @param textContent
+       * @param tokenLine
+       * @param tokenColumn
+       * @param isNumToSet
+       */
+      protected Token( final String textContent,
+                       final int tokenLine,
+                       final int tokenColumn,
+                       final boolean isNumToSet )
+      {
+         text = textContent;
+         line = tokenLine + 1;
+         column = tokenColumn + 1;
+         isNumeric = isNumToSet;
+      }
+
+      /**
+       * @return
+       */
+      public int getColumn()
+      {
+         return column;
+      }
+
+      /**
+       * @return
+       */
+      public int getLine()
+      {
+         return line;
+      }
+
+      /**
+       * @return
+       */
+      public String getText()
+      {
+         return text;
+      }
+
+      /**
+       * @return
+       */
+      public boolean isNum()
+      {
+         return isNumeric;
+      }
+   }
+
+   private static class XMLVerifier
+   {
+      private static DefaultHandler handler;
+      private static SAXParser      saxParser;
+
+      static
+      {
+         final SAXParserFactory factory = SAXParserFactory.newInstance();
+
+         handler = new DefaultHandler();
+         factory.setNamespaceAware( false );
+
+         try
+         {
+            saxParser = factory.newSAXParser();
+         }
+         catch ( final ParserConfigurationException e )
+         {
+            LOGGER.warning( StackTraceUtils.print( e ) );
+         }
+         catch ( final SAXException e )
+         {
+         }
+      }
+
+      public static boolean verify( final String text )
+      {
+         try
+         {
+            saxParser.parse( new InputSource( new StringReader( text ) ),
+                             handler );
+            return true;
+         }
+         catch ( final SAXException e )
+         {
+            LOGGER.warning( StackTraceUtils.print( e ) );
+            return false;
+         }
+         catch ( final IOException e )
+         {
+            LOGGER.warning( StackTraceUtils.print( e ) );
+            return false;
+         }
+      }
+   }
+
+   private static final String END    = "__END__";
+   private static final Logger LOGGER = Logger.getLogger( AS3Scanner.class.getName() );
+
+   protected static boolean isDecimalChar( final char currentCharacter )
+   {
+      return currentCharacter >= '0'
+            && currentCharacter <= '9';
+   }
+
+   private int      column;
+   private boolean  inVector;
+   private int      line;
+   private String[] lines = null;
+
+   /**
+    * @return
+    */
+   public Token moveToNextToken()
+   {
+      return nextToken();
+   }
+
+   /**
+    * @param linesToBeSet
+    */
+   public void setLines( final String[] linesToBeSet )
+   {
+      lines = linesToBeSet;
+      line = 0;
+      column = -1;
+   }
+
+   boolean isHexChar( final char currentCharacter )
+   {
+      final boolean isNum = currentCharacter >= '0'
+            && currentCharacter <= '9';
+      final boolean isLower = currentCharacter >= 'A'
+            && currentCharacter <= 'Z';
+      final boolean isUpper = currentCharacter >= 'a'
+            && currentCharacter <= 'z';
+
+      return isNum
+            || isLower || isUpper;
+   }
+
+   /**
+    * @return
+    */
+   protected Token nextToken()
+   {
+      char currentCharacter;
+
+      if ( lines != null
+            && line < lines.length )
+      {
+         currentCharacter = nextNonWhitespaceCharacter();
+      }
+      else
+      {
+         return new Token( END, line, column );
+      }
+
+      if ( currentCharacter == '\n' )
+      {
+         return new Token( "\n", line, column );
+      }
+      if ( currentCharacter == '/' )
+      {
+         return scanCommentRegExpOrOperator();
+      }
+      if ( currentCharacter == '"' )
+      {
+         return scanString( currentCharacter );
+      }
+      if ( currentCharacter == '\'' )
+      {
+         return scanString( currentCharacter );
+      }
+      if ( currentCharacter == '<' )
+      {
+         return scanXMLOrOperator( currentCharacter );
+      }
+      if ( currentCharacter >= '0'
+            && currentCharacter <= '9' || currentCharacter == '.' )
+      {
+         return scanNumberOrDots( currentCharacter );
+      }
+      if ( currentCharacter == '{'
+            || currentCharacter == '}' || currentCharacter == '(' || currentCharacter == ')'
+            || currentCharacter == '[' || currentCharacter == ']' || currentCharacter == ';'
+            || currentCharacter == ',' || currentCharacter == '?' || currentCharacter == '~' )
+      {
+         return scanSingleCharacterToken( currentCharacter );
+      }
+      if ( currentCharacter == ':' )
+      {
+         return scanCharacterSequence( currentCharacter,
+                                       new String[]
+                                       { "::" } );
+      }
+      if ( currentCharacter == '*' )
+      {
+         return scanCharacterSequence( currentCharacter,
+                                       new String[]
+                                       {} );
+      }
+      if ( currentCharacter == '+' )
+      {
+         return scanCharacterSequence( currentCharacter,
+                                       new String[]
+                                       { "++",
+                                                   "+=" } );
+      }
+      if ( currentCharacter == '-' )
+      {
+         return scanCharacterSequence( currentCharacter,
+                                       new String[]
+                                       { "--",
+                                                   "-=" } );
+      }
+      if ( currentCharacter == '%' )
+      {
+         return scanCharacterSequence( currentCharacter,
+                                       new String[]
+                                       { "%=" } );
+      }
+      if ( currentCharacter == '&' )
+      {
+         return scanCharacterSequence( currentCharacter,
+                                       new String[]
+                                       { "&&",
+                                                   "&=" } );
+      }
+      if ( currentCharacter == '|' )
+      {
+         return scanCharacterSequence( currentCharacter,
+                                       new String[]
+                                       { "||",
+                                                   "|=" } );
+      }
+      if ( currentCharacter == '^' )
+      {
+         return scanCharacterSequence( currentCharacter,
+                                       new String[]
+                                       { "^=" } );
+      }
+      if ( currentCharacter == '>' )
+      {
+         if ( inVector )
+         {
+            inVector = false;
+         }
+         else
+         {
+            return scanCharacterSequence( currentCharacter,
+                                          new String[]
+                                          { ">>>=",
+                                                      ">>>",
+                                                      ">>=",
+                                                      ">>",
+                                                      ">=" } );
+         }
+      }
+      if ( currentCharacter == '=' )
+      {
+         return scanCharacterSequence( currentCharacter,
+                                       new String[]
+                                       { "===",
+                                                   "==" } );
+      }
+      if ( currentCharacter == '!' )
+      {
+         return scanCharacterSequence( currentCharacter,
+                                       new String[]
+                                       { "!==",
+                                                   "!=" } );
+      }
+
+      return scanWord( currentCharacter );
+   }
+
+   private int computePossibleMatchesMaxLength( final String[] possibleMatches )
+   {
+      int max = 0;
+
+      for ( final String possibleMatch : possibleMatches )
+      {
+         max = Math.max( max,
+                         possibleMatch.length() );
+      }
+      return max;
+   }
+
+   private char getPreviousCharacter()
+   {
+      int currentIndex = -1;
+      char currentChar;
+      do
+      {
+         currentChar = peekChar( currentIndex-- );
+      }
+      while ( currentChar == ' ' );
+      return currentChar;
+   }
+
+   private boolean isIdentifierCharacter( final char currentCharacter )
+   {
+      return currentCharacter >= 'A'
+            && currentCharacter <= 'Z' || currentCharacter >= 'a' && currentCharacter <= 'z'
+            || currentCharacter >= '0' && currentCharacter <= '9' || currentCharacter == '_'
+            || currentCharacter == '$';
+   }
+
+   private boolean isProcessingInstruction( final String text )
+   {
+      return text.startsWith( "<?" );
+   }
+
+   private boolean isValidRegExp( final String pattern )
+   {
+      try
+      {
+         Pattern.compile( pattern );
+      }
+      catch ( final PatternSyntaxException t )
+      {
+         return false;
+      }
+      return true;
+   }
+
+   private boolean isValidXML( final String text )
+   {
+      return XMLVerifier.verify( text );
+   }
+
+   private char nextChar()
+   {
+      final String currentLine = lines[ line ];
+
+      column++;
+      if ( currentLine.length() <= column )
+      {
+         column = -1;
+         line++;
+         return '\n';
+      }
+
+      char currentChar = currentLine.charAt( column );
+
+      while ( currentChar == '\uFEFF' )
+      {
+         column++;
+         currentChar = currentLine.charAt( column );
+      }
+      return currentChar;
+   }
+
+   private char nextNonWhitespaceCharacter()
+   {
+      char result;
+      do
+      {
+         result = nextChar();
+      }
+      while ( result == ' '
+            || result == '\t' );
+      return result;
+   }
+
+   private char peekChar( final int offset )
+   {
+      final String currentLine = lines[ line ];
+      final int index = column
+            + offset;
+      if ( index == -1 )
+      {
+         return '\0';
+      }
+      if ( index >= currentLine.length() )
+      {
+         return '\n';
+      }
+
+      return currentLine.charAt( index );
+   }
+
+   /**
+    * find the longest matching sequence
+    * 
+    * @param currentCharacter
+    * @param possibleMatches
+    * @param maxLength
+    * @return
+    */
+   private Token scanCharacterSequence( final char currentCharacter,
+                                        final String[] possibleMatches )
+   {
+      int peekPos = 1;
+      final StringBuffer buffer = new StringBuffer();
+      final int maxLength = computePossibleMatchesMaxLength( possibleMatches );
+
+      buffer.append( currentCharacter );
+      String found = buffer.toString();
+      while ( peekPos < maxLength )
+      {
+         buffer.append( peekChar( peekPos ) );
+         peekPos++;
+         for ( final String possibleMatche : possibleMatches )
+         {
+            if ( buffer.toString().equals( possibleMatche ) )
+            {
+               found = buffer.toString();
+            }
+         }
+      }
+      final Token result = new Token( found, line, column );
+      skipChars( found.length() - 1 );
+      return result;
+   }
+
+   /**
+    * Something started with a slash This might be a comment, a regexp or a
+    * operator
+    * 
+    * @param currentCharacter
+    * @return
+    */
+   private Token scanCommentRegExpOrOperator()
+   {
+      final char firstCharacter = peekChar( 1 );
+
+      if ( firstCharacter == '/' )
+      {
+         return scanSingleLineComment();
+      }
+      if ( firstCharacter == '*' )
+      {
+         return scanMultiLineComment();
+      }
+
+      Token result;
+
+      if ( getPreviousCharacter() == '='
+            || getPreviousCharacter() == '(' || getPreviousCharacter() == ',' )
+      {
+         result = scanRegExp();
+
+         if ( result != null )
+         {
+            return result;
+         }
+      }
+
+      if ( firstCharacter == '=' )
+      {
+         result = new Token( "/=", line, column );
+         skipChars( 1 );
+         return result;
+      }
+      result = new Token( "/", line, column );
+      return result;
+   }
+
+   /**
+    * c is either a dot or a number
+    * 
+    * @return
+    */
+   private Token scanDecimal( final char currentCharacter )
+   {
+      char currentChar = currentCharacter;
+      final StringBuffer buffer = new StringBuffer();
+      int peekPos = 1;
+
+      while ( isDecimalChar( currentChar ) )
+      {
+         buffer.append( currentChar );
+         currentChar = peekChar( peekPos++ );
+      }
+
+      if ( currentChar == '.' )
+      {
+         buffer.append( currentChar );
+         currentChar = peekChar( peekPos++ );
+
+         while ( isDecimalChar( currentChar ) )
+         {
+            buffer.append( currentChar );
+            currentChar = peekChar( peekPos++ );
+         }
+
+         if ( currentChar == 'E' )
+         {
+            buffer.append( currentChar );
+            currentChar = peekChar( peekPos++ );
+            while ( isDecimalChar( currentChar ) )
+            {
+               buffer.append( currentChar );
+               currentChar = peekChar( peekPos++ );
+            }
+         }
+      }
+      final Token result = new Token( buffer.toString(), line, column, true );
+      skipChars( result.text.length() - 1 );
+      return result;
+   }
+
+   /**
+    * The first dot has been scanned Are the next chars dots as well?
+    * 
+    * @return
+    */
+   private Token scanDots()
+   {
+      final char secondCharacter = peekChar( 1 );
+
+      if ( secondCharacter == '.' )
+      {
+         final char thirdCharacter = peekChar( 2 );
+         final String text = thirdCharacter == '.' ? "..."
+                                                  : "..";
+         final Token result = new Token( text, line, column );
+
+         skipChars( text.length() - 1 );
+
+         return result;
+      }
+      else if ( secondCharacter == '<' )
+      {
+         final Token result = new Token( ".<", line, column );
+
+         skipChars( 1 );
+
+         inVector = true;
+         return result;
+      }
+      return null;
+   }
+
+   /**
+    * we have seen the 0x prefix
+    * 
+    * @return
+    */
+   private Token scanHex()
+   {
+      final StringBuffer buffer = new StringBuffer();
+
+      buffer.append( "0x" );
+      int peekPos = 2;
+      for ( ;; )
+      {
+         final char character = peekChar( peekPos++ );
+
+         if ( !isHexChar( character ) )
+         {
+            break;
+         }
+         buffer.append( character );
+      }
+      final Token result = new Token( buffer.toString(), line, column, true );
+      skipChars( result.text.length() - 1 );
+      return result;
+   }
+
+   /**
+    * the current char is the first slash plus we know, that a * is following
+    * 
+    * @return
+    */
+   private Token scanMultiLineComment()
+   {
+      final StringBuffer buffer = new StringBuffer();
+      char currentCharacter = ' ';
+      char previousCharacter = ' ';
+
+      buffer.append( "/*" );
+      skipChar();
+      do
+      {
+         previousCharacter = currentCharacter;
+         currentCharacter = nextChar();
+         buffer.append( currentCharacter );
+      }
+      while ( currentCharacter != 0
+            && !( currentCharacter == '/' && previousCharacter == '*' ) );
+
+      return new Token( buffer.toString(), line, column );
+   }
+
+   /**
+    * Something started with a number or a dot.
+    * 
+    * @param characterToBeScanned
+    * @return
+    */
+   private Token scanNumberOrDots( final char characterToBeScanned )
+   {
+      if ( characterToBeScanned == '.' )
+      {
+         final Token result = scanDots();
+         if ( result != null )
+         {
+            return result;
+         }
+
+         final char firstCharacter = peekChar( 1 );
+         if ( !isDecimalChar( firstCharacter ) )
+         {
+            return new Token( ".", line, column );
+         }
+      }
+      if ( characterToBeScanned == '0' )
+      {
+         final char firstCharacter = peekChar( 1 );
+         if ( firstCharacter == 'x' )
+         {
+            return scanHex();
+         }
+      }
+      return scanDecimal( characterToBeScanned );
+   }
+
+   private Token scanRegExp()
+   {
+      final Token token = scanUntilDelimiter( '/' );
+      if ( token != null
+            && isValidRegExp( token.text ) )
+      {
+         return token;
+      }
+      return null;
+   }
+
+   private Token scanSingleCharacterToken( final char character )
+   {
+      return new Token( String.valueOf( character ), line, column );
+   }
+
+   /**
+    * the current char is the first slash plus we know, that another slash is
+    * following
+    * 
+    * @return
+    */
+   private Token scanSingleLineComment()
+   {
+      final Token result = new Token( lines[ line ].substring( column ), line, column );
+      skipChars( result.text.length() - 1 );
+      return result;
+   }
+
+   /**
+    * Something started with a quote or double quote consume characters until
+    * the quote/double quote shows up again and is not escaped
+    * 
+    * @param startingCharacter
+    * @return
+    */
+   private Token scanString( final char startingCharacter )
+   {
+      return scanUntilDelimiter( startingCharacter );
+   }
+
+   private Token scanUntilDelimiter( final char delimiter )
+   {
+      return scanUntilDelimiter( delimiter,
+                                 delimiter );
+
+   }
+
+   private Token scanUntilDelimiter( final char start,
+                                     final char delimiter )
+   {
+      final StringBuffer buffer = new StringBuffer();
+      int peekPos = 1;
+      int numberOfBackslashes = 0;
+
+      buffer.append( start );
+
+      for ( ;; )
+      {
+         final char currentCharacter = peekChar( peekPos++ );
+         if ( currentCharacter == '\n' )
+         {
+            return null;
+         }
+         buffer.append( currentCharacter );
+         if ( currentCharacter == delimiter
+               && numberOfBackslashes == 0 )
+         {
+            final Token result = Token.create( buffer.toString(),
+                                               line,
+                                               column );
+            skipChars( buffer.toString().length() - 1 );
+            return result;
+         }
+         numberOfBackslashes = currentCharacter == '\\' ? ( numberOfBackslashes + 1 ) % 2
+                                                       : 0;
+      }
+   }
+
+   private Token scanWord( final char startingCharacter )
+   {
+      char currentChar = startingCharacter;
+      final StringBuffer buffer = new StringBuffer();
+
+      buffer.append( currentChar );
+      int peekPos = 1;
+      for ( ;; )
+      {
+         currentChar = peekChar( peekPos++ );
+         if ( !isIdentifierCharacter( currentChar ) )
+         {
+            break;
+         }
+
+         buffer.append( currentChar );
+      }
+      final Token result = new Token( buffer.toString(), line, column );
+      skipChars( buffer.toString().length() - 1 );
+      return result;
+   }
+
+   /**
+    * Try to parse a XML document
+    * 
+    * @return
+    */
+   private Token scanXML()
+   {
+      final int currentLine = line;
+      final int currentColumn = column;
+      int level = 0;
+      final StringBuffer buffer = new StringBuffer();
+      char currentCharacter = '<';
+
+      for ( ;; )
+      {
+         Token currentToken = null;
+         do
+         {
+            currentToken = scanUntilDelimiter( '<',
+                                               '>' );
+            if ( currentToken == null )
+            {
+               line = currentLine;
+               column = currentColumn;
+               return null;
+            }
+            buffer.append( currentToken.text );
+            if ( isProcessingInstruction( currentToken.text ) )
+            {
+               currentCharacter = nextChar();
+               if ( currentCharacter == '\n' )
+               {
+                  buffer.append( '\n' );
+                  skipChar();
+               }
+               currentToken = null;
+            }
+         }
+         while ( currentToken == null );
+
+         if ( currentToken.text.startsWith( "</" ) )
+         {
+            level--;
+         }
+         else if ( !currentToken.text.endsWith( "/>" )
+               && !currentToken.text.equals( "<>" ) ) // NOT operator in AS2
+         {
+            level++;
+         }
+
+         if ( level <= 0 )
+         {
+            return new Token( buffer.toString(), line, column );
+         }
+
+         for ( ;; )
+         {
+            currentCharacter = nextChar();
+            if ( currentCharacter == '<' )
+            {
+               break;
+            }
+            buffer.append( currentCharacter );
+         }
+      }
+   }
+
+   /**
+    * Something started with a lower sign <
+    * 
+    * @param startingCharacterc
+    * @return
+    */
+   private Token scanXMLOrOperator( final char startingCharacterc )
+   {
+      final Token xmlToken = scanXML();
+
+      if ( xmlToken != null
+            && isValidXML( xmlToken.text ) )
+      {
+         return xmlToken;
+      }
+      return scanCharacterSequence( startingCharacterc,
+                                    new String[]
+                                    { "<<<=",
+                                                "<<<",
+                                                "<<=",
+                                                "<<",
+                                                "<=" } );
+   }
+
+   private void skipChar()
+   {
+      nextChar();
+   }
+
+   private void skipChars( final int count )
+   {
+      int decrementCount = count;
+
+      while ( decrementCount-- > 0 )
+      {
+         nextChar();
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/b0fc5f17/FlexPMD/as3-parser/src/main/java/de/bokelberg/flex/parser/NestedNode.java
----------------------------------------------------------------------
diff --git a/FlexPMD/as3-parser/src/main/java/de/bokelberg/flex/parser/NestedNode.java b/FlexPMD/as3-parser/src/main/java/de/bokelberg/flex/parser/NestedNode.java
new file mode 100644
index 0000000..27affb2
--- /dev/null
+++ b/FlexPMD/as3-parser/src/main/java/de/bokelberg/flex/parser/NestedNode.java
@@ -0,0 +1,224 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 de.bokelberg.flex.parser;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.adobe.ac.pmd.parser.IParserNode;
+import com.adobe.ac.pmd.parser.NodeKind;
+
+/**
+ * @author xagnetti
+ */
+class NestedNode
+{
+   private List< IParserNode > children;
+   private NodeKind            nodeId;
+
+   /**
+    * @param idToBeSet
+    */
+   protected NestedNode( final NodeKind idToBeSet )
+   {
+      nodeId = idToBeSet;
+   }
+
+   /**
+    * @param idToBeSet
+    * @param childToBeSet
+    */
+   protected NestedNode( final NodeKind idToBeSet,
+                         final IParserNode childToBeSet )
+   {
+      this( idToBeSet );
+      addChild( childToBeSet );
+   }
+
+   /**
+    * @return
+    */
+   public final int computeCyclomaticComplexity()
+   {
+      int cyclomaticComplexity = 0;
+
+      if ( is( NodeKind.FOREACH )
+            || is( NodeKind.FORIN ) || is( NodeKind.CASE ) || is( NodeKind.DEFAULT ) )
+      {
+         cyclomaticComplexity++;
+      }
+      else if ( is( NodeKind.IF )
+            || is( NodeKind.WHILE ) || is( NodeKind.FOR ) )
+      {
+         cyclomaticComplexity++;
+         cyclomaticComplexity += getChild( 0 ).countNodeFromType( NodeKind.AND );
+         cyclomaticComplexity += getChild( 0 ).countNodeFromType( NodeKind.OR );
+      }
+
+      if ( numChildren() > 0 )
+      {
+         for ( final IParserNode child : getChildren() )
+         {
+            cyclomaticComplexity += child.computeCyclomaticComplexity();
+         }
+      }
+
+      return cyclomaticComplexity;
+   }
+
+   /**
+    * @param type
+    * @return
+    */
+   public final int countNodeFromType( final NodeKind type )
+   {
+      int count = 0;
+
+      if ( is( type ) )
+      {
+         count++;
+      }
+      if ( numChildren() > 0 )
+      {
+         for ( final IParserNode child : getChildren() )
+         {
+            count += child.countNodeFromType( type );
+         }
+      }
+      return count;
+   }
+
+   /**
+    * @param index
+    * @return
+    */
+   public final IParserNode getChild( final int index )
+   {
+      return getChildren() == null
+            || getChildren().size() <= index ? null
+                                            : getChildren().get( index );
+   }
+
+   /**
+    * @return
+    */
+   public List< IParserNode > getChildren()
+   {
+      return children;
+   }
+
+   /**
+    * @return
+    */
+   public NodeKind getId()
+   {
+      return nodeId;
+   }
+
+   /**
+    * @return
+    */
+   public IParserNode getLastChild()
+   {
+      final IParserNode lastChild = getChild( numChildren() - 1 );
+
+      return lastChild != null
+            && lastChild.numChildren() > 0 ? lastChild.getLastChild()
+                                          : lastChild;
+   }
+
+   /**
+    * @param expectedType
+    * @return
+    */
+   public final boolean is( final NodeKind expectedType ) // NOPMD
+   {
+      return getId().equals( expectedType );
+   }
+
+   /**
+    * @return
+    */
+   public final int numChildren()
+   {
+      return getChildren() == null ? 0
+                                  : getChildren().size();
+   }
+
+   /**
+    * @param child
+    * @return
+    */
+   final IParserNode addChild( final IParserNode child )
+   {
+      if ( child == null )
+      {
+         return child; // skip optional children
+      }
+
+      if ( children == null )
+      {
+         children = new ArrayList< IParserNode >();
+      }
+      children.add( child );
+      return child;
+   }
+
+   /**
+    * @param childId
+    * @param childLine
+    * @param childColumn
+    * @param nephew
+    * @return
+    */
+   final IParserNode addChild( final NodeKind childId,
+                               final int childLine,
+                               final int childColumn,
+                               final IParserNode nephew )
+   {
+      return addChild( Node.create( childId,
+                                    childLine,
+                                    childColumn,
+                                    nephew ) );
+   }
+
+   /**
+    * @param childId
+    * @param childLine
+    * @param childColumn
+    * @param value
+    * @return
+    */
+   final IParserNode addChild( final NodeKind childId,
+                               final int childLine,
+                               final int childColumn,
+                               final String value )
+   {
+      return addChild( Node.create( childId,
+                                    childLine,
+                                    childColumn,
+                                    value ) );
+   }
+
+   /**
+    * @param idToBeSet
+    */
+   final void setId( final NodeKind idToBeSet )
+   {
+      nodeId = idToBeSet;
+   }
+}

http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/b0fc5f17/FlexPMD/as3-parser/src/main/java/de/bokelberg/flex/parser/Node.java
----------------------------------------------------------------------
diff --git a/FlexPMD/as3-parser/src/main/java/de/bokelberg/flex/parser/Node.java b/FlexPMD/as3-parser/src/main/java/de/bokelberg/flex/parser/Node.java
new file mode 100644
index 0000000..a044881
--- /dev/null
+++ b/FlexPMD/as3-parser/src/main/java/de/bokelberg/flex/parser/Node.java
@@ -0,0 +1,168 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 de.bokelberg.flex.parser;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.adobe.ac.pmd.parser.IParserNode;
+import com.adobe.ac.pmd.parser.NodeKind;
+
+/**
+ * A single node of the ast
+ * 
+ * @author rbokel
+ */
+final class Node extends NestedNode implements IParserNode
+{
+   protected static Node create( final NodeKind idToBeSet,
+                                 final int lineToBeSet,
+                                 final int columnToBeSet )
+   {
+      return new Node( idToBeSet, lineToBeSet, columnToBeSet );
+   }
+
+   protected static Node create( final NodeKind idToBeSet,
+                                 final int lineToBeSet,
+                                 final int columnToBeSet,
+                                 final IParserNode childToBeSet )
+   {
+      return new Node( idToBeSet, lineToBeSet, columnToBeSet, childToBeSet );
+   }
+
+   protected static Node create( final NodeKind idToBeSet,
+                                 final int lineToBeSet,
+                                 final int columnToBeSet,
+                                 final String valueToBeSet )
+   {
+      return new Node( idToBeSet, lineToBeSet, columnToBeSet, valueToBeSet );
+   }
+
+   private static boolean isNameInArray( final String[] strings,
+                                         final String string )
+   {
+      for ( final String currentName : strings )
+      {
+         if ( currentName.equals( string ) )
+         {
+            return true;
+         }
+      }
+      return false;
+   }
+
+   private final int    column;
+   private final int    line;
+   private final String stringValue;
+
+   private Node( final NodeKind idToBeSet,
+                 final int lineToBeSet,
+                 final int columnToBeSet )
+   {
+      super( idToBeSet );
+
+      line = lineToBeSet;
+      column = columnToBeSet;
+      stringValue = null;
+   }
+
+   private Node( final NodeKind idToBeSet,
+                 final int lineToBeSet,
+                 final int columnToBeSet,
+                 final IParserNode childToBeSet )
+   {
+      super( idToBeSet, childToBeSet );
+
+      line = lineToBeSet;
+      column = columnToBeSet;
+      stringValue = null;
+   }
+
+   private Node( final NodeKind idToBeSet,
+                 final int lineToBeSet,
+                 final int columnToBeSet,
+                 final String valueToBeSet )
+   {
+      super( idToBeSet );
+
+      line = lineToBeSet;
+      column = columnToBeSet;
+      stringValue = valueToBeSet;
+   }
+
+   public List< IParserNode > findPrimaryStatementsFromNameInChildren( final String[] names )
+   {
+      final List< IParserNode > foundNode = new ArrayList< IParserNode >();
+
+      if ( getStringValue() != null
+            && isNameInArray( names,
+                              getStringValue() ) )
+      {
+         foundNode.add( this );
+      }
+      else if ( numChildren() != 0 )
+      {
+         for ( final IParserNode child : getChildren() )
+         {
+            foundNode.addAll( child.findPrimaryStatementsFromNameInChildren( names ) );
+         }
+      }
+      return foundNode;
+   }
+
+   public int getColumn()
+   {
+      return column;
+   }
+
+   public int getLine()
+   {
+      return line;
+   }
+
+   public String getStringValue()
+   {
+      return stringValue;
+   }
+
+   @Override
+   public String toString()
+   {
+      final StringBuffer buffer = new StringBuffer();
+
+      if ( getStringValue() == null )
+      {
+         buffer.append( getId() );
+      }
+      else
+      {
+         buffer.append( getStringValue() );
+      }
+
+      buffer.append( ' ' );
+
+      if ( getChildren() != null )
+      {
+         for ( final IParserNode child : getChildren() )
+         {
+            buffer.append( child.toString() );
+            buffer.append( ' ' );
+         }
+      }
+      return buffer.toString();
+   }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/b0fc5f17/FlexPMD/as3-parser/src/test/java/de/bokelberg/flex/parser/AbstractAs3ParserTest.java
----------------------------------------------------------------------
diff --git a/FlexPMD/as3-parser/src/test/java/de/bokelberg/flex/parser/AbstractAs3ParserTest.java b/FlexPMD/as3-parser/src/test/java/de/bokelberg/flex/parser/AbstractAs3ParserTest.java
new file mode 100644
index 0000000..ff9b895
--- /dev/null
+++ b/FlexPMD/as3-parser/src/test/java/de/bokelberg/flex/parser/AbstractAs3ParserTest.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 de.bokelberg.flex.parser;
+
+import junit.framework.TestCase;
+
+import org.junit.Before;
+
+import com.adobe.ac.pmd.parser.IParserNode;
+
+public abstract class AbstractAs3ParserTest extends TestCase
+{
+   protected final class ASTToXMLConverter
+   {
+      public String convert( final IParserNode ast )
+      {
+         final StringBuffer result = new StringBuffer();
+         visitNodes( ast,
+                     result,
+                     0 );
+         return result.toString();
+      }
+   }
+
+   private static String escapeEntities( final String stringToEscape )
+   {
+      final StringBuffer buffer = new StringBuffer();
+
+      for ( int i = 0; i < stringToEscape.length(); i++ )
+      {
+         final char currentCharacter = stringToEscape.charAt( i );
+
+         if ( currentCharacter == '<' )
+         {
+            buffer.append( "&lt;" );
+         }
+         else if ( currentCharacter == '>' )
+         {
+            buffer.append( "&gt;" );
+         }
+         else
+         {
+            buffer.append( currentCharacter );
+         }
+      }
+      return buffer.toString();
+   }
+
+   private static void visitNodes( final IParserNode ast,
+                                   final StringBuffer result,
+                                   final int level )
+   {
+      result.append( "<"
+            + ast.getId() + " line=\"" + ast.getLine() + "\">" );
+
+      final int numChildren = ast.numChildren();
+      if ( numChildren > 0 )
+      {
+         for ( int i = 0; i < numChildren; i++ )
+         {
+            visitNodes( ast.getChild( i ),
+                        result,
+                        level + 1 );
+         }
+      }
+      else if ( ast.getStringValue() != null )
+      {
+         result.append( escapeEntities( ast.getStringValue() ) );
+      }
+      result.append( "</"
+            + ast.getId() + ">" );
+   }
+
+   protected AS3Parser  asp;
+   protected AS3Scanner scn;
+
+   @Override
+   @Before
+   public void setUp()
+   {
+      asp = new AS3Parser();
+      scn = asp.getScn();
+   }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/b0fc5f17/FlexPMD/as3-parser/src/test/java/de/bokelberg/flex/parser/AbstractStatementTest.java
----------------------------------------------------------------------
diff --git a/FlexPMD/as3-parser/src/test/java/de/bokelberg/flex/parser/AbstractStatementTest.java b/FlexPMD/as3-parser/src/test/java/de/bokelberg/flex/parser/AbstractStatementTest.java
new file mode 100644
index 0000000..b437833
--- /dev/null
+++ b/FlexPMD/as3-parser/src/test/java/de/bokelberg/flex/parser/AbstractStatementTest.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 de.bokelberg.flex.parser;
+
+import com.adobe.ac.pmd.parser.exceptions.TokenException;
+
+public abstract class AbstractStatementTest extends AbstractAs3ParserTest
+{
+   protected void assertStatement( final String message,
+                                   final String input,
+                                   final String expected ) throws TokenException
+   {
+      scn.setLines( new String[]
+      { input,
+                  "__END__" } );
+      asp.nextToken();
+      final String result = new ASTToXMLConverter().convert( asp.parseStatement() );
+      assertEquals( message,
+                    expected,
+                    result );
+   }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/b0fc5f17/FlexPMD/as3-parser/src/test/java/de/bokelberg/flex/parser/NestedNodeTest.java
----------------------------------------------------------------------
diff --git a/FlexPMD/as3-parser/src/test/java/de/bokelberg/flex/parser/NestedNodeTest.java b/FlexPMD/as3-parser/src/test/java/de/bokelberg/flex/parser/NestedNodeTest.java
new file mode 100644
index 0000000..7d547ef
--- /dev/null
+++ b/FlexPMD/as3-parser/src/test/java/de/bokelberg/flex/parser/NestedNodeTest.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 de.bokelberg.flex.parser;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.adobe.ac.pmd.parser.NodeKind;
+import com.adobe.ac.pmd.parser.exceptions.TokenException;
+
+public class NestedNodeTest extends AbstractAs3ParserTest
+{
+   private NestedNode function;
+
+   @Override
+   @Before
+   public void setUp()
+   {
+      super.setUp();
+
+      final String code = "public function foo() : void     {"
+            + "while(i>0){" + "while(true){" + "switch(a){" + "case 1:break;default:return;" + "}" + "}"
+            + "}" + "}";
+      final Node classNode = parseClass( code );
+
+      function = ( Node ) classNode.getChild( 0 );
+   }
+
+   @Test
+   public void testComputeCyclomaticComplexity()
+   {
+      assertEquals( 5,
+                    function.computeCyclomaticComplexity() );
+   }
+
+   @Test
+   public void testCountNodeFromType()
+   {
+      assertEquals( 2,
+                    function.countNodeFromType( NodeKind.WHILE ) );
+   }
+
+   @Test
+   public void testGetLastChild()
+   {
+      assertEquals( NodeKind.RETURN,
+                    function.getLastChild().getId() );
+
+      assertNull( function.getChild( Integer.MAX_VALUE ) );
+   }
+
+   @Test
+   public void testIs()
+   {
+      assertFalse( function.is( null ) );
+   }
+
+   private Node parseClass( final String input )
+   {
+      scn.setLines( new String[]
+      { "{",
+                  input,
+                  "}",
+                  "__END__" } );
+      try
+      {
+         asp.nextToken();
+         asp.nextToken(); // skip {
+         return asp.parseClassContent();
+      }
+      catch ( final TokenException e )
+      {
+         e.printStackTrace();
+      }
+      return null;
+   }
+}

http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/b0fc5f17/FlexPMD/as3-parser/src/test/java/de/bokelberg/flex/parser/NodeTest.java
----------------------------------------------------------------------
diff --git a/FlexPMD/as3-parser/src/test/java/de/bokelberg/flex/parser/NodeTest.java b/FlexPMD/as3-parser/src/test/java/de/bokelberg/flex/parser/NodeTest.java
new file mode 100644
index 0000000..cf7dae1
--- /dev/null
+++ b/FlexPMD/as3-parser/src/test/java/de/bokelberg/flex/parser/NodeTest.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 de.bokelberg.flex.parser;
+
+import org.junit.Test;
+
+import com.adobe.ac.pmd.parser.exceptions.TokenException;
+
+public class NodeTest extends AbstractAs3ParserTest
+{
+   @Test
+   public void testFindPrimaryStatementsFromNameInChildren() throws TokenException
+   {
+      final Node ast = parseFunction( "function set a( value : int ) : void { trace(\"lala\")}" );
+
+      assertEquals( 2,
+                    ast.findPrimaryStatementsFromNameInChildren( new String[]
+                    { "trace",
+                                "\"lala\"" } ).size() );
+   }
+
+   @Test
+   public void testToString() throws TokenException
+   {
+      final Node ast = parseFunction( "function set a( value : int ) : void { trace(\"lala\")}" );
+
+      assertEquals( "content set mod-list  a  parameter-list parameter name-type-init "
+                          + "value  int     void  block call trace  arguments \"lala\"      ",
+                    ast.toString() );
+   }
+
+   private Node parseFunction( final String input ) throws TokenException
+   {
+      scn.setLines( new String[]
+      { "{",
+                  input,
+                  "}",
+                  "__END__" } );
+      asp.nextToken(); // first call
+      asp.nextToken(); // skip {
+      return asp.parseClassContent();
+   }
+}

http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/b0fc5f17/FlexPMD/as3-parser/src/test/java/de/bokelberg/flex/parser/TestAS3Parser.java
----------------------------------------------------------------------
diff --git a/FlexPMD/as3-parser/src/test/java/de/bokelberg/flex/parser/TestAS3Parser.java b/FlexPMD/as3-parser/src/test/java/de/bokelberg/flex/parser/TestAS3Parser.java
new file mode 100644
index 0000000..44738f1
--- /dev/null
+++ b/FlexPMD/as3-parser/src/test/java/de/bokelberg/flex/parser/TestAS3Parser.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 de.bokelberg.flex.parser;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URISyntaxException;
+
+import org.junit.Test;
+
+import com.adobe.ac.pmd.files.impl.FileUtils;
+import com.adobe.ac.pmd.parser.IParserNode;
+import com.adobe.ac.pmd.parser.exceptions.TokenException;
+
+public class TestAS3Parser extends AbstractAs3ParserTest
+{
+   @Test
+   public void testBuildAst() throws IOException,
+                             URISyntaxException,
+                             TokenException
+   {
+      asp.buildAst( getClass().getResource( "/examples/unformatted/IContext.as" ).toURI().getPath() );
+      asp.buildAst( getClass().getResource( "/examples/FlexPMD115.as" ).toURI().getPath() );
+      asp.buildAst( getClass().getResource( "/examples/JPEGEncoder.as" ).toURI().getPath() );
+      asp.buildAst( getClass().getResource( "/examples/JPEGEncoder2.as" ).toURI().getPath() );
+      asp.buildAst( getClass().getResource( "/examples/FisheyeBase.as" ).toURI().getPath() );
+      asp.buildAst( getClass().getResource( "/examples/FlexPMD98.as" ).toURI().getPath() );
+      asp.buildAst( getClass().getResource( "/examples/FlexPMD195.as" ).toURI().getPath() );
+      final String titlePath = getClass().getResource( "/examples/unformatted/Title.as" ).toURI().getPath();
+
+      asp.buildAst( titlePath );
+      asp.buildAst( titlePath,
+                    FileUtils.readLines( new File( titlePath ) ) );
+   }
+
+   @Test
+   public void testBuildAst_AS2() throws IOException,
+                                 URISyntaxException,
+                                 TokenException
+   {
+      asp.buildAst( getClass().getResource( "/examples/toAS2/src/fw/data/request/ResultListener.as" )
+                              .toURI()
+                              .getPath() );
+
+      asp.buildAst( getClass().getResource( "/examples/toAS2/src/epg/StateExit_AS2.as" ).toURI().getPath() );
+   }
+
+   @Test
+   public void testBuildAst2() throws IOException,
+                              TokenException,
+                              URISyntaxException
+   {
+      final IParserNode flexPmd62 = asp.buildAst( getClass().getResource( "/examples/FlexPMD62.as" )
+                                                            .toURI()
+                                                            .getPath() );
+
+      assertEquals( "com.test.testy.ui.components",
+                    flexPmd62.getChild( 0 ).getChild( 0 ).getStringValue() );
+
+   }
+}

http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/b0fc5f17/FlexPMD/as3-parser/src/test/java/de/bokelberg/flex/parser/TestAS3Scanner.java
----------------------------------------------------------------------
diff --git a/FlexPMD/as3-parser/src/test/java/de/bokelberg/flex/parser/TestAS3Scanner.java b/FlexPMD/as3-parser/src/test/java/de/bokelberg/flex/parser/TestAS3Scanner.java
new file mode 100644
index 0000000..240b354
--- /dev/null
+++ b/FlexPMD/as3-parser/src/test/java/de/bokelberg/flex/parser/TestAS3Scanner.java
@@ -0,0 +1,362 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 de.bokelberg.flex.parser;
+
+import org.junit.Test;
+
+import de.bokelberg.flex.parser.AS3Scanner.Token;
+
+public class TestAS3Scanner extends AbstractAs3ParserTest
+{
+   @Test
+   public void testAssignments()
+   {
+      final String[] lines = new String[]
+      { "=",
+                  "+=",
+                  "-=",
+                  "%=",
+                  "^=",
+                  "&=",
+                  "|=",
+                  "/=" };
+      scn.setLines( lines );
+
+      for ( int i = 0; i < lines.length; i++ )
+      {
+         assertText( Integer.toString( i ),
+                     lines[ i ] );
+         assertText( "\n" );
+      }
+   }
+
+   @Test
+   public void testBooleanOperators()
+   {
+      final String[] lines = new String[]
+      { "&&",
+                  "&=",
+                  "||",
+                  "|=" };
+      scn.setLines( lines );
+
+      for ( int i = 0; i < lines.length; i++ )
+      {
+         assertText( Integer.toString( i ),
+                     lines[ i ] );
+         assertText( "\n" );
+      }
+   }
+
+   @Test
+   public void testComparisonOperators()
+   {
+      final String[] lines = new String[]
+      { ">",
+                  ">>>=",
+                  ">>>",
+                  ">>=",
+                  ">>",
+                  ">=",
+                  "===",
+                  "==",
+                  "!==",
+                  "!=" };
+      scn.setLines( lines );
+
+      for ( int i = 0; i < lines.length; i++ )
+      {
+         assertText( Integer.toString( i ),
+                     lines[ i ] );
+         assertText( "\n" );
+      }
+   }
+
+   @Test
+   public void testIdentifiers()
+   {
+      final String[] lines = new String[]
+      { "a",
+                  "a.b.*",
+                  "a.b::c",
+                  "a.E" };
+      scn.setLines( lines );
+
+      assertText( "1",
+                  lines[ 0 ] );
+      assertText( "\n" );
+
+      assertText( "2",
+                  "a" );
+      assertText( "2",
+                  "." );
+      assertText( "2",
+                  "b" );
+      assertText( "2",
+                  "." );
+      assertText( "2",
+                  "*" );
+      assertText( "\n" );
+
+      assertText( "3",
+                  "a" );
+      assertText( "3",
+                  "." );
+      assertText( "3",
+                  "b" );
+      assertText( "3",
+                  "::" );
+      assertText( "3",
+                  "c" );
+      assertText( "\n" );
+
+      assertText( "4",
+                  "a" );
+      assertText( "4",
+                  "." );
+      assertText( "4",
+                  "E" );
+   }
+
+   @Test
+   public void testIsDecimalChar()
+   {
+      final String decimalString = "0123456789";
+      for ( int i = 0; i < decimalString.length(); i++ )
+      {
+         assertTrue( "",
+                     AS3Scanner.isDecimalChar( decimalString.charAt( i ) ) );
+      }
+      assertFalse( "",
+                   AS3Scanner.isDecimalChar( ( char ) 0 ) );
+
+   }
+
+   @Test
+   public void testIsHex()
+   {
+      assertTrue( "",
+                  scn.isHexChar( '0' ) );
+      assertTrue( "",
+                  scn.isHexChar( '9' ) );
+      assertTrue( "",
+                  scn.isHexChar( 'A' ) );
+      assertTrue( "",
+                  scn.isHexChar( 'a' ) );
+      assertTrue( "",
+                  scn.isHexChar( 'F' ) );
+      assertTrue( "",
+                  scn.isHexChar( 'f' ) );
+      assertFalse( "",
+                   scn.isHexChar( ';' ) );
+      assertFalse( "",
+                   scn.isHexChar( ']' ) );
+      assertFalse( "",
+                   scn.isHexChar( ' ' ) );
+   }
+
+   @Test
+   public void testMultiLineComment()
+   {
+      final String[] lines = new String[]
+      { "/* this is a multi line comment, not really */",
+                  "/** now for real",
+                  "/* now for real",
+                  "*/" };
+      scn.setLines( lines );
+
+      assertText( lines[ 0 ] );
+      assertText( "\n" );
+      assertText( "/** now for real\n/* now for real\n*/" );
+   }
+
+   @Test
+   public void testMultilineXML()
+   {
+      final String[] lines = new String[]
+      { "<?xml version=\"1.0\"?>",
+                  "<a>",
+                  "<b>test</b>",
+                  "</a>" };
+      scn.setLines( lines );
+      assertText( join( lines,
+                        "\n" ) );
+   }
+
+   @Test
+   public void testMultipleWords()
+   {
+      final String[] lines = new String[]
+      { "word1 word2 word3",
+                  "word4",
+                  "word5 word6" };
+      scn.setLines( lines );
+
+      assertText( "word1" );
+      assertText( "word2" );
+      assertText( "word3" );
+      assertText( "\n" );
+      assertText( "word4" );
+      assertText( "\n" );
+      assertText( "word5" );
+      assertText( "word6" );
+   }
+
+   @Test
+   public void testNumbers()
+   {
+      final String[] lines = new String[]
+      { "0",
+                  "1.2",
+                  "1.2E5",
+                  "0xffgg" };
+      scn.setLines( lines );
+
+      assertText( lines[ 0 ] );
+      assertText( "\n" );
+      assertText( lines[ 1 ] );
+      assertText( "\n" );
+      assertText( lines[ 2 ] );
+      assertText( "\n" );
+      assertText( lines[ 3 ] );
+   }
+
+   @Test
+   public void testPlusSymbols()
+   {
+      final String[] lines = new String[]
+      { "++",
+                  "+=",
+                  "+",
+                  "--",
+                  "-=",
+                  "-" };
+      scn.setLines( lines );
+
+      for ( int i = 0; i < lines.length; i++ )
+      {
+         assertText( Integer.toString( i ),
+                     lines[ i ] );
+         assertText( "\n" );
+      }
+   }
+
+   @Test
+   public void testSingleCharacterSymbols()
+   {
+      final String[] lines = "{}()[]:;,?~".split( "" );
+      scn.setLines( lines );
+
+      // the first entry is empty, so we skip it
+      for ( int i = 1; i < lines.length; i++ )
+      {
+         assertText( "\n" );
+         assertText( Integer.toString( i ),
+                     lines[ i ] );
+      }
+   }
+
+   @Test
+   public void testSingleLineComment()
+   {
+      final String[] lines = new String[]
+      { "//this is a single line comment",
+                  "word //another single line comment" };
+      scn.setLines( lines );
+
+      assertText( lines[ 0 ] );
+      assertText( "\n" );
+      assertText( "word" );
+      assertText( "//another single line comment" );
+   }
+
+   @Test
+   public void testSingleWord()
+   {
+      final String[] lines = new String[]
+      { "word" };
+      scn.setLines( lines );
+
+      assertText( lines[ 0 ] );
+   }
+
+   @Test
+   public void testStrings()
+   {
+      final String[] lines = new String[]
+      { "\"string\"",
+                  "\'string\'",
+                  "\"string\\\"\"" };
+      scn.setLines( lines );
+
+      assertText( "1",
+                  lines[ 0 ] );
+      assertText( "\n" );
+      assertText( "2",
+                  lines[ 1 ] );
+      assertText( "\n" );
+      assertText( "3",
+                  lines[ 2 ] );
+   }
+
+   @Test
+   public void testXML()
+   {
+      final String[] lines = new String[]
+      { "<root/>",
+                  "<root>test</root>",
+                  "<?xml version=\"1.0\"?><root>test</root>" };
+      scn.setLines( lines );
+      for ( int i = 0; i < lines.length; i++ )
+      {
+         assertText( Integer.toString( i ),
+                     lines[ i ] );
+         assertText( "\n" );
+      }
+   }
+
+   private void assertText( final String text )
+   {
+      assertText( "",
+                  text );
+   }
+
+   private void assertText( final String message,
+                            final String text )
+   {
+      Token tokent = null;
+      tokent = scn.nextToken();
+      assertEquals( message,
+                    text,
+                    tokent.getText() );
+   }
+
+   private String join( final String[] lines,
+                        final String delimiter )
+   {
+      final StringBuffer result = new StringBuffer();
+      for ( int i = 0; i < lines.length; i++ )
+      {
+         if ( i > 0 )
+         {
+            result.append( delimiter );
+         }
+         result.append( lines[ i ] );
+      }
+      return result.toString();
+   }
+}

http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/b0fc5f17/FlexPMD/as3-parser/src/test/java/de/bokelberg/flex/parser/TestAS3ScannerWithFiles.java
----------------------------------------------------------------------
diff --git a/FlexPMD/as3-parser/src/test/java/de/bokelberg/flex/parser/TestAS3ScannerWithFiles.java b/FlexPMD/as3-parser/src/test/java/de/bokelberg/flex/parser/TestAS3ScannerWithFiles.java
new file mode 100644
index 0000000..d101579
--- /dev/null
+++ b/FlexPMD/as3-parser/src/test/java/de/bokelberg/flex/parser/TestAS3ScannerWithFiles.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 de.bokelberg.flex.parser;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URISyntaxException;
+
+import org.junit.Test;
+
+import com.adobe.ac.pmd.files.impl.FileUtils;
+
+import de.bokelberg.flex.parser.AS3Scanner.Token;
+
+public class TestAS3ScannerWithFiles extends AbstractAs3ParserTest
+{
+   @Test
+   public void testSimple() throws IOException,
+                           URISyntaxException
+   {
+      final String[] expected = new String[]
+      { "package",
+                  "simple",
+                  "{",
+                  "public",
+                  "class",
+                  "Simple",
+                  "{",
+                  "public",
+                  "function",
+                  "Simple",
+                  "(",
+                  ")",
+                  "{",
+                  "trace",
+                  "(",
+                  "\"Simple\"",
+                  ")",
+                  ";",
+                  "}",
+                  "}" };
+      assertFile( expected,
+                  "Simple.as" );
+   }
+
+   private void assertFile( final String[] expected,
+                            final String fileName ) throws IOException,
+                                                   URISyntaxException
+   {
+      final String[] lines = FileUtils.readLines( new File( getClass().getResource( "/examples/unformatted/" )
+                                                                       .toURI()
+                                                                       .getPath()
+            + fileName ) );
+      assertLines( expected,
+                   lines );
+   }
+
+   private void assertLines( final String[] expected,
+                             final String[] lines )
+   {
+      scn.setLines( lines );
+      for ( int i = 0; i < expected.length; i++ )
+      {
+         assertText( Integer.toString( i ),
+                     expected[ i ] );
+      }
+   }
+
+   private void assertText( final String message,
+                            final String text )
+   {
+      Token token = null;
+      token = scn.nextToken();
+      assertEquals( message,
+                    text,
+                    token.getText() );
+   }
+}

http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/b0fc5f17/FlexPMD/as3-parser/src/test/java/de/bokelberg/flex/parser/TestClass.java
----------------------------------------------------------------------
diff --git a/FlexPMD/as3-parser/src/test/java/de/bokelberg/flex/parser/TestClass.java b/FlexPMD/as3-parser/src/test/java/de/bokelberg/flex/parser/TestClass.java
new file mode 100644
index 0000000..54235ef
--- /dev/null
+++ b/FlexPMD/as3-parser/src/test/java/de/bokelberg/flex/parser/TestClass.java
@@ -0,0 +1,144 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 de.bokelberg.flex.parser;
+
+import org.junit.Test;
+
+import com.adobe.ac.pmd.parser.exceptions.TokenException;
+
+public class TestClass extends AbstractAs3ParserTest
+{
+   @Test
+   public void testExtends() throws TokenException
+   {
+      assertPackageContent( "1",
+                            "public class A extends B { } ",
+                            "<content line=\"2\">"
+                                  + "<class line=\"2\">" + "<name line=\"2\">A</name><mod-list line=\"2\">"
+                                  + "<mod line=\"2\">public</mod></mod-list><extends line=\"2\""
+                                  + ">B</extends><content line=\"2\"></content>" + "</class></content>" );
+
+      assertPackageContent( "1",
+                            "public class A extends com.adobe::B { } ",
+                            "<content line=\"2\"><class line=\"2\"><name line=\"2\""
+                                  + ">A</name><mod-list line=\"2\"><mod line=\"2\""
+                                  + ">public</mod></mod-list><extends line=\"2\""
+                                  + ">com.adobe::B</extends><content line=\"2\"></content>"
+                                  + "</class></content>" );
+   }
+
+   @Test
+   public void testFinalClass() throws TokenException
+   {
+      assertPackageContent( "",
+                            "public final class Title{ }",
+                            "<content line=\"2\">"
+                                  + "<class line=\"2\">" + "<name line=\"2\">Title</name>"
+                                  + "<mod-list line=\"2\">" + "<mod line=\"2\">public</mod>"
+                                  + "<mod line=\"2\">final</mod></mod-list>" + "<content line=\"2\""
+                                  + "></content>" + "</class>" + "</content>" );
+   }
+
+   @Test
+   public void testFullFeatured() throws TokenException
+   {
+      // assertPackageContent( "",
+      // "public class A { public static const RULE_REMOVED : String = \"ruleRemoved\";}",
+      // "" );
+
+      assertPackageContent( "1",
+                            "public class A extends B implements C,D { } ",
+                            "<content line=\"2\"><class line=\"2\">"
+                                  + "<name line=\"2\">A</name><mod-list line=\"2\">"
+                                  + "<mod line=\"2\">public</mod></mod-list><extends line=\"2\""
+                                  + ">B</extends><implements-list line=\"2\">"
+                                  + "<implements line=\"2\">C</implements><implements line=\"2\""
+                                  + ">D</implements></implements-list><content line=\"2\">"
+                                  + "</content></class></content>" );
+   }
+
+   @Test
+   public void testImplementsList() throws TokenException
+   {
+      assertPackageContent( "1",
+                            "public class A implements B,C { } ",
+                            "<content line=\"2\"><class line=\"2\">"
+                                  + "<name line=\"2\">A</name><mod-list line=\"2\""
+                                  + "><mod line=\"2\">public</mod></mod-list>"
+                                  + "<implements-list line=\"2\"><implements line=\"2\""
+                                  + ">B</implements><implements line=\"2\">"
+                                  + "C</implements></implements-list><content line=\"2\">"
+                                  + "</content></class></content>" );
+   }
+
+   @Test
+   public void testImplementsSingle() throws TokenException
+   {
+      assertPackageContent( "1",
+                            "public class A implements B { } ",
+                            "<content line=\"2\"><class line=\"2\">"
+                                  + "<name line=\"2\">A</name><mod-list line=\"2\""
+                                  + "><mod line=\"2\">public</mod></mod-list>"
+                                  + "<implements-list line=\"2\"><implements line=\"2\""
+                                  + ">B</implements></implements-list><content line=\"2\""
+                                  + "></content></class></content>" );
+   }
+
+   @Test
+   public void testImportInsideClass() throws TokenException
+   {
+      assertPackageContent( "",
+                            "public final class Title{ import lala.lala; }",
+                            "<content line=\"2\">"
+                                  + "<class line=\"2\"><name line=\"2\">Title</name>"
+                                  + "<mod-list line=\"2\"><mod line=\"2\">public</mod>"
+                                  + "<mod line=\"2\">final</mod></mod-list>"
+                                  + "<content line=\"2\"><import line=\"2\""
+                                  + ">lala.lala</import></content></class></content>" );
+
+   }
+
+   @Test
+   public void testInclude() throws TokenException
+   {
+      assertPackageContent( "1",
+                            "public class A extends B { include \"ITextFieldInterface.asz\" } ",
+                            "<content line=\"2\"><class line=\"2\">"
+                                  + "<name line=\"2\">A</name><mod-list line=\"2\">"
+                                  + "<mod line=\"2\">public</mod></mod-list>"
+                                  + "<extends line=\"2\">B</extends>"
+                                  + "<content line=\"2\"></content></class></content>" );
+   }
+
+   private void assertPackageContent( final String message,
+                                      final String input,
+                                      final String expected ) throws TokenException
+   {
+      scn.setLines( new String[]
+      { "{",
+                  input,
+                  "}",
+                  "__END__" } );
+      asp.nextToken(); // first call
+      asp.nextToken(); // skip {
+      final String result = new ASTToXMLConverter().convert( asp.parsePackageContent() );
+      assertEquals( message,
+                    expected,
+                    result );
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/b0fc5f17/FlexPMD/as3-parser/src/test/java/de/bokelberg/flex/parser/TestClassContent.java
----------------------------------------------------------------------
diff --git a/FlexPMD/as3-parser/src/test/java/de/bokelberg/flex/parser/TestClassContent.java b/FlexPMD/as3-parser/src/test/java/de/bokelberg/flex/parser/TestClassContent.java
new file mode 100644
index 0000000..79b767d
--- /dev/null
+++ b/FlexPMD/as3-parser/src/test/java/de/bokelberg/flex/parser/TestClassContent.java
@@ -0,0 +1,307 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 de.bokelberg.flex.parser;
+
+import org.junit.Test;
+
+import com.adobe.ac.pmd.parser.exceptions.TokenException;
+
+public class TestClassContent extends AbstractAs3ParserTest
+{
+   @Test
+   public void testCommentInMethod() throws TokenException
+   {
+      assertClassContent( "",
+                          "public function log():void{/* comment */}",
+                          "<function line=\"2\"><mod-list line=\"2\"><mod "
+                                + "line=\"2\">public</mod></mod-list><name line=\"2\">log</name>"
+                                + "<parameter-list line=\"2\"></parameter-list><type line=\"2\">"
+                                + "void</type><block line=\"2\"><multi-line-comment line=\"2\">"
+                                + "/* comment */</multi-line-comment></block></function>" );
+
+      assertClassContent( "",
+                          new String[]
+                          { "{",
+                                      "public function log():void{// comment ",
+                                      "}",
+                                      "}",
+                                      "__END__" },
+                          "<function line=\"2\"><mod-list line=\"2\"><mod line=\"2\""
+                                + ">public</mod></mod-list><name line=\"2\">log</name>"
+                                + "<parameter-list line=\"2\"></parameter-list><type line=\"2\""
+                                + ">void</type><block line=\"2\"></block></function>" );
+   }
+
+   @Test
+   public void testConstDeclarations() throws TokenException
+   {
+      assertClassContent( "1",
+                          "const a",
+                          "<const-list line=\"2\"><mod-list line=\"2\">"
+                                + "</mod-list><name-type-init line=\"2\"><name line=\"2\">a"
+                                + "</name><type line=\"3\"></type></name-type-init></const-list>" );
+
+      assertClassContent( "2",
+                          "public const a",
+                          "<const-list line=\"2\"><mod-list line=\"2\">"
+                                + "<mod line=\"2\">public</mod></mod-list><name-type-init line=\"2\""
+                                + "><name line=\"2\">a</name><type line=\"3\">"
+                                + "</type></name-type-init></const-list>" );
+
+      assertClassContent( "3",
+                          "public static const a : int = 0",
+                          "<const-list line=\"2\"><mod-list line=\"2\">"
+                                + "<mod line=\"2\">public</mod><mod line=\"2\">"
+                                + "static</mod></mod-list><name-type-init line=\"2\"><name "
+                                + "line=\"2\">a</name><type line=\"2\">int</type>"
+                                + "<init line=\"2\"><primary line=\"2\">0</primary>"
+                                + "</init></name-type-init></const-list>" );
+
+      assertClassContent( "4",
+                          "[Bindable] const a",
+                          "<const-list line=\"2\"><meta-list line=\"2\">"
+                                + "<meta line=\"2\">Bindable</meta></meta-list><mod-list line=\"2\""
+                                + "></mod-list><name-type-init line=\"2\">"
+                                + "<name line=\"2\">a</name><type line=\"3\">"
+                                + "</type></name-type-init></const-list>" );
+   }
+
+   @Test
+   public void testFlexPMD211() throws TokenException
+   {
+      assertClassContent( "",
+                          "private function foo(sf:int):void{"
+                                + "var a:Vector.<String> = new Vector.<String>()}",
+                          "<function line=\"2\"><mod-list line=\"2\"><mod line=\"2\">private</mod>"
+                                + "</mod-list><name line=\"2\">foo</name><parameter-list line=\"2\">"
+                                + "<parameter line=\"2\"><name-type-init line=\"2\"><name line=\"2\">sf</name>"
+                                + "<type line=\"2\">int</type></name-type-init></parameter></parameter-list>"
+                                + "<type line=\"2\">void</type><block line=\"2\"><var-list line=\"2\">"
+                                + "<name-type-init line=\"2\"><name line=\"2\">a</name><vector line=\"2\">"
+                                + "<type line=\"2\">String</type></vector><init line=\"2\"><new line=\"2\">"
+                                + "<primary line=\"2\">Vector</primary><vector line=\"2\"><vector line=\"2\">"
+                                + "<type line=\"2\">String</type></vector></vector><arguments line=\"2\">"
+                                + "</arguments></new></init></name-type-init></var-list></block></function>" );
+      assertClassContent( "",
+                          "private function foo(sf:int):void{"
+                                + "var a:Vector.<String> = new Vector.<String>();}",
+                          "<function line=\"2\"><mod-list line=\"2\"><mod line=\"2\">private</mod></mod-list>"
+                                + "<name line=\"2\">foo</name><parameter-list line=\"2\"><parameter line=\"2\">"
+                                + "<name-type-init line=\"2\"><name line=\"2\">sf</name><type line=\"2\">int</type>"
+                                + "</name-type-init></parameter></parameter-list><type line=\"2\">void</type>"
+                                + "<block line=\"2\"><var-list line=\"2\"><name-type-init line=\"2\">"
+                                + "<name line=\"2\">a</name><vector line=\"2\"><type line=\"2\">String</type>"
+                                + "</vector><init line=\"2\"><new line=\"2\"><primary line=\"2\">Vector</primary>"
+                                + "<vector line=\"2\"><vector line=\"2\"><type line=\"2\">String</type></vector>"
+                                + "</vector><arguments line=\"2\"></arguments></new></init></name-type-init>"
+                                + "</var-list></block></function>" );
+   }
+
+   @Test
+   public void testImports() throws TokenException
+   {
+      assertClassContent( "1",
+                          "import a.b.c;",
+                          "<import line=\"2\">a.b.c</import>" );
+      assertClassContent( "2",
+                          "import a.b.c import x.y.z",
+                          "<import line=\"2\">a.b.c</import>"
+                                + "<import line=\"2\">x.y.z</import>" );
+   }
+
+   @Test
+   public void testMethods() throws TokenException
+   {
+      assertClassContent( "1",
+                          "function a(){}",
+                          "<function line=\"2\"><mod-list line=\"2\">"
+                                + "</mod-list><name line=\"2\">a</name><parameter-list line=\"2\""
+                                + "></parameter-list><type line=\"2\"></type><block "
+                                + "line=\"2\"></block></function>" );
+
+      assertClassContent( "2",
+                          "function set a( value : int ) : void {}",
+                          "<set line=\"2\"><mod-list line=\"2\">"
+                                + "</mod-list><name line=\"2\">a</name>"
+                                + "<parameter-list line=\"2\"><parameter line=\"2\">"
+                                + "<name-type-init line=\"2\"><name line=\"2\">value"
+                                + "</name><type line=\"2\">int</type></name-type-init></parameter>"
+                                + "</parameter-list><type line=\"2\">void</type><block line=\"2\""
+                                + "></block></set>" );
+
+      assertClassContent( "3",
+                          "function get a() : int {}",
+                          "<get line=\"2\"><mod-list line=\"2\">"
+                                + "</mod-list><name line=\"2\">a</name><parameter-list line=\"2\""
+                                + "></parameter-list><type line=\"2\">int"
+                                + "</type><block line=\"2\"></block></get>" );
+
+      assertClassContent( "function with default parameter",
+                          "public function newLine ( height:*='' ):void{}",
+                          "<function line=\"2\"><mod-list line=\"2\"><mod line=\"2\""
+                                + ">public</mod></mod-list><name line=\"2\">newLine"
+                                + "</name><parameter-list line=\"2\"><parameter line=\"2\""
+                                + "><name-type-init line=\"2\"><name line=\"2\""
+                                + ">height</name><type line=\"2\">*</type>"
+                                + "<init line=\"2\"><primary line=\"2\">''"
+                                + "</primary></init></name-type-init></parameter></parameter-list>"
+                                + "<type line=\"2\">void</type><block line=\"2\">" + "</block></function>" );
+   }
+
+   @Test
+   public void testMethodsWithAsDoc() throws TokenException
+   {
+      scn.setLines( new String[]
+      { "{",
+                  "/** AsDoc */public function a(){}",
+                  "}",
+                  "__END__" } );
+      asp.nextToken(); // first call
+      asp.nextToken(); // skip {
+
+      assertEquals( "<content line=\"2\"><function line=\"2\">"
+                          + "<as-doc line=\"2\">/** AsDoc */</as-doc><mod-list "
+                          + "line=\"2\"><mod line=\"2\">public</mod>"
+                          + "</mod-list><name line=\"2\">a</name><parameter-list "
+                          + "line=\"2\"></parameter-list><type line=\"2\">"
+                          + "</type><block line=\"2\"></block></function></content>",
+                    new ASTToXMLConverter().convert( asp.parseClassContent() ) );
+   }
+
+   @Test
+   public void testMethodsWithMultiLineComments() throws TokenException
+   {
+      scn.setLines( new String[]
+      { "{",
+                  "/* Commented */public function a(){}",
+                  "}",
+                  "__END__" } );
+      asp.nextToken(); // first call
+      asp.nextToken(); // skip {
+
+      assertEquals( "<content line=\"2\"><multi-line-comment line=\"2\">"
+                          + "/* Commented */</multi-line-comment><function line=\"2\">"
+                          + "<mod-list line=\"2\"><mod line=\"2\">public"
+                          + "</mod></mod-list><name line=\"2\">a</name><parameter-list "
+                          + "line=\"2\"></parameter-list><type line=\"2\">"
+                          + "</type><block line=\"2\"></block></function></content>",
+                    new ASTToXMLConverter().convert( asp.parseClassContent() ) );
+   }
+
+   @Test
+   public void testMethodWithMetadataComment() throws TokenException
+   {
+      scn.setLines( new String[]
+      { "{",
+                  "/* Comment */ [Bindable] public function a () : void { }",
+                  "}",
+                  "__END__" } );
+      asp.nextToken(); // first call
+      asp.nextToken(); // skip {
+
+      assertEquals( "1",
+                    "<content line=\"2\"><multi-line-comment line=\"2\">"
+                          + "/* Comment */</multi-line-comment><function line=\"2\">"
+                          + "<meta-list line=\"2\"><meta line=\"2\">Bindable"
+                          + "</meta></meta-list><mod-list line=\"2\"><mod line=\"2\""
+                          + ">public</mod></mod-list><name line=\"2\">a</name>"
+                          + "<parameter-list line=\"2\"></parameter-list><type line=\"2\""
+                          + ">void</type><block line=\"2\"></block></function>" + "</content>",
+                    new ASTToXMLConverter().convert( asp.parseClassContent() ) );
+   }
+
+   @Test
+   public void testRestParameter() throws TokenException
+   {
+      assertClassContent( "",
+                          "public function log(message:String, ... rest):void{}",
+                          "<function line=\"2\"><mod-list line=\"2\">"
+                                + "<mod line=\"2\">public</mod></mod-list><name line=\"2\">"
+                                + "log</name><parameter-list line=\"2\">"
+                                + "<parameter line=\"2\"><name-type-init line=\"2\">"
+                                + "<name line=\"2\">message</name><type line=\"2\">String"
+                                + "</type></name-type-init></parameter><parameter line=\"2\">"
+                                + "<rest line=\"2\">rest</rest></parameter></parameter-list>"
+                                + "<type line=\"2\">void</type><block line=\"2\">" + "</block></function>" );
+   }
+
+   @Test
+   public void testVarDeclarations() throws TokenException
+   {
+      assertClassContent( "1",
+                          "var a",
+                          "<var-list line=\"2\"><mod-list line=\"2\">"
+                                + "</mod-list><name-type-init line=\"2\"><name line=\"2\">a"
+                                + "</name><type line=\"3\"></type></name-type-init></var-list>" );
+
+      assertClassContent( "2",
+                          "public var a;",
+                          "<var-list line=\"2\"><mod-list line=\"2\">"
+                                + "<mod line=\"2\">public</mod></mod-list><name-type-init line=\"2\""
+                                + "><name line=\"2\">a</name><type line=\"2\""
+                                + "></type></name-type-init></var-list>" );
+
+      assertClassContent( "3",
+                          "public static var a : int = 0",
+                          "<var-list line=\"2\"><mod-list line=\"2\">"
+                                + "<mod line=\"2\">public</mod><mod line=\"2\">"
+                                + "static</mod></mod-list><name-type-init line=\"2\">"
+                                + "<name line=\"2\">a</name><type line=\"2\">int</type>"
+                                + "<init line=\"2\"><primary line=\"2\">0</primary>"
+                                + "</init></name-type-init></var-list>" );
+
+      assertClassContent( "4",
+                          "[Bindable] var a",
+                          "<var-list line=\"2\"><meta-list line=\"2\">"
+                                + "<meta line=\"2\">Bindable</meta></meta-list>"
+                                + "<mod-list line=\"2\"></mod-list>" + "<name-type-init line=\"2\">"
+                                + "<name line=\"2\">a</name><type line=\"3\">"
+                                + "</type></name-type-init></var-list>" );
+   }
+
+   private void assertClassContent( final String message,
+                                    final String input,
+                                    final String expected ) throws TokenException
+   {
+      scn.setLines( new String[]
+      { "{",
+                  input,
+                  "}",
+                  "__END__" } );
+      asp.nextToken(); // first call
+      asp.nextToken(); // skip {
+      final String result = new ASTToXMLConverter().convert( asp.parseClassContent() );
+      assertEquals( message,
+                    "<content line=\"2\">"
+                          + expected + "</content>",
+                    result );
+   }
+
+   private void assertClassContent( final String message,
+                                    final String[] input,
+                                    final String expected ) throws TokenException
+   {
+      scn.setLines( input );
+      asp.nextToken(); // first call
+      asp.nextToken(); // skip {
+      final String result = new ASTToXMLConverter().convert( asp.parseClassContent() );
+      assertEquals( message,
+                    "<content line=\"2\">"
+                          + expected + "</content>",
+                    result );
+   }
+}