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 }