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 }