001 /* 002 * Sonar, open source software quality management tool. 003 * Copyright (C) 2008-2011 SonarSource 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.google.common.collect.Lists; 023 import org.apache.commons.io.FileUtils; 024 import org.apache.commons.io.IOUtils; 025 import org.apache.commons.io.filefilter.FileFilterUtils; 026 import org.slf4j.Logger; 027 import org.slf4j.LoggerFactory; 028 import org.sonar.api.resources.InputFile; 029 import org.sonar.api.resources.InputFileUtils; 030 import org.sonar.java.ast.visitor.*; 031 import org.sonar.java.squid.JavaSquidConfiguration; 032 import org.sonar.squid.api.AnalysisException; 033 import org.sonar.squid.api.CodeScanner; 034 import org.sonar.squid.api.CodeVisitor; 035 import org.sonar.squid.api.SourceCode; 036 import org.xml.sax.InputSource; 037 038 import com.puppycrawl.tools.checkstyle.Checker; 039 import com.puppycrawl.tools.checkstyle.ConfigurationLoader; 040 import com.puppycrawl.tools.checkstyle.PropertiesExpander; 041 import com.puppycrawl.tools.checkstyle.api.Configuration; 042 043 import java.io.ByteArrayInputStream; 044 import java.io.File; 045 import java.io.InputStream; 046 import java.nio.charset.Charset; 047 import java.util.*; 048 049 /** 050 * Squid uses Checkstyle to get an out-of-the-box java parser with AST generation and visitor pattern support. 051 */ 052 public class JavaAstScanner extends CodeScanner<JavaAstVisitor> { 053 054 private static final Logger LOG = LoggerFactory.getLogger(JavaAstScanner.class); 055 private JavaSquidConfiguration conf; 056 private SourceCode project; 057 058 public JavaAstScanner(JavaSquidConfiguration conf, SourceCode project) { 059 this.conf = conf; 060 this.project = project; 061 } 062 063 private Checker createChecker(Charset charset) { 064 InputStream checkstyleConfig = null; 065 try { 066 checkstyleConfig = JavaAstScanner.class.getClassLoader().getResourceAsStream("checkstyle-configuration.xml"); 067 String readenConfig = IOUtils.toString(checkstyleConfig); 068 readenConfig = readenConfig.replace("${charset}", charset.toString()); 069 checkstyleConfig = new ByteArrayInputStream(readenConfig.getBytes()); 070 Configuration config = ConfigurationLoader.loadConfiguration(new InputSource(checkstyleConfig), new PropertiesExpander(System 071 .getProperties()), false); 072 Checker c = new Checker(); 073 final ClassLoader moduleClassLoader = Checker.class.getClassLoader(); 074 c.setModuleClassLoader(moduleClassLoader); 075 c.configure(config); 076 c.addListener(new CheckstyleAuditListener()); 077 return c; 078 079 } catch (Exception e) { // NOSONAR We want to be sure to catch any unexpected exception 080 throw new AnalysisException( 081 "Unable to create Checkstyle Checker object with 'checkstyle-configuration.xml' as Checkstyle configuration file name", e); 082 083 } finally { 084 IOUtils.closeQuietly(checkstyleConfig); 085 } 086 } 087 088 public JavaAstScanner scanDirectory(File javaSourceDirectory) { 089 List<InputFile> inputFiles = Lists.newArrayList(); 090 Collection<File> files = FileUtils.listFiles(javaSourceDirectory, FileFilterUtils.fileFileFilter(), FileFilterUtils.directoryFileFilter()); 091 for (File file : files) { 092 inputFiles.add(InputFileUtils.create(javaSourceDirectory, file)); 093 } 094 return scanFiles(inputFiles); 095 } 096 097 public JavaAstScanner scanFile(InputFile javaFile) { 098 return scanFiles(Arrays.asList(javaFile)); 099 } 100 101 public JavaAstScanner scanFiles(Collection<InputFile> inputFiles) { 102 if (LOG.isDebugEnabled()) { 103 LOG.debug("----- Java sources analyzed by Squid:"); 104 for (InputFile inputFile : inputFiles) { 105 LOG.debug(inputFile.toString()); 106 } 107 LOG.debug("-----"); 108 } 109 110 Stack<SourceCode> resourcesStack = new Stack<SourceCode>(); 111 resourcesStack.add(project); 112 for (JavaAstVisitor visitor : getVisitors()) { 113 visitor.setSourceCodeStack(resourcesStack); 114 } 115 CheckstyleSquidBridge.setASTVisitors(getVisitors()); 116 CheckstyleSquidBridge.setSquidConfiguration(conf); 117 CheckstyleSquidBridge.setInputFiles(inputFiles); 118 launchCheckstyle(InputFileUtils.toFiles(inputFiles), conf.getCharset()); 119 return this; 120 } 121 122 private void launchCheckstyle(Collection<File> files, Charset charset) { 123 Checker c = createChecker(charset); 124 ClassLoader initialClassLoader = Thread.currentThread().getContextClassLoader(); 125 Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); 126 try { 127 c.setClassloader(getClass().getClassLoader()); 128 c.setModuleClassLoader(getClass().getClassLoader()); 129 c.process(Lists.<File>newArrayList(files)); 130 c.destroy(); 131 } finally { 132 Thread.currentThread().setContextClassLoader(initialClassLoader); 133 } 134 } 135 136 137 @Override 138 public Collection<Class<? extends JavaAstVisitor>> getVisitorClasses() { 139 List<Class<? extends JavaAstVisitor>> visitorClasses = Lists.newArrayList(); 140 visitorClasses.add(PackageVisitor.class); 141 visitorClasses.add(FileVisitor.class); 142 visitorClasses.add(ClassVisitor.class); 143 visitorClasses.add(AnonymousInnerClassVisitor.class); 144 visitorClasses.add(MethodVisitor.class); 145 visitorClasses.add(EndAtLineVisitor.class); 146 visitorClasses.add(LinesVisitor.class); 147 visitorClasses.add(BlankLinesVisitor.class); 148 visitorClasses.add(CommentVisitor.class); 149 visitorClasses.add(PublicApiVisitor.class); 150 visitorClasses.add(BranchVisitor.class); 151 visitorClasses.add(StatementVisitor.class); 152 if (conf.isAnalysePropertyAccessors()) { 153 visitorClasses.add(AccessorVisitor.class); 154 } 155 visitorClasses.add(ComplexityVisitor.class); 156 visitorClasses.add(LinesOfCodeVisitor.class); 157 return visitorClasses; 158 } 159 160 @Override 161 public void accept(CodeVisitor visitor) { 162 if (visitor instanceof JavaAstVisitor) { 163 super.accept(visitor); 164 } 165 } 166 }