001: ////////////////////////////////////////////////////////////////////////////////
002: // checkstyle: Checks Java source code for adherence to a set of rules.
003: // Copyright (C) 2001-2003 Oliver Burn
004: //
005: // This library is free software; you can redistribute it and/or
006: // modify it under the terms of the GNU Lesser General Public
007: // License as published by the Free Software Foundation; either
008: // version 2.1 of the License, or (at your option) any later version.
009: //
010: // This library is distributed in the hope that it will be useful,
011: // but WITHOUT ANY WARRANTY; without even the implied warranty of
012: // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: // Lesser General Public License for more details.
014: //
015: // You should have received a copy of the GNU Lesser General Public
016: // License along with this library; if not, write to the Free Software
017: // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018: ////////////////////////////////////////////////////////////////////////////////
019: package com.puppycrawl.tools.checkstyle.checks.usage.transmogrify;
020:
021: import java.io.File;
022: import java.util.HashMap;
023: import java.util.HashSet;
024: import java.util.Iterator;
025: import java.util.Map;
026: import java.util.Set;
027:
028: import antlr.collections.AST;
029:
030: import com.puppycrawl.tools.checkstyle.checks.usage.AbstractUsageCheck;
031:
032: /**
033: * Manages AST trees and nodes. Capable of managing multiple parse trees, which
034: * is useful for inter-file checks.
035: * @author Rick Giles
036: */
037: public final class ASTManager {
038: /** singleton */
039: private static final ASTManager INSTANCE = new ASTManager();
040:
041: /**
042: * Maps DetailASTs to SymTabASTs.
043: * A HashMap is acceptable provided DetailAST method hashCode() returns
044: * distinct integers for distinct objects.
045: * If not, a structure such as IdentityHashMap must be employed.
046: */
047: private Map mMap = new HashMap();
048:
049: /** root with subtrees for a set of files */
050: private SymTabAST mCompleteTree = null;
051:
052: /** Map for parse trees, keyed on File name */
053: private Map mTrees = new HashMap();
054:
055: /** Set of checks and their nodes to check */
056: private Map mCheckNodes = new HashMap();
057:
058: /** prevent client creation */
059: private ASTManager() {
060: }
061:
062: /**
063: * Returns the singleon ASTManager.
064: * @return the singleon ASTManager.
065: */
066: public static ASTManager getInstance() {
067: return INSTANCE;
068: }
069:
070: /**
071: * Add the parse tree for a file to the set of parse trees.
072: * Postcondition: since all checks are local to one source file,
073: * all managed elements are cleared.
074: * @param aFileName the name of the file.
075: * @param aRoot the root of the AST.
076: */
077: public void addTree(String aFileName, AST aRoot) {
078: clear();
079: mTrees.put(aFileName, aRoot);
080: }
081:
082: /**
083: * Clears all managed elements.
084: */
085: private void clear() {
086: mCheckNodes.clear();
087: mTrees.clear();
088: mMap.clear();
089: mCompleteTree = null;
090: }
091:
092: /**
093: * Builds the complete tree for all added parse trees.
094: * @throws SymbolTableException if there is an error.
095: */
096: private void buildTree() throws SymbolTableException {
097: mCompleteTree = SymTabASTFactory.create(0, "AST Root");
098: final Set keys = mTrees.keySet();
099: final Iterator it = keys.iterator();
100: while (it.hasNext()) {
101: final String fileName = (String) it.next();
102: final File file = new File(fileName);
103: final AST rootAST = (AST) mTrees.get(fileName);
104: addToCompleteTree(file, rootAST);
105: }
106:
107: // Walk of the complete tree.
108: // TODO: This is a hack. Find a better way.
109: new TableMaker(mCompleteTree).getTable();
110: }
111:
112: /**
113: * Adds a file and a DetailAST to the root SymTabAST tree. Normally, the
114: * DetailAST will be the parse tree for the file.
115: * @param aFile the file to add.
116: * @param aAST the DetailAST to add.
117: */
118: private void addToCompleteTree(File aFile, AST aAST) {
119: // add aFile to the root
120: final SymTabAST fileNode = SymTabASTFactory.create(0, aFile
121: .getAbsolutePath());
122: fileNode.setFile(aFile);
123: mCompleteTree.addChild(fileNode);
124: fileNode.setParent(mCompleteTree);
125:
126: // add aAST to aFile
127: final SymTabAST child = SymTabASTFactory.create(aAST);
128: child.setFile(aFile);
129: fileNode.addChild(child);
130: child.setParent(fileNode);
131: fileNode.finishDefinition(aFile, mCompleteTree);
132: }
133:
134: /**
135: * Registers a node for checking.
136: * @param aCheck the check to apply.
137: * @param aNode the node to check.
138: */
139: public void registerCheckNode(AbstractUsageCheck aCheck, AST aNode) {
140: Set nodeSet = (Set) mCheckNodes.get(aCheck);
141: if (nodeSet == null) {
142: nodeSet = new HashSet();
143: nodeSet.add(aNode);
144: mCheckNodes.put(aCheck, nodeSet);
145: } else {
146: nodeSet.add(aNode);
147: }
148: }
149:
150: /**
151: * Gets the nodes to check with a usage check.
152: * @param aCheck the usage check.
153: * @return the nodes to check with aCheck.
154: * @throws SymbolTableException if there is an error.
155: */
156: public Set getCheckNodes(AbstractUsageCheck aCheck)
157: throws SymbolTableException {
158: // lazy initialization
159: if (mCompleteTree == null) {
160: buildTree();
161: }
162: Set result = (Set) mCheckNodes.get(aCheck);
163: if (result == null) {
164: result = new HashSet();
165: }
166: return result;
167: }
168:
169: /**
170: * Maps a AST to its associated SymTabAST.
171: * @param aAST the AST.
172: * @param aSymTabAST the SymTabAST associated with aAST.
173: */
174: public void put(AST aAST, SymTabAST aSymTabAST) {
175: mMap.put(aAST, aSymTabAST);
176: }
177:
178: /**
179: * Gets the SymTabAST associated with a AST.
180: * @param aAST the AST.
181: * @return the the SymTabAST associated with aAST.
182: */
183: public SymTabAST get(AST aAST) {
184: return (SymTabAST) mMap.get(aAST);
185: }
186:
187: /**
188: * Clears all associations from DetailsASTs to SymTabASTs.
189: */
190: public void clearDetailsMap() {
191: mMap.clear();
192: }
193:
194: /**
195: * Determines whether the map from DetailsASTs to SymTabASTs is empty.
196: * @return true if the map is empty.
197: */
198: public boolean isEmptyDetailsMap() {
199: return mMap.isEmpty();
200: }
201:
202: /**
203: * Removes a check and its check nodes. Clears all managed elements if
204: * last check removed.
205: * @param aCheck the check to remove.
206: */
207: public void removeCheck(AbstractUsageCheck aCheck) {
208: mCheckNodes.remove(aCheck);
209: if (mCheckNodes.isEmpty()) {
210: clear();
211: }
212: }
213: }
|