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 java.io.ByteArrayInputStream;
023    import java.io.File;
024    import java.io.InputStream;
025    import java.nio.charset.Charset;
026    import java.util.ArrayList;
027    import java.util.Collection;
028    import java.util.List;
029    import java.util.Stack;
030    
031    import org.apache.commons.io.IOUtils;
032    import org.sonar.java.ast.visitor.AccessorVisitor;
033    import org.sonar.java.ast.visitor.AnonymousInnerClassVisitor;
034    import org.sonar.java.ast.visitor.AstVisitor;
035    import org.sonar.java.ast.visitor.BlankLinesVisitor;
036    import org.sonar.java.ast.visitor.BranchVisitor;
037    import org.sonar.java.ast.visitor.ClassVisitor;
038    import org.sonar.java.ast.visitor.CommentVisitor;
039    import org.sonar.java.ast.visitor.ComplexityVisitor;
040    import org.sonar.java.ast.visitor.EndAtLineVisitor;
041    import org.sonar.java.ast.visitor.FileVisitor;
042    import org.sonar.java.ast.visitor.LinesOfCodeVisitor;
043    import org.sonar.java.ast.visitor.LinesVisitor;
044    import org.sonar.java.ast.visitor.MethodVisitor;
045    import org.sonar.java.ast.visitor.PackageVisitor;
046    import org.sonar.java.ast.visitor.PublicApiVisitor;
047    import org.sonar.java.ast.visitor.StatementVisitor;
048    import org.sonar.squid.api.AnalysisException;
049    import org.sonar.squid.api.CodeScanner;
050    import org.sonar.squid.api.CodeVisitor;
051    import org.sonar.squid.api.SourceCode;
052    import org.sonar.squid.api.SquidConfiguration;
053    
054    import com.puppycrawl.tools.checkstyle.Checker;
055    import com.puppycrawl.tools.checkstyle.ConfigurationLoader;
056    import com.puppycrawl.tools.checkstyle.PropertiesExpander;
057    import com.puppycrawl.tools.checkstyle.api.Configuration;
058    
059    /**
060     * Squid uses Checkstyle to get an out-of-the-box java parser with AST generation and visitor pattern support.
061     */
062    public class JavaAstScanner implements CodeScanner {
063    
064      private SquidConfiguration conf;
065      private List<AstVisitor> visitors = new ArrayList<AstVisitor>();
066    
067      public JavaAstScanner(SquidConfiguration conf) {
068        this.conf = conf;
069      }
070    
071      /**
072       * Create and execute the Checkstyle engine.
073       * 
074       * @param files
075       *          collection of files to analyse. This list shouldn't contain and directory.
076       * @param charset
077       *          the default charset to use to read files
078       */
079      private void launchCheckstyleEngine(Collection<File> files, Charset charset) {
080        Checker c = createChecker(charset);
081        File[] processedFiles = new File[files.size()];
082        files.toArray(processedFiles);
083        c.process(processedFiles);
084        c.destroy();
085      }
086    
087      /**
088       * Creates the Checkstyle Checker object.
089       * 
090       * @return a nice new fresh Checkstyle Checker
091       */
092      private Checker createChecker(Charset charset) {
093        try {
094          InputStream checkstyleConfig = JavaAstScanner.class.getClassLoader().getResourceAsStream("checkstyle-configuration.xml");
095          String readenConfig = IOUtils.toString(checkstyleConfig);
096          readenConfig = readenConfig.replace("${charset}", charset.toString());
097          checkstyleConfig = new ByteArrayInputStream(readenConfig.getBytes());
098          Configuration config = ConfigurationLoader.loadConfiguration(checkstyleConfig, new PropertiesExpander(System.getProperties()), false);
099          Checker c = new Checker();
100          c.configure(config);
101          c.addListener(new CheckstyleAuditListener());
102          return c;
103        } catch (final Exception e) {
104          throw new AnalysisException(
105              "Unable to create Checkstyle Checker object with 'checkstyle-configuration.xml' as Checkstyle configuration file name", e);
106        }
107      }
108    
109      public void scanCode(SourceCode project, Collection<File> filesToAnalyse) {
110        Stack<SourceCode> resourcesStack = new Stack<SourceCode>();
111        resourcesStack.add(project);
112        for (AstVisitor visitor : visitors) {
113          visitor.setResourcesStack(resourcesStack);
114        }
115        CheckstyleSquidBridge.setASTVisitors(visitors);
116        CheckstyleSquidBridge.setSquidConfiguration(conf);
117        launchCheckstyleEngine(filesToAnalyse, conf.getCharset());
118      }
119    
120      public void accept(CodeVisitor visitor) {
121        visitors.add((AstVisitor) visitor);
122      }
123    
124      public Collection<Class<? extends CodeVisitor>> getVisitors() {
125        List<Class<? extends CodeVisitor>> visitorClasses = new ArrayList<Class<? extends CodeVisitor>>();
126        visitorClasses.add(PackageVisitor.class);
127        visitorClasses.add(FileVisitor.class);
128        visitorClasses.add(ClassVisitor.class);
129        visitorClasses.add(AnonymousInnerClassVisitor.class);
130        visitorClasses.add(MethodVisitor.class);
131        visitorClasses.add(EndAtLineVisitor.class);
132        visitorClasses.add(LinesVisitor.class);
133        visitorClasses.add(BlankLinesVisitor.class);
134        visitorClasses.add(CommentVisitor.class);
135        visitorClasses.add(PublicApiVisitor.class);
136        visitorClasses.add(BranchVisitor.class);
137        visitorClasses.add(StatementVisitor.class);
138        if (conf.isAnalysePropertyAccessors()) {
139          visitorClasses.add(AccessorVisitor.class);
140        }
141        visitorClasses.add(ComplexityVisitor.class);
142        visitorClasses.add(LinesOfCodeVisitor.class);
143        return visitorClasses;
144      }
145    }