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 }