////////////////////////////////////////////////////////////////////////////////
// checkstyle: Checks Java source code for adherence to a set of rules.
// Copyright (C) 2001-2003 Oliver Burn
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
////////////////////////////////////////////////////////////////////////////////
package com.puppycrawl.tools.checkstyle.checks.usage.transmogrify;
import java.io.File;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import antlr.collections.AST;
import com.puppycrawl.tools.checkstyle.checks.usage.AbstractUsageCheck;
/**
* Manages AST trees and nodes. Capable of managing multiple parse trees, which
* is useful for inter-file checks.
* @author Rick Giles
*/
public final class ASTManager
{
/** singleton */
private static final ASTManager INSTANCE = new ASTManager();
/**
* Maps DetailASTs to SymTabASTs.
* A HashMap is acceptable provided DetailAST method hashCode() returns
* distinct integers for distinct objects.
* If not, a structure such as IdentityHashMap must be employed.
*/
private Map mMap = new HashMap();
/** root with subtrees for a set of files */
private SymTabAST mCompleteTree = null;
/** Map for parse trees, keyed on File name */
private Map mTrees = new HashMap();
/** Set of checks and their nodes to check */
private Map mCheckNodes = new HashMap();
/** prevent client creation */
private ASTManager()
{
}
/**
* Returns the singleon ASTManager.
* @return the singleon ASTManager.
*/
public static ASTManager getInstance()
{
return INSTANCE;
}
/**
* Add the parse tree for a file to the set of parse trees.
* Postcondition: since all checks are local to one source file,
* all managed elements are cleared.
* @param aFileName the name of the file.
* @param aRoot the root of the AST.
*/
public void addTree(String aFileName, AST aRoot)
{
clear();
mTrees.put(aFileName, aRoot);
}
/**
* Clears all managed elements.
*/
private void clear() {
mCheckNodes.clear();
mTrees.clear();
mMap.clear();
mCompleteTree = null;
}
/**
* Builds the complete tree for all added parse trees.
* @throws SymbolTableException if there is an error.
*/
private void buildTree()
throws SymbolTableException
{
mCompleteTree = SymTabASTFactory.create(0, "AST Root");
final Set keys = mTrees.keySet();
final Iterator it = keys.iterator();
while (it.hasNext()) {
final String fileName = (String) it.next();
final File file = new File(fileName);
final AST rootAST = (AST) mTrees.get(fileName);
addToCompleteTree(file, rootAST);
}
// Walk of the complete tree.
// TODO: This is a hack. Find a better way.
new TableMaker(mCompleteTree).getTable();
}
/**
* Adds a file and a DetailAST to the root SymTabAST tree. Normally, the
* DetailAST will be the parse tree for the file.
* @param aFile the file to add.
* @param aAST the DetailAST to add.
*/
private void addToCompleteTree(File aFile, AST aAST)
{
// add aFile to the root
final SymTabAST fileNode =
SymTabASTFactory.create(0, aFile.getAbsolutePath());
fileNode.setFile(aFile);
mCompleteTree.addChild(fileNode);
fileNode.setParent(mCompleteTree);
// add aAST to aFile
final SymTabAST child = SymTabASTFactory.create(aAST);
child.setFile(aFile);
fileNode.addChild(child);
child.setParent(fileNode);
fileNode.finishDefinition(aFile, mCompleteTree);
}
/**
* Registers a node for checking.
* @param aCheck the check to apply.
* @param aNode the node to check.
*/
public void registerCheckNode(AbstractUsageCheck aCheck, AST aNode)
{
Set nodeSet = (Set) mCheckNodes.get(aCheck);
if (nodeSet == null) {
nodeSet = new HashSet();
nodeSet.add(aNode);
mCheckNodes.put(aCheck, nodeSet);
}
else {
nodeSet.add(aNode);
}
}
/**
* Gets the nodes to check with a usage check.
* @param aCheck the usage check.
* @return the nodes to check with aCheck.
* @throws SymbolTableException if there is an error.
*/
public Set getCheckNodes(AbstractUsageCheck aCheck)
throws SymbolTableException
{
// lazy initialization
if (mCompleteTree == null) {
buildTree();
}
Set result = (Set) mCheckNodes.get(aCheck);
if (result == null) {
result = new HashSet();
}
return result;
}
/**
* Maps a AST to its associated SymTabAST.
* @param aAST the AST.
* @param aSymTabAST the SymTabAST associated with aAST.
*/
public void put(AST aAST, SymTabAST aSymTabAST)
{
mMap.put(aAST, aSymTabAST);
}
/**
* Gets the SymTabAST associated with a AST.
* @param aAST the AST.
* @return the the SymTabAST associated with aAST.
*/
public SymTabAST get(AST aAST)
{
return (SymTabAST) mMap.get(aAST);
}
/**
* Clears all associations from DetailsASTs to SymTabASTs.
*/
public void clearDetailsMap()
{
mMap.clear();
}
/**
* Determines whether the map from DetailsASTs to SymTabASTs is empty.
* @return true if the map is empty.
*/
public boolean isEmptyDetailsMap()
{
return mMap.isEmpty();
}
/**
* Removes a check and its check nodes. Clears all managed elements if
* last check removed.
* @param aCheck the check to remove.
*/
public void removeCheck(AbstractUsageCheck aCheck)
{
mCheckNodes.remove(aCheck);
if (mCheckNodes.isEmpty()) {
clear();
}
}
}