001    /*
002     * Sonar, open source software quality management tool.
003     * Copyright (C) 2009 SonarSource SA
004     * mailto:contact AT sonarsource DOT com
005     *
006     * Sonar is free software; you can redistribute it and/or
007     * modify it under the terms of the GNU Lesser General Public
008     * License as published by the Free Software Foundation; either
009     * version 3 of the License, or (at your option) any later version.
010     *
011     * Sonar is distributed in the hope that it will be useful,
012     * but WITHOUT ANY WARRANTY; without even the implied warranty of
013     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014     * Lesser General Public License for more details.
015     *
016     * You should have received a copy of the GNU Lesser General Public
017     * License along with Sonar; if not, write to the Free Software
018     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
019     */
020    package org.sonar.java.ast;
021    
022    import com.puppycrawl.tools.checkstyle.Checker;
023    import com.puppycrawl.tools.checkstyle.ConfigurationLoader;
024    import com.puppycrawl.tools.checkstyle.PropertiesExpander;
025    import com.puppycrawl.tools.checkstyle.api.Configuration;
026    import org.apache.commons.io.FileUtils;
027    import org.apache.commons.io.IOUtils;
028    import org.apache.commons.io.filefilter.FileFilterUtils;
029    import org.slf4j.Logger;
030    import org.slf4j.LoggerFactory;
031    import org.sonar.java.ast.visitor.*;
032    import org.sonar.squid.api.AnalysisException;
033    import org.sonar.squid.api.CodeScanner;
034    import org.sonar.squid.api.SourceCode;
035    import org.sonar.squid.api.SquidConfiguration;
036    
037    import java.io.ByteArrayInputStream;
038    import java.io.File;
039    import java.io.InputStream;
040    import java.nio.charset.Charset;
041    import java.util.*;
042    
043    /**
044     * Squid uses Checkstyle to get an out-of-the-box java parser with AST generation and visitor pattern support.
045     */
046    public class JavaAstScanner extends CodeScanner<AstVisitor> {
047    
048      private static final Logger LOG = LoggerFactory.getLogger(JavaAstScanner.class);
049      private SquidConfiguration conf;
050      private SourceCode project;
051    
052      public JavaAstScanner(SquidConfiguration conf, SourceCode project) {
053        this.conf = conf;
054        this.project = project;
055      }
056    
057      /**
058       * Create and execute the Checkstyle engine.
059       *
060       * @param files   collection of files to analyse. This list shouldn't contain and directory.
061       * @param charset the default charset to use to read files
062       */
063      private void launchCheckstyleEngine(Collection<File> files, Charset charset) {
064        Checker c = createChecker(charset);
065        File[] processedFiles = new File[files.size()];
066        files.toArray(processedFiles);
067        c.process(processedFiles);
068        c.destroy();
069      }
070    
071      /**
072       * Creates the Checkstyle Checker object.
073       *
074       * @return a nice new fresh Checkstyle Checker
075       */
076      private Checker createChecker(Charset charset) {
077        try {
078          InputStream checkstyleConfig = JavaAstScanner.class.getClassLoader().getResourceAsStream("checkstyle-configuration.xml");
079          String readenConfig = IOUtils.toString(checkstyleConfig);
080          readenConfig = readenConfig.replace("${charset}", charset.toString());
081          checkstyleConfig = new ByteArrayInputStream(readenConfig.getBytes());
082          Configuration config = ConfigurationLoader.loadConfiguration(checkstyleConfig, new PropertiesExpander(System.getProperties()), false);
083          Checker c = new Checker();
084          c.configure(config);
085          c.addListener(new CheckstyleAuditListener());
086          return c;
087        } catch (Exception e) {
088          throw new AnalysisException(
089              "Unable to create Checkstyle Checker object with 'checkstyle-configuration.xml' as Checkstyle configuration file name", e);
090        }
091      }
092    
093      public JavaAstScanner scanDirectory(File javaSourceDirectory) {
094        Collection<File> files = (Collection<File>) FileUtils.listFiles(javaSourceDirectory, FileFilterUtils.fileFileFilter(), FileFilterUtils.directoryFileFilter());
095        return scanFiles(files);
096      }
097    
098      public JavaAstScanner scanFile(File javaFile) {
099        return scanFiles(Arrays.asList(javaFile));
100      }
101    
102      public JavaAstScanner scanFiles(Collection<File> javaFiles) {
103        if (LOG.isDebugEnabled()) {
104          LOG.debug("----- Java sources analyzed by Squid:");
105          for (File javaFile : javaFiles) {
106            LOG.debug(javaFile.getAbsolutePath());
107          }
108          LOG.debug("-----");
109        }
110    
111        Stack<SourceCode> resourcesStack = new Stack<SourceCode>();
112        resourcesStack.add(project);
113        for (AstVisitor visitor : getVisitors()) {
114          visitor.setResourcesStack(resourcesStack);
115        }
116        CheckstyleSquidBridge.setASTVisitors(getVisitors());
117        CheckstyleSquidBridge.setSquidConfiguration(conf);
118        launchCheckstyleEngine(javaFiles, conf.getCharset());
119        return this;
120      }
121    
122      @Override
123      public Collection<Class<? extends AstVisitor>> getVisitorClasses() {
124        List<Class<? extends AstVisitor>> visitorClasses = new ArrayList<Class<? extends AstVisitor>>();
125        visitorClasses.add(PackageVisitor.class);
126        visitorClasses.add(FileVisitor.class);
127        visitorClasses.add(ClassVisitor.class);
128        visitorClasses.add(AnonymousInnerClassVisitor.class);
129        visitorClasses.add(MethodVisitor.class);
130        visitorClasses.add(EndAtLineVisitor.class);
131        visitorClasses.add(LinesVisitor.class);
132        visitorClasses.add(BlankLinesVisitor.class);
133        visitorClasses.add(CommentVisitor.class);
134        visitorClasses.add(PublicApiVisitor.class);
135        visitorClasses.add(BranchVisitor.class);
136        visitorClasses.add(StatementVisitor.class);
137        if (conf.isAnalysePropertyAccessors()) {
138          visitorClasses.add(AccessorVisitor.class);
139        }
140        visitorClasses.add(ComplexityVisitor.class);
141        visitorClasses.add(LinesOfCodeVisitor.class);
142        return visitorClasses;
143      }
144    }