Coverage Report - com.puppycrawl.tools.checkstyle.TreeWalker
 
Classes in this File Line Coverage Branch Coverage Complexity
TreeWalker
100%
275/275
100%
104/104
0
TreeWalker$AstState
100%
3/3
N/A
0
 
 1  
 ////////////////////////////////////////////////////////////////////////////////
 2  
 // checkstyle: Checks Java source code for adherence to a set of rules.
 3  
 // Copyright (C) 2001-2017 the original author or authors.
 4  
 //
 5  
 // This library is free software; you can redistribute it and/or
 6  
 // modify it under the terms of the GNU Lesser General Public
 7  
 // License as published by the Free Software Foundation; either
 8  
 // version 2.1 of the License, or (at your option) any later version.
 9  
 //
 10  
 // This library is distributed in the hope that it will be useful,
 11  
 // but WITHOUT ANY WARRANTY; without even the implied warranty of
 12  
 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 13  
 // Lesser General Public License for more details.
 14  
 //
 15  
 // You should have received a copy of the GNU Lesser General Public
 16  
 // License along with this library; if not, write to the Free Software
 17  
 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 18  
 ////////////////////////////////////////////////////////////////////////////////
 19  
 
 20  
 package com.puppycrawl.tools.checkstyle;
 21  
 
 22  
 import java.io.File;
 23  
 import java.io.Reader;
 24  
 import java.io.StringReader;
 25  
 import java.util.Arrays;
 26  
 import java.util.Collection;
 27  
 import java.util.HashSet;
 28  
 import java.util.Locale;
 29  
 import java.util.Set;
 30  
 import java.util.SortedSet;
 31  
 import java.util.TreeSet;
 32  
 
 33  
 import antlr.CommonHiddenStreamToken;
 34  
 import antlr.RecognitionException;
 35  
 import antlr.Token;
 36  
 import antlr.TokenStreamException;
 37  
 import antlr.TokenStreamHiddenTokenFilter;
 38  
 import antlr.TokenStreamRecognitionException;
 39  
 import com.google.common.collect.HashMultimap;
 40  
 import com.google.common.collect.Multimap;
 41  
 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
 42  
 import com.puppycrawl.tools.checkstyle.api.AbstractFileSetCheck;
 43  
 import com.puppycrawl.tools.checkstyle.api.AutomaticBean;
 44  
 import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
 45  
 import com.puppycrawl.tools.checkstyle.api.Configuration;
 46  
 import com.puppycrawl.tools.checkstyle.api.Context;
 47  
 import com.puppycrawl.tools.checkstyle.api.DetailAST;
 48  
 import com.puppycrawl.tools.checkstyle.api.ExternalResourceHolder;
 49  
 import com.puppycrawl.tools.checkstyle.api.FileContents;
 50  
 import com.puppycrawl.tools.checkstyle.api.FileText;
 51  
 import com.puppycrawl.tools.checkstyle.api.LocalizedMessage;
 52  
 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
 53  
 import com.puppycrawl.tools.checkstyle.grammars.GeneratedJavaLexer;
 54  
 import com.puppycrawl.tools.checkstyle.grammars.GeneratedJavaRecognizer;
 55  
 import com.puppycrawl.tools.checkstyle.utils.CommonUtils;
 56  
 import com.puppycrawl.tools.checkstyle.utils.TokenUtils;
 57  
 
 58  
 /**
 59  
  * Responsible for walking an abstract syntax tree and notifying interested
 60  
  * checks at each each node.
 61  
  *
 62  
  * @author Oliver Burn
 63  
  */
 64  
 // -@cs[ClassFanOutComplexity] To resolve issue 4714, new classes were imported. Number of
 65  
 // classes current class relies on currently is 27, which is above threshold 25.
 66  
 // see https://github.com/checkstyle/checkstyle/issues/4714.
 67  
 public final class TreeWalker extends AbstractFileSetCheck implements ExternalResourceHolder {
 68  
 
 69  
     /** Default distance between tab stops. */
 70  
     private static final int DEFAULT_TAB_WIDTH = 8;
 71  
 
 72  
     /** Maps from token name to ordinary checks. */
 73  1816
     private final Multimap<String, AbstractCheck> tokenToOrdinaryChecks =
 74  1816
         HashMultimap.create();
 75  
 
 76  
     /** Maps from token name to comment checks. */
 77  1816
     private final Multimap<String, AbstractCheck> tokenToCommentChecks =
 78  1816
             HashMultimap.create();
 79  
 
 80  
     /** Registered ordinary checks, that don't use comment nodes. */
 81  1816
     private final Set<AbstractCheck> ordinaryChecks = new HashSet<>();
 82  
 
 83  
     /** Registered comment checks. */
 84  1816
     private final Set<AbstractCheck> commentChecks = new HashSet<>();
 85  
 
 86  
     /** The ast filters. */
 87  1816
     private final Set<TreeWalkerFilter> filters = new HashSet<>();
 88  
 
 89  
     /** The sorted set of messages. */
 90  1816
     private final SortedSet<LocalizedMessage> messages = new TreeSet<>();
 91  
 
 92  
     /** The distance between tab stops. */
 93  1816
     private int tabWidth = DEFAULT_TAB_WIDTH;
 94  
 
 95  
     /** Class loader to resolve classes with. **/
 96  
     private ClassLoader classLoader;
 97  
 
 98  
     /** Context of child components. */
 99  
     private Context childContext;
 100  
 
 101  
     /** A factory for creating submodules (i.e. the Checks) */
 102  
     private ModuleFactory moduleFactory;
 103  
 
 104  
     /**
 105  
      * Creates a new {@code TreeWalker} instance.
 106  
      */
 107  1816
     public TreeWalker() {
 108  1816
         setFileExtensions("java");
 109  1816
     }
 110  
 
 111  
     /**
 112  
      * Sets tab width.
 113  
      * @param tabWidth the distance between tab stops
 114  
      */
 115  
     public void setTabWidth(int tabWidth) {
 116  5
         this.tabWidth = tabWidth;
 117  5
     }
 118  
 
 119  
     /**
 120  
      * Sets cache file.
 121  
      * @deprecated Use {@link Checker#setCacheFile} instead. It does not do anything now. We just
 122  
      *             keep the setter for transition period to the same option in Checker. The
 123  
      *             method will be completely removed in Checkstyle 8.0. See
 124  
      *             <a href="https://github.com/checkstyle/checkstyle/issues/2883">issue#2883</a>
 125  
      * @param fileName the cache file
 126  
      */
 127  
     @Deprecated
 128  
     public void setCacheFile(String fileName) {
 129  
         // Deprecated
 130  1
     }
 131  
 
 132  
     /**
 133  
      * Sets classLoader to load class.
 134  
      * @param classLoader class loader to resolve classes with.
 135  
      */
 136  
     public void setClassLoader(ClassLoader classLoader) {
 137  1804
         this.classLoader = classLoader;
 138  1804
     }
 139  
 
 140  
     /**
 141  
      * Sets the module factory for creating child modules (Checks).
 142  
      * @param moduleFactory the factory
 143  
      */
 144  
     public void setModuleFactory(ModuleFactory moduleFactory) {
 145  1811
         this.moduleFactory = moduleFactory;
 146  1811
     }
 147  
 
 148  
     @Override
 149  
     public void finishLocalSetup() {
 150  1813
         final DefaultContext checkContext = new DefaultContext();
 151  1813
         checkContext.add("classLoader", classLoader);
 152  1813
         checkContext.add("severity", getSeverity());
 153  1813
         checkContext.add("tabWidth", String.valueOf(tabWidth));
 154  
 
 155  1813
         childContext = checkContext;
 156  1813
     }
 157  
 
 158  
     /**
 159  
      * {@inheritDoc} Creates child module.
 160  
      * @noinspection ChainOfInstanceofChecks
 161  
      */
 162  
     @Override
 163  
     public void setupChild(Configuration childConf)
 164  
             throws CheckstyleException {
 165  1976
         final String name = childConf.getName();
 166  1976
         final Object module = moduleFactory.createModule(name);
 167  1974
         if (module instanceof AutomaticBean) {
 168  1973
             final AutomaticBean bean = (AutomaticBean) module;
 169  1973
             bean.contextualize(childContext);
 170  1973
             bean.configure(childConf);
 171  
         }
 172  1951
         if (module instanceof AbstractCheck) {
 173  1902
             final AbstractCheck check = (AbstractCheck) module;
 174  1902
             check.init();
 175  1900
             registerCheck(check);
 176  1897
         }
 177  49
         else if (module instanceof TreeWalkerFilter) {
 178  47
             final TreeWalkerFilter filter = (TreeWalkerFilter) module;
 179  47
             filters.add(filter);
 180  47
         }
 181  
         else {
 182  2
             throw new CheckstyleException(
 183  
                 "TreeWalker is not allowed as a parent of " + name
 184  
                         + " Please review 'Parent Module' section for this Check in web"
 185  
                         + " documentation if Check is standard.");
 186  
         }
 187  1944
     }
 188  
 
 189  
     @Override
 190  
     protected void processFiltered(File file, FileText fileText) throws CheckstyleException {
 191  
         // check if already checked and passed the file
 192  1393
         if (CommonUtils.matchesFileExtension(file, getFileExtensions())) {
 193  1392
             final String msg = "%s occurred during the analysis of file %s.";
 194  1392
             final String fileName = file.getPath();
 195  
 
 196  
             try {
 197  1392
                 if (!ordinaryChecks.isEmpty()
 198  150
                         || !commentChecks.isEmpty()) {
 199  1386
                     final FileContents contents = new FileContents(fileText);
 200  1386
                     final DetailAST rootAST = parse(contents);
 201  
 
 202  1379
                     if (!ordinaryChecks.isEmpty()) {
 203  1237
                         walk(rootAST, contents, AstState.ORDINARY);
 204  
                     }
 205  1378
                     if (!commentChecks.isEmpty()) {
 206  151
                         final DetailAST astWithComments = appendHiddenCommentNodes(rootAST);
 207  
 
 208  151
                         walk(astWithComments, contents, AstState.WITH_COMMENTS);
 209  
                     }
 210  1377
                     if (filters.isEmpty()) {
 211  1353
                         addMessages(messages);
 212  
                     }
 213  
                     else {
 214  24
                         final SortedSet<LocalizedMessage> filteredMessages =
 215  24
                             getFilteredMessages(fileName, contents, rootAST);
 216  20
                         addMessages(filteredMessages);
 217  
                     }
 218  1373
                     messages.clear();
 219  
                 }
 220  
             }
 221  4
             catch (final TokenStreamRecognitionException tre) {
 222  4
                 final String exceptionMsg = String.format(Locale.ROOT, msg,
 223  
                         "TokenStreamRecognitionException", fileName);
 224  4
                 throw new CheckstyleException(exceptionMsg, tre);
 225  
             }
 226  3
             catch (RecognitionException | TokenStreamException ex) {
 227  6
                 final String exceptionMsg = String.format(Locale.ROOT, msg,
 228  3
                         ex.getClass().getSimpleName(), fileName);
 229  3
                 throw new CheckstyleException(exceptionMsg, ex);
 230  1379
             }
 231  
         }
 232  1380
     }
 233  
 
 234  
     /**
 235  
      * Returns filtered set of {@link LocalizedMessage}.
 236  
      * @param fileName path to the file
 237  
      * @param fileContents the contents of the file
 238  
      * @param rootAST root AST element {@link DetailAST} of the file
 239  
      * @return filtered set of messages
 240  
      */
 241  
     private SortedSet<LocalizedMessage> getFilteredMessages(
 242  
             String fileName, FileContents fileContents, DetailAST rootAST) {
 243  24
         final SortedSet<LocalizedMessage> result = new TreeSet<>(messages);
 244  24
         for (LocalizedMessage element : messages) {
 245  386
             final TreeWalkerAuditEvent event =
 246  
                     new TreeWalkerAuditEvent(fileContents, fileName, element, rootAST);
 247  386
             for (TreeWalkerFilter filter : filters) {
 248  386
                 if (!filter.accept(event)) {
 249  54
                     result.remove(element);
 250  54
                     break;
 251  
                 }
 252  328
             }
 253  382
         }
 254  20
         return result;
 255  
     }
 256  
 
 257  
     /**
 258  
      * Register a check for a given configuration.
 259  
      * @param check the check to register
 260  
      * @throws CheckstyleException if an error occurs
 261  
      */
 262  
     private void registerCheck(AbstractCheck check)
 263  
             throws CheckstyleException {
 264  1900
         validateDefaultTokens(check);
 265  
         final int[] tokens;
 266  1899
         final Set<String> checkTokens = check.getTokenNames();
 267  1899
         if (checkTokens.isEmpty()) {
 268  1677
             tokens = check.getDefaultTokens();
 269  
         }
 270  
         else {
 271  222
             tokens = check.getRequiredTokens();
 272  
 
 273  
             //register configured tokens
 274  222
             final int[] acceptableTokens = check.getAcceptableTokens();
 275  222
             Arrays.sort(acceptableTokens);
 276  222
             for (String token : checkTokens) {
 277  496
                 final int tokenId = TokenUtils.getTokenId(token);
 278  496
                 if (Arrays.binarySearch(acceptableTokens, tokenId) >= 0) {
 279  495
                     registerCheck(token, check);
 280  
                 }
 281  
                 else {
 282  2
                     final String message = String.format(Locale.ROOT, "Token \"%s\" was "
 283  
                             + "not found in Acceptable tokens list in check %s",
 284  1
                             token, check.getClass().getName());
 285  1
                     throw new CheckstyleException(message);
 286  
                 }
 287  495
             }
 288  
         }
 289  12101
         for (int element : tokens) {
 290  10204
             registerCheck(element, check);
 291  
         }
 292  1897
         if (check.isCommentNodesRequired()) {
 293  201
             commentChecks.add(check);
 294  
         }
 295  
         else {
 296  1696
             ordinaryChecks.add(check);
 297  
         }
 298  1897
     }
 299  
 
 300  
     /**
 301  
      * Register a check for a specified token id.
 302  
      * @param tokenId the id of the token
 303  
      * @param check the check to register
 304  
      * @throws CheckstyleException if Check is misconfigured
 305  
      */
 306  
     private void registerCheck(int tokenId, AbstractCheck check) throws CheckstyleException {
 307  10204
         registerCheck(TokenUtils.getTokenName(tokenId), check);
 308  10203
     }
 309  
 
 310  
     /**
 311  
      * Register a check for a specified token name.
 312  
      * @param token the name of the token
 313  
      * @param check the check to register
 314  
      * @throws CheckstyleException if Check is misconfigured
 315  
      */
 316  
     private void registerCheck(String token, AbstractCheck check) throws CheckstyleException {
 317  10699
         if (check.isCommentNodesRequired()) {
 318  814
             tokenToCommentChecks.put(token, check);
 319  
         }
 320  9885
         else if (TokenUtils.isCommentType(token)) {
 321  2
             final String message = String.format(Locale.ROOT, "Check '%s' waits for comment type "
 322  
                     + "token ('%s') and should override 'isCommentNodesRequired()' "
 323  1
                     + "method to return 'true'", check.getClass().getName(), token);
 324  1
             throw new CheckstyleException(message);
 325  
         }
 326  
         else {
 327  9884
             tokenToOrdinaryChecks.put(token, check);
 328  
         }
 329  10698
     }
 330  
 
 331  
     /**
 332  
      * Validates that check's required tokens are subset of default tokens.
 333  
      * @param check to validate
 334  
      * @throws CheckstyleException when validation of default tokens fails
 335  
      */
 336  
     private static void validateDefaultTokens(AbstractCheck check) throws CheckstyleException {
 337  1900
         if (check.getRequiredTokens().length != 0) {
 338  1240
             final int[] defaultTokens = check.getDefaultTokens();
 339  1240
             Arrays.sort(defaultTokens);
 340  7110
             for (final int token : check.getRequiredTokens()) {
 341  5871
                 if (Arrays.binarySearch(defaultTokens, token) < 0) {
 342  2
                     final String message = String.format(Locale.ROOT, "Token \"%s\" from required "
 343  
                             + "tokens was not found in default tokens list in check %s",
 344  1
                             token, check.getClass().getName());
 345  1
                     throw new CheckstyleException(message);
 346  
                 }
 347  
             }
 348  
         }
 349  1899
     }
 350  
 
 351  
     /**
 352  
      * Initiates the walk of an AST.
 353  
      * @param ast the root AST
 354  
      * @param contents the contents of the file the AST was generated from.
 355  
      * @param astState state of AST.
 356  
      */
 357  
     private void walk(DetailAST ast, FileContents contents,
 358  
             AstState astState) {
 359  1388
         notifyBegin(ast, contents, astState);
 360  
 
 361  
         // empty files are not flagged by javac, will yield ast == null
 362  1388
         if (ast != null) {
 363  1382
             processIter(ast, astState);
 364  
         }
 365  1386
         notifyEnd(ast, astState);
 366  1386
     }
 367  
 
 368  
     /**
 369  
      * Notify checks that we are about to begin walking a tree.
 370  
      * @param rootAST the root of the tree.
 371  
      * @param contents the contents of the file the AST was generated from.
 372  
      * @param astState state of AST.
 373  
      */
 374  
     private void notifyBegin(DetailAST rootAST, FileContents contents,
 375  
             AstState astState) {
 376  
         final Set<AbstractCheck> checks;
 377  
 
 378  1388
         if (astState == AstState.WITH_COMMENTS) {
 379  151
             checks = commentChecks;
 380  
         }
 381  
         else {
 382  1237
             checks = ordinaryChecks;
 383  
         }
 384  
 
 385  1388
         for (AbstractCheck check : checks) {
 386  1514
             check.setFileContents(contents);
 387  1514
             check.clearMessages();
 388  1514
             check.beginTree(rootAST);
 389  1514
         }
 390  1388
     }
 391  
 
 392  
     /**
 393  
      * Notify checks that we have finished walking a tree.
 394  
      * @param rootAST the root of the tree.
 395  
      * @param astState state of AST.
 396  
      */
 397  
     private void notifyEnd(DetailAST rootAST, AstState astState) {
 398  
         final Set<AbstractCheck> checks;
 399  
 
 400  1386
         if (astState == AstState.WITH_COMMENTS) {
 401  150
             checks = commentChecks;
 402  
         }
 403  
         else {
 404  1236
             checks = ordinaryChecks;
 405  
         }
 406  
 
 407  1386
         for (AbstractCheck check : checks) {
 408  1512
             check.finishTree(rootAST);
 409  1512
             messages.addAll(check.getMessages());
 410  1512
         }
 411  1386
     }
 412  
 
 413  
     /**
 414  
      * Notify checks that visiting a node.
 415  
      * @param ast the node to notify for.
 416  
      * @param astState state of AST.
 417  
      */
 418  
     private void notifyVisit(DetailAST ast, AstState astState) {
 419  619507
         final Collection<AbstractCheck> visitors = getListOfChecks(ast, astState);
 420  
 
 421  619507
         if (visitors != null) {
 422  128240
             for (AbstractCheck check : visitors) {
 423  128772
                 check.visitToken(ast);
 424  128770
             }
 425  
         }
 426  619505
     }
 427  
 
 428  
     /**
 429  
      * Notify checks that leaving a node.
 430  
      * @param ast
 431  
      *        the node to notify for
 432  
      * @param astState state of AST.
 433  
      */
 434  
     private void notifyLeave(DetailAST ast, AstState astState) {
 435  619503
         final Collection<AbstractCheck> visitors = getListOfChecks(ast, astState);
 436  
 
 437  619503
         if (visitors != null) {
 438  128237
             for (AbstractCheck check : visitors) {
 439  128769
                 check.leaveToken(ast);
 440  128769
             }
 441  
         }
 442  619503
     }
 443  
 
 444  
     /**
 445  
      * Method returns list of checks.
 446  
      *
 447  
      * @param ast
 448  
      *            the node to notify for
 449  
      * @param astState
 450  
      *            state of AST.
 451  
      * @return list of visitors
 452  
      */
 453  
     private Collection<AbstractCheck> getListOfChecks(DetailAST ast, AstState astState) {
 454  1239010
         Collection<AbstractCheck> visitors = null;
 455  1239010
         final String tokenType = TokenUtils.getTokenName(ast.getType());
 456  
 
 457  1239010
         if (astState == AstState.WITH_COMMENTS) {
 458  209949
             if (tokenToCommentChecks.containsKey(tokenType)) {
 459  66852
                 visitors = tokenToCommentChecks.get(tokenType);
 460  
             }
 461  
         }
 462  
         else {
 463  1029061
             if (tokenToOrdinaryChecks.containsKey(tokenType)) {
 464  189625
                 visitors = tokenToOrdinaryChecks.get(tokenType);
 465  
             }
 466  
         }
 467  1239010
         return visitors;
 468  
     }
 469  
 
 470  
     /**
 471  
      * Static helper method to parses a Java source file.
 472  
      *
 473  
      * @param contents
 474  
      *                contains the contents of the file
 475  
      * @return the root of the AST
 476  
      * @throws TokenStreamException
 477  
      *                 if lexing failed
 478  
      * @throws RecognitionException
 479  
      *                 if parsing failed
 480  
      */
 481  
     public static DetailAST parse(FileContents contents)
 482  
             throws RecognitionException, TokenStreamException {
 483  2454
         final String fullText = contents.getText().getFullText().toString();
 484  2454
         final Reader reader = new StringReader(fullText);
 485  2454
         final GeneratedJavaLexer lexer = new GeneratedJavaLexer(reader);
 486  2454
         lexer.setCommentListener(contents);
 487  2454
         lexer.setTokenObjectClass("antlr.CommonHiddenStreamToken");
 488  
 
 489  2454
         final TokenStreamHiddenTokenFilter filter =
 490  
                 new TokenStreamHiddenTokenFilter(lexer);
 491  2454
         filter.hide(TokenTypes.SINGLE_LINE_COMMENT);
 492  2454
         filter.hide(TokenTypes.BLOCK_COMMENT_BEGIN);
 493  
 
 494  2454
         final GeneratedJavaRecognizer parser =
 495  
             new GeneratedJavaRecognizer(filter);
 496  2454
         parser.setFilename(contents.getFileName());
 497  2454
         parser.setASTNodeClass(DetailAST.class.getName());
 498  2454
         parser.compilationUnit();
 499  
 
 500  2445
         return (DetailAST) parser.getAST();
 501  
     }
 502  
 
 503  
     /**
 504  
      * Parses Java source file. Result AST contains comment nodes.
 505  
      * @param contents source file content
 506  
      * @return DetailAST tree
 507  
      * @throws RecognitionException if parser failed
 508  
      * @throws TokenStreamException if lexer failed
 509  
      */
 510  
     public static DetailAST parseWithComments(FileContents contents)
 511  
             throws RecognitionException, TokenStreamException {
 512  185
         return appendHiddenCommentNodes(parse(contents));
 513  
     }
 514  
 
 515  
     @Override
 516  
     public void destroy() {
 517  1376
         ordinaryChecks.forEach(AbstractCheck::destroy);
 518  1376
         commentChecks.forEach(AbstractCheck::destroy);
 519  1376
         super.destroy();
 520  1376
     }
 521  
 
 522  
     @Override
 523  
     public Set<String> getExternalResourceLocations() {
 524  6
         final Set<String> ordinaryChecksResources =
 525  6
                 getExternalResourceLocationsOfChecks(ordinaryChecks);
 526  6
         final Set<String> commentChecksResources =
 527  6
                 getExternalResourceLocationsOfChecks(commentChecks);
 528  6
         final Set<String> filtersResources =
 529  6
                 getExternalResourceLocationsOfFilters();
 530  6
         final int resultListSize = commentChecksResources.size()
 531  6
                 + ordinaryChecksResources.size()
 532  6
                 + filtersResources.size();
 533  6
         final Set<String> resourceLocations = new HashSet<>(resultListSize);
 534  6
         resourceLocations.addAll(ordinaryChecksResources);
 535  6
         resourceLocations.addAll(commentChecksResources);
 536  6
         resourceLocations.addAll(filtersResources);
 537  6
         return resourceLocations;
 538  
     }
 539  
 
 540  
     /**
 541  
      * Returns a set of external configuration resource locations which are used by the filters set.
 542  
      * @return a set of external configuration resource locations which are used by the filters set.
 543  
      */
 544  
     private Set<String> getExternalResourceLocationsOfFilters() {
 545  6
         final Set<String> externalConfigurationResources = new HashSet<>();
 546  8
         filters.stream().filter(filter -> filter instanceof ExternalResourceHolder)
 547  6
                 .forEach(filter -> {
 548  2
                     final Set<String> checkExternalResources =
 549  2
                         ((ExternalResourceHolder) filter).getExternalResourceLocations();
 550  2
                     externalConfigurationResources.addAll(checkExternalResources);
 551  2
                 });
 552  6
         return externalConfigurationResources;
 553  
     }
 554  
 
 555  
     /**
 556  
      * Returns a set of external configuration resource locations which are used by the checks set.
 557  
      * @param checks a set of checks.
 558  
      * @return a set of external configuration resource locations which are used by the checks set.
 559  
      */
 560  
     private static Set<String> getExternalResourceLocationsOfChecks(Set<AbstractCheck> checks) {
 561  12
         final Set<String> externalConfigurationResources = new HashSet<>();
 562  16
         checks.stream().filter(check -> check instanceof ExternalResourceHolder).forEach(check -> {
 563  2
             final Set<String> checkExternalResources =
 564  2
                 ((ExternalResourceHolder) check).getExternalResourceLocations();
 565  2
             externalConfigurationResources.addAll(checkExternalResources);
 566  2
         });
 567  12
         return externalConfigurationResources;
 568  
     }
 569  
 
 570  
     /**
 571  
      * Processes a node calling interested checks at each node.
 572  
      * Uses iterative algorithm.
 573  
      * @param root the root of tree for process
 574  
      * @param astState state of AST.
 575  
      */
 576  
     private void processIter(DetailAST root, AstState astState) {
 577  1382
         DetailAST curNode = root;
 578  620887
         while (curNode != null) {
 579  619507
             notifyVisit(curNode, astState);
 580  619505
             DetailAST toVisit = curNode.getFirstChild();
 581  1239008
             while (curNode != null && toVisit == null) {
 582  619503
                 notifyLeave(curNode, astState);
 583  619503
                 toVisit = curNode.getNextSibling();
 584  619503
                 if (toVisit == null) {
 585  274860
                     curNode = curNode.getParent();
 586  
                 }
 587  
             }
 588  619505
             curNode = toVisit;
 589  619505
         }
 590  1380
     }
 591  
 
 592  
     /**
 593  
      * Appends comment nodes to existing AST.
 594  
      * It traverses each node in AST, looks for hidden comment tokens
 595  
      * and appends found comment tokens as nodes in AST.
 596  
      * @param root
 597  
      *        root of AST.
 598  
      * @return root of AST with comment nodes.
 599  
      */
 600  
     private static DetailAST appendHiddenCommentNodes(DetailAST root) {
 601  336
         DetailAST result = root;
 602  336
         DetailAST curNode = root;
 603  336
         DetailAST lastNode = root;
 604  
 
 605  64567
         while (curNode != null) {
 606  64231
             if (isPositionGreater(curNode, lastNode)) {
 607  36347
                 lastNode = curNode;
 608  
             }
 609  
 
 610  64231
             CommonHiddenStreamToken tokenBefore = curNode.getHiddenBefore();
 611  64231
             DetailAST currentSibling = curNode;
 612  98154
             while (tokenBefore != null) {
 613  33923
                 final DetailAST newCommentNode =
 614  33923
                          createCommentAstFromToken(tokenBefore);
 615  
 
 616  33923
                 currentSibling.addPreviousSibling(newCommentNode);
 617  
 
 618  33923
                 if (currentSibling == result) {
 619  234
                     result = newCommentNode;
 620  
                 }
 621  
 
 622  33923
                 currentSibling = newCommentNode;
 623  33923
                 tokenBefore = tokenBefore.getHiddenBefore();
 624  33923
             }
 625  
 
 626  64231
             DetailAST toVisit = curNode.getFirstChild();
 627  128462
             while (curNode != null && toVisit == null) {
 628  64231
                 toVisit = curNode.getNextSibling();
 629  64231
                 if (toVisit == null) {
 630  26999
                     curNode = curNode.getParent();
 631  
                 }
 632  
             }
 633  64231
             curNode = toVisit;
 634  64231
         }
 635  336
         if (lastNode != null) {
 636  330
             CommonHiddenStreamToken tokenAfter = lastNode.getHiddenAfter();
 637  330
             DetailAST currentSibling = lastNode;
 638  351
             while (tokenAfter != null) {
 639  21
                 final DetailAST newCommentNode =
 640  21
                         createCommentAstFromToken(tokenAfter);
 641  
 
 642  21
                 currentSibling.addNextSibling(newCommentNode);
 643  
 
 644  21
                 currentSibling = newCommentNode;
 645  21
                 tokenAfter = tokenAfter.getHiddenAfter();
 646  21
             }
 647  
         }
 648  336
         return result;
 649  
     }
 650  
 
 651  
     /**
 652  
      * Checks if position of first DetailAST is greater than position of
 653  
      * second DetailAST. Position is line number and column number in source
 654  
      * file.
 655  
      * @param ast1
 656  
      *        first DetailAST node.
 657  
      * @param ast2
 658  
      *        second DetailAST node.
 659  
      * @return true if position of ast1 is greater than position of ast2.
 660  
      */
 661  
     private static boolean isPositionGreater(DetailAST ast1, DetailAST ast2) {
 662  64234
         boolean isGreater = ast1.getLineNo() > ast2.getLineNo();
 663  64234
         if (!isGreater && ast1.getLineNo() == ast2.getLineNo()) {
 664  55253
             isGreater = ast1.getColumnNo() > ast2.getColumnNo();
 665  
         }
 666  64234
         return isGreater;
 667  
     }
 668  
 
 669  
     /**
 670  
      * Create comment AST from token. Depending on token type
 671  
      * SINGLE_LINE_COMMENT or BLOCK_COMMENT_BEGIN is created.
 672  
      * @param token
 673  
      *        Token object.
 674  
      * @return DetailAST of comment node.
 675  
      */
 676  
     private static DetailAST createCommentAstFromToken(Token token) {
 677  
         final DetailAST commentAst;
 678  33944
         if (token.getType() == TokenTypes.SINGLE_LINE_COMMENT) {
 679  31177
             commentAst = createSlCommentNode(token);
 680  
         }
 681  
         else {
 682  2767
             commentAst = CommonUtils.createBlockCommentNode(token);
 683  
         }
 684  33944
         return commentAst;
 685  
     }
 686  
 
 687  
     /**
 688  
      * Create single-line comment from token.
 689  
      * @param token
 690  
      *        Token object.
 691  
      * @return DetailAST with SINGLE_LINE_COMMENT type.
 692  
      */
 693  
     private static DetailAST createSlCommentNode(Token token) {
 694  31177
         final DetailAST slComment = new DetailAST();
 695  31177
         slComment.setType(TokenTypes.SINGLE_LINE_COMMENT);
 696  31177
         slComment.setText("//");
 697  
 
 698  
         // column counting begins from 0
 699  31177
         slComment.setColumnNo(token.getColumn() - 1);
 700  31177
         slComment.setLineNo(token.getLine());
 701  
 
 702  31177
         final DetailAST slCommentContent = new DetailAST();
 703  31177
         slCommentContent.setType(TokenTypes.COMMENT_CONTENT);
 704  
 
 705  
         // column counting begins from 0
 706  
         // plus length of '//'
 707  31177
         slCommentContent.setColumnNo(token.getColumn() - 1 + 2);
 708  31177
         slCommentContent.setLineNo(token.getLine());
 709  31177
         slCommentContent.setText(token.getText());
 710  
 
 711  31177
         slComment.addChild(slCommentContent);
 712  31177
         return slComment;
 713  
     }
 714  
 
 715  
     /**
 716  
      * State of AST.
 717  
      * Indicates whether tree contains certain nodes.
 718  
      */
 719  15
     private enum AstState {
 720  
         /**
 721  
          * Ordinary tree.
 722  
          */
 723  5
         ORDINARY,
 724  
 
 725  
         /**
 726  
          * AST contains comment nodes.
 727  
          */
 728  5
         WITH_COMMENTS
 729  
     }
 730  
 }