All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.geomajas.checkstyle.TreeWalker Maven / Gradle / Ivy

There is a newer version: 2.6.0
Show newest version
/*
 * This is part of Geomajas, a GIS framework, http://www.geomajas.org/.
 *
 * Copyright 2008-2015 Geosparc nv, http://www.geosparc.com/, Belgium.
 *
 * The program is available in open source according to the GNU Affero
 * General Public License. All contributions in this program are covered
 * by the Geomajas Contributors License Agreement. For full licensing
 * details, see LICENSE.txt in the project root.
 */

package org.geomajas.checkstyle;

/**
 * Checkstyle TreeWalker replacement which never tries to cache checked files.
 *
 * @author Joachim Van der Auwera
 */

import antlr.RecognitionException;
import antlr.TokenStreamException;
import antlr.TokenStreamRecognitionException;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.puppycrawl.tools.checkstyle.DefaultContext;
import com.puppycrawl.tools.checkstyle.Defn;
import com.puppycrawl.tools.checkstyle.ModuleFactory;
import com.puppycrawl.tools.checkstyle.api.AbstractFileSetCheck;
import com.puppycrawl.tools.checkstyle.api.Check;
import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
import com.puppycrawl.tools.checkstyle.api.Configuration;
import com.puppycrawl.tools.checkstyle.api.Context;
import com.puppycrawl.tools.checkstyle.api.DetailAST;
import com.puppycrawl.tools.checkstyle.api.FileContents;
import com.puppycrawl.tools.checkstyle.api.LocalizedMessage;
import com.puppycrawl.tools.checkstyle.api.TokenTypes;
import com.puppycrawl.tools.checkstyle.api.Utils;
import com.puppycrawl.tools.checkstyle.grammars.GeneratedJavaLexer;
import com.puppycrawl.tools.checkstyle.grammars.GeneratedJavaRecognizer;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.File;
import java.io.Reader;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Set;

