// Transmogrify License
//
// Copyright (c) 2001, ThoughtWorks, Inc.
// All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// - Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// - Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// Neither the name of the ThoughtWorks, Inc. nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.puppycrawl.tools.checkstyle.checks.usage.transmogrify;
import java.io.File;
import java.util.Enumeration;
import java.util.Vector;
import com.puppycrawl.tools.checkstyle.api.TokenTypes;
/**
* this is the class that does the work of the "walking" step --
* going through the SymTabAST and constructing the definitions and
* references. The SymTabAST is constructed in a DOMish fashion, i.e.
* each node knows about its first child and its next sibling. The
* DFS search is carried out by two functions -- walkTree, which
* deals with a single SymTabAST node, and walkSibilngs, which deals with
* all the siblings of an SymTabAST node.
*/
public class TableMaker {
private SymbolTable symbolTable;
private SymTabAST _tree;
private File currentFile;
private Vector imports = new Vector();
/**
* Constructor for the TableMaker class
*
* @param tree is the<code>SymTabAST</code> from which to
* build the <code>TableMaker</code>. It is the tree which wil be walked
* in order to build definitions and references.
*/
public TableMaker(SymTabAST tree)
{
_tree = tree;
}
/**
* returns the <code> SymTabAST </code> which is the
* SymTabAST generated from the parsed Java Source files.
*
* @return <code>SymTabAST</code>
*/
public SymTabAST getTree() {
return _tree;
}
/**
* returns the <code>SymbolTable</code> that has been constructed by
* this <code>TableMaker</code>
*
* @return <code>SymbolTable</code>
* @throws <code>SymbolTableException</code>
*/
public SymbolTable getTable() throws SymbolTableException {
if (symbolTable == null) {
symbolTable = new SymbolTable( _tree );
createDefinitions();
resolveReferences();
}
return symbolTable;
}
/**
* walks the tree and finishes creating definitions.
*
* @return <code>void</code>
* @throws <code>SymbolTableException</code>
* @see #walkTree()
* @see #finishCreatingDefinitions()
*/
private void createDefinitions() throws SymbolTableException {
walkTree();
finishCreatingDefinitions();
}
/**
* finishes up creating definitions process
* starts at the base of the Table
*
* @return <code>void</code>
* @throws <code>SymbolTableException</code>
* @see net.sourceforge.transmogrify.symtab.SymbolTable
* @see #finishCreatingDefinition(Definition)
*/
private void finishCreatingDefinitions() throws SymbolTableException {
finishCreatingDefinition(symbolTable.getBaseScope());
}
/**
* begins at the base of the Table and starts finishing definition creation.
*
* @param def <code>Definition</code> needs to be completed
* @return <code>void</code>
* @throws <code>SymbolTableException</code>
* @see ClassFinisher
* @see VariableFinisher
* @see MethodFinisher
* @see CatchFinisher
*/
private void finishCreatingDefinition(Definition def) throws SymbolTableException {
if (def instanceof AnonymousInnerClass) {
AnonymousInnerClass innerClass = (AnonymousInnerClass)def;
innerClass.finishMakingDefinition();
}
else if (def instanceof ClassDef) {
new ClassFinisher(def).finish();
}
else if ( def instanceof VariableDef ) {
new VariableFinisher( def ).finish();
}
else if (def instanceof DefaultConstructor) {}
else if ( def instanceof MethodDef ) {
new MethodFinisher( def ).finish();
}
else if (def instanceof BlockDef) {
SymTabAST firstChild = (SymTabAST)def.getTreeNode().getFirstChild();
//handle Checkstyle grammar
if (firstChild.getType() == TokenTypes.LPAREN) {
firstChild = (SymTabAST) firstChild.getNextSibling();
}
if (firstChild.getType() == TokenTypes.PARAMETER_DEF) {
// this is a catch block
new CatchFinisher((BlockDef)def).finish();
}
}
if ( def instanceof Scope ) {
finishCreatingChildren((Scope)def);
}
}
/**
* iterates through all the definitions in the <code> Scope </code>
* finishes creating each <code>Scope</code>.
*
* @param scope <code> Scope </code> where defininitions will be finished.
* @return <code>void</code>
* @throws <code>SymbolTableException</code>
* @see net.sourceforge.transmogrify.symtab.Scope
* @see #finishCreatingDefinition(Definition)
*/
private void finishCreatingChildren( Scope scope ) throws SymbolTableException {
Enumeration definitions = scope.getDefinitions();
while ( definitions.hasMoreElements() ) {
Definition child = (Definition)(definitions.nextElement());
finishCreatingDefinition(child);
}
}
/**
* resolves <code>SymbolTable</code> using <code>Resolver</code>
*
* @return <code>void</code>
* @see net.sourceforge.transmogrify.symtab.Resolver
* @see net.sourceforge.transmogrify.symtab.SymbolTable
*/
private void resolveReferences() {
new Resolver( symbolTable ).resolve();
}
/**
* starts walking the <code> SymTabAST </code>
*
* @return <code>void</code>
* @see #walkSiblings(SymTabAST,boolean)
* @see net.sourceforge.transmogrify.symtab.antlr.SymTabAST
*/
public void walkTree() {
walkSiblings((SymTabAST)_tree.getFirstChild(), false);
}
/**
* processes a single SymTabAST node based on its type and passes of its
* children for further processing where appropriate.
*
* @param tree the <code>SymTabAST</code> node to process
* @param makeAnonymousScopes sets to <code>false</code> after walking Class,
* Methods and Try otherwise sets to <code>true</code>
* @return <code>void</code>
* @see #walkSiblings(SymTabAST, boolean)
* @see #processAnonymousInnerClass(SymTabAST, SymTabAST)
* @see #processBlock(SymTabAST, boolean)
* @see #processClass(SymTabAST)
* @see #processConstructorDef(SymTabAST)
* @see #processElse(SymTabAST)
* @see #processFile(SymTabAST)
* @see #processFinally(SymTabAST)
* @see #processFor(SymTabAST)
* @see #processImplicitPackage(SymTabAST)
* @see #processImport(SymTabAST)
* @see #processLabel(SymTabAST)
* @see #processMethodDef(SymTabAST)
* @see #processPackage(SymTabAST)
* @see #processTry(SymTabAST)
* @see net.sourceforge.transmogrify.symtab.antlr.SymTabAST
* @see net.sourceforge.transmogrify.symtab.antlr.JavaTokenTypes
*/
public void walkTree(SymTabAST tree, boolean makeAnonymousScopes) {
if (tree != null) {
tree.setScope( symbolTable.getCurrentScope() );
switch(tree.getType()) {
case 0:
processFile(tree);
if ( tree.getFirstChild().getType() != TokenTypes.PACKAGE_DEF ) {
processImplicitPackage( tree.getFile() );
}
walkSiblings((SymTabAST)tree.getFirstChild(), false);
// pop package scope
symbolTable.popScope();
clearImports();
break;
case TokenTypes.LITERAL_NEW:
SymTabAST symtabTree = tree;
//walk parameters, in case of anonymous inner class
walkTree(symtabTree.findFirstToken(TokenTypes.ELIST),
makeAnonymousScopes);
SymTabAST objblock
= symtabTree.findFirstToken(TokenTypes.OBJBLOCK);
if (objblock != null) {
SymTabAST classExtended
= symtabTree.findFirstToken(TokenTypes.IDENT);
processAnonymousInnerClass(objblock, classExtended);
}
break;
case TokenTypes.SLIST:
if (makeAnonymousScopes) {
processBlock(tree, true);
}
else {
walkSiblings((SymTabAST)tree.getFirstChild(), true);
}
break;
case TokenTypes.CTOR_DEF:
processConstructorDef(tree);
break;
case TokenTypes.METHOD_DEF:
processMethodDef(tree);
break;
case TokenTypes.LITERAL_FINALLY:
processFinally(tree);
break;
case TokenTypes.LITERAL_TRY:
processTry(tree);
break;
case TokenTypes.VARIABLE_DEF:
processVariableDef(tree);
break;
case TokenTypes.PACKAGE_DEF:
processPackage(tree);
break;
case TokenTypes.LABELED_STAT:
processLabel(tree);
break;
case TokenTypes.IMPORT:
processImport(tree);
break;
case TokenTypes.CLASS_DEF:
case TokenTypes.INTERFACE_DEF:
processClass(tree);
break;
case TokenTypes.LITERAL_FOR:
processFor(tree);
break;
case TokenTypes.LITERAL_IF:
processIf(tree);
break;
case TokenTypes.LITERAL_ASSERT:
processAssert(tree);
break;
case TokenTypes.LITERAL_CATCH:
case TokenTypes.LITERAL_WHILE:
case TokenTypes.LITERAL_SWITCH:
case TokenTypes.LITERAL_DO:
case TokenTypes.LITERAL_SYNCHRONIZED:
case TokenTypes.STATIC_INIT:
case TokenTypes.INSTANCE_INIT:
processBlock(tree, false);
break;
default:
walkSiblings((SymTabAST)tree.getFirstChild(), false);
}
}
}
/**
* @param tree
*/
public void processAssert(SymTabAST tree) {
BlockDef block = makeBlock(tree);
SymTabAST expr = tree.findFirstToken(TokenTypes.EXPR);
SymTabAST message = (SymTabAST)expr.getNextSibling();
while ((message != null) && (message.getType() != TokenTypes.EXPR)) {
message = (SymTabAST) message.getNextSibling();
}
symbolTable.pushScope( block );
walkTree(expr, false);
if (message != null) {
walkTree(message, false);
}
symbolTable.popScope();
}
/**
* processes the given <code>SymTabAST</code> and each of its siblings
*
* @param tree <code>SymTabAST</code> node to process
* @param makeAnonymousScopes
*
* @return <code>void</code>
* @see #walkTree(SymTabAST, boolean)
* @see net.sourceforge.transmogrify.symtab.antlr.SymTabAST
*/
public void walkSiblings(SymTabAST tree, boolean makeAnonymousScopes) {
while(tree != null) {
walkTree(tree, makeAnonymousScopes);
tree = (SymTabAST)tree.getNextSibling();
}
}
/**
* processes the given <code>SymTabAST</code> as a package defintion
*
* @param tree the <code>SymTabAST</code> to process
* @return <code>void</code>
* @see net.sourceforge.transmogrify.symtab.antlr.SymTabAST
* @see net.sourceforge.transmogrify.symtab.PackageDef
* @see net.sourceforge.transmogrify.symtab.SymbolTable
*/
public void processPackage(SymTabAST tree) {
SymTabAST firstChild = (SymTabAST)tree.getFirstChild();
String name = ASTUtil.constructDottedName(firstChild);
firstChild.ignoreChildren();
PackageDef pkg = symbolTable.getPackage(name);
if (pkg == null) {
pkg = createPackage( (SymTabAST)(tree.getFirstChild()) );
}
symbolTable.pushScope(pkg);
}
/**
* processes a java class that use default no package
*
* @param file <code>File</code> object of the java class
* @return <code>void</code>
* @see net.sourceforge.transmogrify.symtab.PackageDef
* @see net.sourceforge.transmogrify.symtab.SymbolTable
*/
public void processImplicitPackage( File file ) {
String name = file.getParent();
if (name == null) {
name = "";
}
PackageDef pkg = symbolTable.getPackage( name );
if ( pkg == null ) {
pkg = new PackageDef( name, symbolTable.getBaseScope(), null );
symbolTable.definePackage( pkg, symbolTable.getBaseScope() );
}
symbolTable.pushScope( pkg );
}
/**
* gets the package represented by the tree. The method
* analyzes the tree, constructs an appropriate package name
* and fetches it from the internal package list. If the package does not
* exist it is created.
*
* @param tree <code>SymTabAST</code> to consider
*
* @return <code>PackageDef</code> the resulting package definition
* @see #getPackage(Scope, SymTabAST)
*/
private PackageDef createPackage( SymTabAST tree ) {
PackageDef result = null;
if (tree.getType() == TokenTypes.DOT) {
// find the package result of left child
SymTabAST leftChild = (SymTabAST)tree.getFirstChild();
SymTabAST rightChild = (SymTabAST)leftChild.getNextSibling();
PackageDef context = createPackage(leftChild);
result = getPackage( context, rightChild );
}
else {
result = getPackage(symbolTable.getBaseScope(), tree);
}
return result;
}
/**
* gets the package determined by the tree and parent package def.
* The method analyzes the tree and parent scope and retrieves the
* appropriate package definition from the internal package list.
* If the package does not exist it is created.
*
* @param tree <code>SymTabAST</code> to consider
* @param parent the parent package definition
*
* @return PackageDef the resulting package definition
* @see net.sourceforge.transmogrify.symtab.PackageDef
* @see net.sourceforge.transmogrify.symtab.SymbolTable
* @see net.sourceforge.transmogrify.symtab.antlr.SymTabAST
*/
private PackageDef getPackage(Scope parent, SymTabAST tree ) {
String name = tree.getText();
PackageDef result = null;
if (!(parent instanceof BaseScope)) {
result = symbolTable.getPackage(parent.getQualifiedName() + "." + name);
}
else {
result = symbolTable.getPackage(name);
}
if (result == null) {
result = new PackageDef(tree.getText(), parent, tree);
symbolTable.definePackage(result, parent);
}
return result;
}
/**
* process the given <code>SymTabAST</code> as a file definition
*
* @param tree the <code>SymTabAST</code> to process
* @return <code>void</code>
* @see #setCurrentFile(String)
*/
public void processFile(SymTabAST tree) {
setCurrentFile(tree.getText());
}
/**
* adds the given <code>SymTabAST</code> to <code>imports</code> member
*
* @param tree the <code>SymTabAST</code> to process
* @return <code>void</code>
*/
public void processImport(SymTabAST tree) {
imports.add( tree );
}
/**
* clears the <code>imports</code> member
* @return <code>void</code>
*/
private void clearImports() {
imports.clear();
}
/**
* process the given SymTabAST as a label definition
*
* @param tree the SymTabAST to process
* @return <code>void</code>
* @see com.trwx.symtab.antlr.SymTabAST
* @see net.sourceforge.transmogrify.symtab.LabelDef
* @see #walkTree(SymTabAST, boolean)
*/
public void processLabel(SymTabAST tree) {
String name = tree.findFirstToken(TokenTypes.IDENT).getText();
LabelDef label = new LabelDef( name, symbolTable.getCurrentScope(),
tree );
symbolTable.defineLabel( label );
walkTree((SymTabAST)tree.getFirstChild().getNextSibling(), false);
}
/**
* process the given <code>SymTabAST</code> as a class definition
*
* @param tree the <code>SymTabAST</code> to process
* @return <code>void</code>
* @see #makeClass(String, SymTabAST)
* @see #walkSiblings(SymTabAST, boolean)
* @see net.sourceforge.transmogrify.symtab.antlr.SymTabAST
*/
public void processClass(SymTabAST tree) {
String name = tree.findFirstToken(TokenTypes.IDENT).getText();
makeClass(name, tree);
final SymTabAST objblock =
tree.findFirstToken(TokenTypes.OBJBLOCK);
SymTabAST start = (SymTabAST)objblock.getFirstChild();
if (start != null) {
//skip LPAREN
if (start.getType() == TokenTypes.LPAREN) {
start = (SymTabAST)start.getNextSibling();
}
walkSiblings(start, false);
}
symbolTable.popScope();
}
/**
* creates <code>ClassDef</code> for the current class node and add it to the
* symbol table
* @param name name of the class
* @param tree current node to be processed
* @return <code>void</code>
* @see net.sourceforge.transmogrify.symtab.ClassDef
* @see net.sourceforge.transmogrify.symtab.SymbolTable
*/
public void makeClass(String name, SymTabAST tree) {
ClassDef def = new ClassDef(name, symbolTable.getCurrentScope(), tree);
def.addUnprocessedImports(imports);
symbolTable.defineClass(def);
symbolTable.pushScope(def);
}
/**
* processes anonymous inner class encountered in the tree
* @param objblock the anonymous block
* @param classExtended
* @return <code>void</code>
* @see net.sourceforge.transmogrify.symtab.AnonymousInnerClass
* @see net.sourceforge.transmogrify.symtab.SymbolTable
*/
public void processAnonymousInnerClass(SymTabAST objblock,
SymTabAST classExtended) {
ClassDef def = new AnonymousInnerClass(objblock,
classExtended,
symbolTable.getCurrentScope());
symbolTable.defineClass(def);
symbolTable.pushScope(def);
walkSiblings((SymTabAST)objblock.getFirstChild(), false);
symbolTable.popScope();
}
/**
* process the given SymTabAST as a variable definition
*
* @param tree the SymTabAST to process
* @return <code>void</code>
* @see net.sourceforge.transmogrify.symtab.VariableDef
* @see net.sourceforge.transmogrify.symtab.SymbolTable
* @see net.sourceforge.transmogrify.symtab.antlr.SymTabAST
* @see #makeVariableDef(SymTabAST, Scope)
* @see #walkTree(SymTabAST, boolean)
*/
private void processVariableDef(SymTabAST tree) {
VariableDef def = makeVariableDef( tree, symbolTable.getCurrentScope() );
symbolTable.defineVariable(def);
SymTabAST assignmentNode
= tree.findFirstToken(TokenTypes.ASSIGN);
if (assignmentNode != null) {
walkTree((SymTabAST)assignmentNode.getFirstChild(), false);
}
}
/**
* creates <code>VariableDef</code> based on the current tree node and scope
* @param tree the current tree node
* @param scope the current scope
* @return <code>VariableDef</code>
* @see net.sourceforge.transmogrify.symtab.VariableDef
*/
public VariableDef makeVariableDef(SymTabAST tree, Scope scope) {
String name = tree.findFirstToken(TokenTypes.IDENT).getText();
VariableDef result = new VariableDef(name, scope, tree);
return result;
}
/**
* process the given SymTabAST as a try block
*
* @param tree the SymTabAST to process
* @return <code>void</code>
* @see #makeBlock(SymTabAST)
* @see net.sourceforge.transmogrify.symtab.antlr.SymTabAST
* @see #walkTree(SymTabAST, boolean)
* @see #walkSiblings(SymTabAST, boolean)
*/
public void processTry(SymTabAST tree){
BlockDef block = makeBlock(tree);
SymTabAST slist = tree.findFirstToken(TokenTypes.SLIST);
SymTabAST everythingElse = (SymTabAST)slist.getNextSibling();
symbolTable.pushScope( block );
walkTree( slist, false );
symbolTable.popScope();
walkSiblings( everythingElse, false );
}
/**
* process the given SymTabAST as a finally block
*
* @param tree the SymTabAST to process
* @return <code>void</code>
* @see #makeBlock(SymTabAST)
* @see #walkTree(SymTabAST, boolean)
*/
public void processFinally(SymTabAST tree){
BlockDef block = makeBlock(tree);
SymTabAST slist = tree.findFirstToken(TokenTypes.SLIST);
SymTabAST tryBlock = tree.findFirstToken(TokenTypes.LITERAL_TRY);
symbolTable.pushScope( block );
walkTree( slist, false );
symbolTable.popScope();
walkTree( tryBlock, false );
}
/**
* process the given SymTabAST as a method definition
*
* @param tree the SymTabAST to process
* @return <code>void</code>
* @see net.sourceforge.transmogrify.symtab.MethodDef
* @see net.sourceforge.transmogrify.symtab.SymbolTable
* @see #walkTree(SymTabAST, boolean)
*/
public void processMethodDef(SymTabAST tree) {
String name = tree.findFirstToken(TokenTypes.IDENT).getText();
MethodDef method = new MethodDef(name, symbolTable.getCurrentScope(),
tree);
symbolTable.defineMethod( method );
symbolTable.pushScope( method );
walkTree(tree.findFirstToken(TokenTypes.SLIST), false);
symbolTable.popScope();
}
/**
* process the given SymTabAST as a constructor definition
*
* @param tree the SymTabAST to process
* @return <code>void</code>
* @see net.sourceforge.transmogrify.symtab.MethodDef
* @see net.sourceforge.transmogrify.symtab.SymbolTable
* @see #walkTree(SymTabAST, boolean)
*/
public void processConstructorDef(SymTabAST tree) {
processMethodDef(tree);
}
/**
* process the given SymTabAST as a for block
*
* @param tree the SymTabAST to process
* @return <code>void</code>
* @see #makeBlock(SymTabAST)
* @see #walkTree(SymTabAST, boolean)
*/
public void processFor(SymTabAST tree) {
BlockDef block = makeBlock(tree);
symbolTable.pushScope( block );
SymTabAST body;
SymTabAST forEach = tree.findFirstToken(TokenTypes.FOR_EACH_CLAUSE);
if (forEach != null) {
walkTree(forEach, false);
body = (SymTabAST)forEach.getNextSibling();
}
else {
walkTree(tree.findFirstToken(TokenTypes.FOR_INIT), false);
walkTree(tree.findFirstToken(TokenTypes.FOR_CONDITION), false);
SymTabAST forIter = tree.findFirstToken(TokenTypes.FOR_ITERATOR);
walkTree(forIter, false);
body = (SymTabAST)forIter.getNextSibling();
}
//handle Checkstyle grammar
if (body.getType() == TokenTypes.RPAREN) {
body = (SymTabAST) body.getNextSibling();
}
walkTree(body, false);
symbolTable.popScope();
}
/**
* process the given SymTabAST as an if block
*
* @param tree the SymTabAST to process
* @return <code>void</code>
* @see #makeBlock(SymTabAST)
* @see #walkTree(SymTabAST, boolean)
* @see #processElse(SymTabAST)
*/
public void processIf(SymTabAST tree) {
BlockDef block = makeBlock(tree);
SymTabAST expr = tree.findFirstToken(TokenTypes.EXPR);
SymTabAST ifBranch = (SymTabAST)expr.getNextSibling();
// handle Checkstyle grammar
if (ifBranch.getType() == TokenTypes.RPAREN) {
ifBranch = (SymTabAST) ifBranch.getNextSibling();
}
SymTabAST elseBranch = (SymTabAST)ifBranch.getNextSibling();
// handle Checkstyle grammar
if ((elseBranch != null) && (elseBranch.getType() == TokenTypes.SEMI)) {
elseBranch = (SymTabAST) elseBranch.getNextSibling();
}
if ((elseBranch != null) && (elseBranch.getType() == TokenTypes.LITERAL_ELSE)) {
elseBranch = (SymTabAST) elseBranch.getFirstChild();
}
symbolTable.pushScope( block );
walkTree(expr, false);
walkTree(ifBranch, false);
symbolTable.popScope();
processElse(elseBranch);
}
/**
* process the given SymTabAST as an else block
*
* @param tree the SymTabAST to process
* @return <code>void</code>
* @see #processIf(SymTabAST)
* @see #makeElseBlock(SymTabAST)
*/
public void processElse(SymTabAST tree) {
if (tree != null) {
if (tree.getType() == TokenTypes.LITERAL_IF) {
processIf(tree);
}
else {
makeElseBlock(tree);
}
}
}
/**
* defines an anonymous block to enclose the scope of an else block
*
* @param tree the SymTabAST to process
* @return <code>void</code>
* @see #makeBlock(SymTabAST)
* @see #walkTree(SymTabAST, boolean)
*/
public void makeElseBlock(SymTabAST tree) {
if (tree.getType() == TokenTypes.SLIST) {
BlockDef block = makeBlock(tree);
symbolTable.pushScope( block );
walkTree(tree, false);
symbolTable.popScope();
}
else {
walkTree(tree, false);
}
}
/**
* processes the current tree node as BlockDef
* @param tree current tree node
* @param makeAnonymousScopes
* @return <code>void</code>
*/
public void processBlock(SymTabAST tree, boolean makeAnonymousScopes) {
BlockDef block = makeBlock(tree);
symbolTable.pushScope(block);
//handle Checkstyle grammar
SymTabAST child = (SymTabAST)tree.getFirstChild();
if ((child != null) && (child.getType() == TokenTypes.LPAREN)) {
child = (SymTabAST) child.getNextSibling();
}
walkSiblings(child, makeAnonymousScopes);
symbolTable.popScope();
}
/**
* set the current file to the named file
*
* @param fileName the name of the file
* @return <code>void</code>
*/
public void setCurrentFile(String fileName) {
currentFile = new File(fileName);
symbolTable.setCurrentFile(currentFile);
}
/**
* creates a new <code> BlockDef </code> in the SymbolTable
*
* @param tree is a <code> SymTabAST </code> whose root begins the new block
* @return <code> BlockDef </code>
* @see net.sourceforge.transmogrify.symtab.BlockDef
* @see net.sourceforge.transmogrify.symtab.SymbolTable
*/
private BlockDef makeBlock( SymTabAST tree ) {
BlockDef block = new BlockDef( symbolTable.getCurrentScope(), tree );
symbolTable.defineBlock( block );
return block;
}
/**
* returns the <code>SymTabAST</code> that contains the parameter classDef's
* extends nodes
*
* @param classDef is a <code> ClassDef </code> whose extends nodes we want
* @return <code> SymTabAST </code>
*/
public static SymTabAST getExtendsNode(ClassDef classDef) {
SymTabAST result = null;
SymTabAST extendsClause = null;
SymTabAST classDefTreeNode = classDef.getTreeNode();
extendsClause =
classDefTreeNode.findFirstToken(TokenTypes.EXTENDS_CLAUSE);
if (extendsClause != null) {
result = (SymTabAST)(extendsClause.getFirstChild());
}
return result;
}
/**
* Superclass for different kind of XXXFinisher subclass
* @see CatchFinisher
* @see ClassFinisher
* @see DefinitionFinisher
* @see MethodFinisher
* @see VariableFinisher
*/
class DefinitionFinisher {
protected SymTabAST _node = null;
/**
* Constructor. It finishes the definition passed to it
*
*/
public DefinitionFinisher( Definition def ) {
_node = def.getTreeNode();
}
public void finish() throws SymbolTableException {}
/**
* gets the classDef that represents the type of the given definition
*
*
* @param def the definition whose type to find
* @param typeNode the TokenTypes.TYPE node associated with the def
*
* @return the resulting class definition
*/
protected IClass getType( Definition def, SymTabAST typeNode ) {
IClass result = null;
SymTabAST typeClassNode = null;
boolean isArray = false;
if ( typeNode.getFirstChild().getType()
== TokenTypes.ARRAY_DECLARATOR ) {
isArray = true;
typeClassNode = (SymTabAST)typeNode.getFirstChild().getFirstChild();
}
else {
typeClassNode = (SymTabAST)typeNode.getFirstChild();
}
Scope lookupScope = null;
if (def instanceof Scope) {
lookupScope = (Scope)def;
}
else {
lookupScope = def.getParentScope();
}
Resolver resolver = new Resolver(symbolTable);
IClass typeClass = resolver.resolveClass(typeClassNode, lookupScope, null, false);
if ( isArray ) {
result = new ArrayDef( typeClass );
}
else {
result = typeClass;
}
return result;
}
}
class ClassFinisher extends DefinitionFinisher {
private ClassDef _def = null;
/**
* Constructor. Creates a ClassFinisher from a <code> Definition </code>
*
* @param def is a <code> Definition </code>
*/
public ClassFinisher( Definition def ) {
super( def );
_def = (ClassDef)def;
}
/**
* Completes all tasks required for finishing a ClassDef
* Including adding imports, setting super classes and adding
* interfaces.
* @return <code>void</code>
* @throws <code>SymbolTableException</code>
* @see #addImports()
* @see #setSuperclass()
* @see #addInterfaces()
*/
public void finish() throws SymbolTableException {
if ( _node != null ) {
addImports();
setSuperclass();
addInterfaces();
}
}
/**
* Adds all packages and classes that are imported by this class
* to the class for later reference
*/
private void addImports() throws ClassImportException {
IPackage java = new ExternalPackage("java", null);
IPackage lang = new ExternalPackage("lang", java);
java.addDefinition(lang);
_def.importPackage(lang);
Vector unprocessedImports = _def.getUnprocessedImports();
for ( int i = 0; i < unprocessedImports.size(); i++ ) {
SymTabAST importNode = (SymTabAST)unprocessedImports.get(i);
SymTabAST imported = (SymTabAST)importNode.getFirstChild();
SymTabAST lastPart = (SymTabAST)imported.getFirstChild().getNextSibling();
DotIterator it = new DotIterator(imported);
SymTabAST current = null;
String className = null;
IClass importedClass = null;
// attempt at each token to find the class
// first in source
// then on classpath
//
// if there are more tokens left
// continue until you hit the last token
// if it's a star
// import all inner classes
// else
// import the explicitly named inner class
// else import the class
//
// if no classes were found, import the package
while(it.hasNext()) {
current = it.nextNode();
if (className == null) {
className = current.getText();
}
else {
if (!current.getText().equals("*")) {
className += "." + current.getText();
}
else {
break;
}
}
importedClass = findOrLoadClass(className, importedClass);
if (importedClass != null) {
break;
}
}
if (it.hasNext()) {
boolean isImported = false;
while(it.hasNext()) {
current = it.nextNode();
if (current.getText().equals("*")) {
importInnerClasses(importedClass);
isImported = true;
}
else {
className += "$" + current.getText();
importedClass = findOrLoadClass(className, importedClass);
}
}
if (!isImported) {
_def.importClass(importedClass);
}
}
else {
if (importedClass != null) {
_def.importClass(importedClass);
}
else {
if (current != null && current.getText().equals("*")) {
IPackage pkg = symbolTable.getPackage(className);
if (pkg == null) {
pkg = getPackage(className);
}
_def.importPackage(pkg);
}
else {
//TODO: can we safely ignore this?
//throw new ClassImportException(className);
;
}
}
}
// now set definitions where appropriate
imported.ignoreChildren();
if ((lastPart.getType() == TokenTypes.IDENT)
//TODO: guard added for class not loaded
//This is OK for single file processing, but not
//multiple files.
&& (importedClass != null)
)
{
lastPart.setDefinition(importedClass, null, true);
lastPart.setMeaningfulness(true);
}
}
}
/**
* creates <code>ExternalPackage</code>
* @param packageName name of the package
* @return <code>ExternalPackage</code>
* @see net.sourceforge.transmogrify.symtab.ExternalPackage
*/
private ExternalPackage getPackage(String packageName) {
return new ExternalPackage(packageName, null);
}
/**
* stores the inner classes in the approriate ClassDef
* @param outerClass
* @return <code>void</code>
*/
private void importInnerClasses(IClass outerClass) {
IClass[] innerClasses = outerClass.getInnerClasses();
for (int i = 0; i < innerClasses.length; i++) {
_def.importClass(innerClasses[i]);
}
}
/**
* creates <code>ExternalClass</code> based on a java class
* @param className class to be loaded
* @return <code>IClass</code>
* @see net.sourceforge.transmogrify.symtab.ExternalClass
*/
private IClass loadClass(String className) {
IClass result = null;
try {
Class javaClass
= ClassManager.getClassLoader().loadClass(className);
result = new ExternalClass(javaClass);
}
catch (ClassNotFoundException ignoreMe) {}
return result;
}
/**
* find a class from <code>BaseCode</code> or its parent
* @param className name of the class to be load or found
* @param parentClass its parent class
* @return <code>IClass</code>
* @see net.sourceforge.transmogrify.symtab.SymbolTable
* @see net.sourceforge.transmogrify.symtab.IClass
* @see #loadClass(String)
*/
private IClass findOrLoadClass(String className, IClass parentClass) {
IClass result = null;
if (parentClass == null) {
result = symbolTable.getBaseScope().getClassDefinition(className);
}
else {
int index = className.lastIndexOf("$");
if (index < 0) {
index = className.lastIndexOf(".");
}
result = parentClass.getClassDefinition(className.substring(index + 1));
}
if (result == null) {
result = loadClass(className);
}
return result;
}
/**
*
* If the class has a super class a reference to this super class
* is added to this class.
* @return <code>void</code>
* @see net.sourceforge.transmogrify.symtab.ClassDef
*/
private void setSuperclass() {
if (_def.getTreeNode().getType() == TokenTypes.CLASS_DEF) {
SymTabAST extendsNode = getExtendsNode(_def);
if ( extendsNode != null ) {
String superclassName = ASTUtil.constructDottedName(extendsNode);
IClass superclass = _def.getClassDefinition(superclassName);
if ( superclass != null ) {
_def.setSuperclass( superclass );
superclass.addSubclass( _def );
}
}
else {
_def.setSuperclass(new ExternalClass(Object.class));
}
}
else {
_def.setSuperclass(new ExternalClass(Object.class));
}
}
/**
*
* If the class implements an interface a reference to this interface
* is added to this class.
* @return <code>void</code>
* @see net.sourceforge.transmogrify.symtab.ClassDef
*/
private void addInterfaces() {
SymTabAST implementsClause = null;
if (_def.getTreeNode().getType() == TokenTypes.CLASS_DEF) {
implementsClause
= _node.findFirstToken(TokenTypes.IMPLEMENTS_CLAUSE);
}
else {
implementsClause
= _node.findFirstToken(TokenTypes.EXTENDS_CLAUSE);
}
if ( implementsClause != null ) {
SymTabAST interfaceNode = (SymTabAST)implementsClause.getFirstChild();
while ( interfaceNode != null ) {
IClass implemented =
_def.getClassDefinition(interfaceNode.getText());
if ( implemented != null ) {
_def.addInterface( implemented );
implemented.addImplementor( _def );
}
interfaceNode = (SymTabAST)(interfaceNode.getNextSibling());
}
}
}
}
/**
*
* Completes a Variable by setting all required references
*
*/
class VariableFinisher extends DefinitionFinisher {
VariableDef _def = null;
/**
*
* Constructor. Creates a VariableFinishes from the <code>Definition</code>
* @param def VariableDef to be finished
* @see net.sourceforge.transmogrify.symtab.VariableDef
*/
public VariableFinisher( Definition def ) {
super( def );
_def = (VariableDef)def;
}
/**
*
* Finishes the variable by setting the Type
* @return <code>void</code>
* @see #getType(Definition, SymTabAST)
* @see net.sourceforge.transmogrify.symtab.VariableDef
*/
public void finish() {
SymTabAST typeNode = _node.findFirstToken(TokenTypes.TYPE);
SymTabAST typeTextNode = (SymTabAST)(typeNode.getFirstChild());
if ( typeTextNode.getType() == TokenTypes.ARRAY_DECLARATOR ) {
typeTextNode = (SymTabAST)(typeTextNode.getFirstChild());
}
typeTextNode.setLine(ASTUtil.getLine( _def.getTreeNode() ));
IClass varType = getType(_def, typeNode);
_def.setType( varType );
}
}
/**
*
* Completes a Variable by setting all required references
*
*/
class MethodFinisher extends DefinitionFinisher {
MethodDef _def = null;
/**
*
* Creates a MethodFinisher from a <code> Definition <code>
* @param def MethodDef to be finished
* @see net.sourceforge.transmogrify.symtab.MethodDef
*/
public MethodFinisher( Definition def ) {
super( def );
_def = (MethodDef)def;
}
/**
*
* Completes a method by setting all references to return types,
* signatures and exceptions.
* @return <code>void</code>
* @see #setReturnType()
* @see #setSignature()
* @see #setExceptionsThrown()
*/
public void finish() {
setReturnType();
setSignature();
setExceptionsThrown();
}
/**
*
* setReturnType adds a reference to the methods return type
* to the method definition
* @return <code>void</code>
* @see net.sourceforge.transmogrify.symtab.MethodDef
* @see #getType(Definition, SymTabAST)
* @see #getTypeNode()
*/
private void setReturnType() {
IClass type = null;
if ( isConstructor() ) {
type = _def.getEnclosingClass();
}
else {
type = getType(_def, getTypeNode());
}
_def.setType(type);
}
/**
*
* setSignature adds a reference to the methods paramaters
* to the method definition
* @return <code>void</code>
* @see #makeVariableDef(SymTabAST, Definition)
* @see VariableFinisher
* @see net.sourceforge.transmogrify.symtab.MethodDef
*/
private void setSignature() {
SymTabAST parametersNode
= _node.findFirstToken(TokenTypes.PARAMETERS);
SymTabAST parameterNode = (SymTabAST)(parametersNode.getFirstChild());
while ( parameterNode != null ) {
if (parameterNode.getType() == TokenTypes.PARAMETER_DEF) {
VariableDef parameter = makeVariableDef( parameterNode, _def );
new VariableFinisher( parameter ).finish();
_def.addParameter( parameter );
}
parameterNode = (SymTabAST)(parameterNode.getNextSibling());
}
}
/**
*
* setExceptionsThrown adds a reference to the methods Exceptions
* to the method definition
* @return <code>void</code>
* @see net.sourceforge.transmogrify.symtab.SymbolTable
* @see net.sourceforge.transmogrify.symtab.PackageDef
* @see net.sourceforge.transmogrify.symtab.MethodDef
*/
private void setExceptionsThrown() {
IClass exception = null;
SymTabAST throwsNode
= _node.findFirstToken(TokenTypes.LITERAL_THROWS);
if ( throwsNode != null ) {
SymTabAST exceptionNode = (SymTabAST)(throwsNode.getFirstChild());
while (exceptionNode != null ) {
if (exceptionNode.getType() == TokenTypes.DOT) {
PackageDef pkg = symbolTable.getPackage(ASTUtil.constructPackage(exceptionNode));
if ( pkg != null ) {
exception = pkg.getClassDefinition(ASTUtil.constructClass(exceptionNode));
}
}
else {
exception = _def.getClassDefinition(exceptionNode.getText());
}
_def.addException(exception);
exceptionNode = (SymTabAST)(exceptionNode.getNextSibling());
}
}
}
/**
*
* isConstructor sets the flag in the method definition to indicate
* whether it is a constructor or not
* @return <code>boolean</code> <code>true</code> if a node has no return type
* <code>false</code> if a node has a return type
* @see #getTypeNode()
*/
private boolean isConstructor() {
boolean result = false;
if ( getTypeNode() == null ) {
result = true;
}
return result;
}
/**
*
* getTypeNode returnss the <code> SymTabAST</code> node that is the
* return type of this method
*
* @return <code>SymTabAST</code>
*/
private SymTabAST getTypeNode() {
return _node.findFirstToken(TokenTypes.TYPE);
}
}
class CatchFinisher extends DefinitionFinisher {
BlockDef _def = null;
/**
* constructor that takes a <code>BlockDef</code>
* @param def <code>BlockDef</code> for the catch block
*/
public CatchFinisher(BlockDef def) {
super(def);
_def = def;
}
/**
* finishes definition creation
* @return <code>void</code>
* @see #createExceptionVariable()
*/
public void finish() {
createExceptionVariable();
}
/**
* creates <code>VariableDef<code> and finishes creation definition for
* arguments in the catch clause
* @return <code>void</code>
* @see net.sourceforge.transmogrify.symtab.VariableDef
* @see #makeVariableDef(SymTabAST, Definition)
* @see VariableFinisher
*/
private void createExceptionVariable() {
SymTabAST exceptionNode
= _def.getTreeNode().findFirstToken(TokenTypes.PARAMETER_DEF);
VariableDef exception = makeVariableDef(exceptionNode, _def);
new VariableFinisher(exception).finish();
_def.addDefinition(exception);
}
}
}