/**
 * Responsible for walking an abstract syntax tree and notifying interested
 * checks at each each node.
 * Modified by Joachim Van der Auwera to always walk all files, no caching.
 *
 * @author Oliver Burn
 * @version 1.0
 *          

* This file was copied from checkstyle and is originally licensed as LGPL. */ public final class TreeWalker extends AbstractFileSetCheck { /** default distance between tab stops */ private static final int DEFAULT_TAB_WIDTH = 8; /** maps from token name to checks */ private final Multimap mTokenToChecks = new HashMultimap(); /** all the registered checks */ private final Set mAllChecks = Sets.newHashSet(); /** the distance between tab stops */ private int mTabWidth = DEFAULT_TAB_WIDTH; /** class loader to resolve classes with. * */ private ClassLoader mClassLoader; /** context of child components */ private Context mChildContext; /** a factory for creating submodules (i.e. the Checks) */ private ModuleFactory mModuleFactory; /** * controls whether we should use recursive or iterative * algorithm for tree processing. */ private final boolean mRecursive; /** logger for debug purpose */ private static final Log LOG = LogFactory.getLog("com.puppycrawl.tools.checkstyle.TreeWalker"); /** Creates a new TreeWalker instance. */ public TreeWalker() { setFileExtensions(new String[] {"java"}); // Tree walker can use two possible algorithms for // tree processing (iterative and recursive. // Recursive is default for now. final String recursive = System.getProperty("checkstyle.use.recursive.algorithm", "false"); mRecursive = "true".equals(recursive); if (mRecursive) { LOG.debug("TreeWalker uses recursive algorithm"); } else { LOG.debug("TreeWalker uses iterative algorithm"); } } /** @param aTabWidth the distance between tab stops */ public void setTabWidth(int aTabWidth) { mTabWidth = aTabWidth; } public void setCacheFile(String aFileName) { // ignore, just for api compatibility } /** @param aClassLoader class loader to resolve classes with. */ public void setClassLoader(ClassLoader aClassLoader) { mClassLoader = aClassLoader; } /** * Sets the module factory for creating child modules (Checks). * * @param aModuleFactory the factory */ public void setModuleFactory(ModuleFactory aModuleFactory) { mModuleFactory = aModuleFactory; } @Override public void finishLocalSetup() { final DefaultContext checkContext = new DefaultContext(); checkContext.add("classLoader", mClassLoader); checkContext.add("messages", getMessageCollector()); checkContext.add("severity", getSeverity()); // TODO: hmmm.. this looks less than elegant // we have just parsed the string, // now we're recreating it only to parse it again a few moments later checkContext.add("tabWidth", String.valueOf(mTabWidth)); mChildContext = checkContext; } @Override public void setupChild(Configuration aChildConf) throws CheckstyleException { // TODO: improve the error handing final String name = aChildConf.getName(); final Object module = mModuleFactory.createModule(name); if (!(module instanceof Check)) { throw new CheckstyleException( "TreeWalker is not allowed as a parent of " + name); } final Check c = (Check) module; c.contextualize(mChildContext); c.configure(aChildConf); c.init(); registerCheck(c); } @Override protected void processFiltered(File aFile, List aLines) { // check if already checked and passed the file final String fileName = aFile.getPath(); try { final FileContents contents = new FileContents(fileName, aLines .toArray(new String[aLines.size()])); final DetailAST rootAST = TreeWalker.parse(contents); walk(rootAST, contents); } catch (final RecognitionException re) { Utils.getExceptionLogger() .debug("RecognitionException occured.", re); getMessageCollector().add( new LocalizedMessage( re.getLine(), re.getColumn(), Defn.CHECKSTYLE_BUNDLE, "general.exception", new String[] {re.getMessage()}, getId(), this.getClass(), null)); } catch (final TokenStreamRecognitionException tre) { Utils.getExceptionLogger() .debug("TokenStreamRecognitionException occured.", tre); final RecognitionException re = tre.recog; if (re != null) { getMessageCollector().add( new LocalizedMessage( re.getLine(), re.getColumn(), Defn.CHECKSTYLE_BUNDLE, "general.exception", new String[] {re.getMessage()}, getId(), this.getClass(), null)); } else { getMessageCollector().add( new LocalizedMessage( 0, Defn.CHECKSTYLE_BUNDLE, "general.exception", new String[] {"TokenStreamRecognitionException occured."}, getId(), this.getClass(), null)); } } catch (final TokenStreamException te) { Utils.getExceptionLogger() .debug("TokenStreamException occured.", te); getMessageCollector().add( new LocalizedMessage( 0, Defn.CHECKSTYLE_BUNDLE, "general.exception", new String[] {te.getMessage()}, getId(), this.getClass(), null)); } catch (final Throwable err) { Utils.getExceptionLogger().debug("Throwable occured.", err); getMessageCollector().add( new LocalizedMessage( 0, Defn.CHECKSTYLE_BUNDLE, "general.exception", new String[] {"" + err}, getId(), this.getClass(), null)); } } /** * Register a check for a given configuration. * * @param aCheck the check to register * @throws CheckstyleException if an error occurs */ private void registerCheck(Check aCheck) throws CheckstyleException { final int[] tokens; final Set checkTokens = aCheck.getTokenNames(); if (!checkTokens.isEmpty()) { tokens = aCheck.getRequiredTokens(); //register configured tokens final int[] acceptableTokens = aCheck.getAcceptableTokens(); Arrays.sort(acceptableTokens); for (String token : checkTokens) { try { final int tokenId = TokenTypes.getTokenId(token); if (Arrays.binarySearch(acceptableTokens, tokenId) >= 0) { registerCheck(token, aCheck); } // TODO: else log warning } catch (final IllegalArgumentException ex) { throw new CheckstyleException("illegal token \"" + token + "\" in check " + aCheck, ex); } } } else { tokens = aCheck.getDefaultTokens(); } for (int element : tokens) { registerCheck(element, aCheck); } mAllChecks.add(aCheck); } /** * Register a check for a specified token id. * * @param aTokenID the id of the token * @param aCheck the check to register */ private void registerCheck(int aTokenID, Check aCheck) { registerCheck(TokenTypes.getTokenName(aTokenID), aCheck); } /** * Register a check for a specified token name * * @param aToken the name of the token * @param aCheck the check to register */ private void registerCheck(String aToken, Check aCheck) { mTokenToChecks.put(aToken, aCheck); } /** * Initiates the walk of an AST. * * @param aAST the root AST * @param aContents the contents of the file the AST was generated from */ private void walk(DetailAST aAST, FileContents aContents) { getMessageCollector().reset(); notifyBegin(aAST, aContents); // empty files are not flagged by javac, will yield aAST == null if (aAST != null) { if (useRecursiveAlgorithm()) { processRec(aAST); } else { processIter(aAST); } } notifyEnd(aAST); } /** * Notify interested checks that about to begin walking a tree. * * @param aRootAST the root of the tree * @param aContents the contents of the file the AST was generated from */ private void notifyBegin(DetailAST aRootAST, FileContents aContents) { for (Check ch : mAllChecks) { ch.setFileContents(aContents); ch.beginTree(aRootAST); } } /** * Notify checks that finished walking a tree. * * @param aRootAST the root of the tree */ private void notifyEnd(DetailAST aRootAST) { for (Check ch : mAllChecks) { ch.finishTree(aRootAST); } } /** * Recursively processes a node calling interested checks at each node. * Uses recursive algorithm. * * @param aAST the node to start from */ private void processRec(DetailAST aAST) { if (aAST == null) { return; } notifyVisit(aAST); final DetailAST child = aAST.getFirstChild(); if (child != null) { processRec(child); } notifyLeave(aAST); final DetailAST sibling = aAST.getNextSibling(); if (sibling != null) { processRec(sibling); } } /** * Notify interested checks that visiting a node. * * @param aAST the node to notify for */ private void notifyVisit(DetailAST aAST) { final Collection visitors = mTokenToChecks.get(TokenTypes.getTokenName(aAST.getType())); for (Check c : visitors) { c.visitToken(aAST); } } /** * Notify interested checks that leaving a node. * * @param aAST the node to notify for */ private void notifyLeave(DetailAST aAST) { final Collection visitors = mTokenToChecks.get(TokenTypes.getTokenName(aAST.getType())); for (Check ch : visitors) { ch.leaveToken(aAST); } } /** * Static helper method to parses a Java source file. * * @param aContents contains the contents of the file * @return the root of the AST * @throws TokenStreamException if lexing failed * @throws RecognitionException if parsing failed */ public static DetailAST parse(FileContents aContents) throws RecognitionException, TokenStreamException { final Reader sar = new StringArrayReader(aContents.getLines()); final GeneratedJavaLexer lexer = new GeneratedJavaLexer(sar); lexer.setFilename(aContents.getFilename()); lexer.setCommentListener(aContents); lexer.setTreatAssertAsKeyword(true); lexer.setTreatEnumAsKeyword(true); final GeneratedJavaRecognizer parser = new GeneratedJavaRecognizer(lexer); parser.setFilename(aContents.getFilename()); parser.setASTNodeClass(DetailAST.class.getName()); parser.compilationUnit(); return (DetailAST) parser.getAST(); } @Override public void destroy() { for (Check c : mAllChecks) { c.destroy(); } super.destroy(); } /** * @return true if we should use recursive algorithm * for tree processing, false for iterative one. */ private boolean useRecursiveAlgorithm() { return mRecursive; } /** * Processes a node calling interested checks at each node. * Uses iterative algorithm. * * @param aRoot the root of tree for process */ private void processIter(DetailAST aRoot) { DetailAST curNode = aRoot; while (curNode != null) { notifyVisit(curNode); DetailAST toVisit = curNode.getFirstChild(); while ((curNode != null) && (toVisit == null)) { notifyLeave(curNode); toVisit = curNode.getNextSibling(); if (toVisit == null) { curNode = curNode.getParent(); } } curNode = toVisit; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy