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.plugins.squid; 021 022 import org.apache.commons.io.FileUtils; 023 import org.apache.commons.lang.StringUtils; 024 import org.sonar.api.CoreProperties; 025 import org.sonar.api.batch.SensorContext; 026 import org.sonar.api.checks.CheckFactory; 027 import org.sonar.api.checks.NoSonarFilter; 028 import org.sonar.api.resources.InputFile; 029 import org.sonar.api.resources.Project; 030 import org.sonar.api.resources.Resource; 031 import org.sonar.api.utils.TimeProfiler; 032 import org.sonar.java.api.JavaClass; 033 import org.sonar.java.api.JavaMethod; 034 import org.sonar.java.ast.JavaAstScanner; 035 import org.sonar.java.bytecode.BytecodeScanner; 036 import org.sonar.java.squid.JavaSquidConfiguration; 037 import org.sonar.java.squid.SquidScanner; 038 import org.sonar.plugins.squid.bridges.Bridge; 039 import org.sonar.plugins.squid.bridges.BridgeFactory; 040 import org.sonar.plugins.squid.bridges.ResourceIndex; 041 import org.sonar.squid.Squid; 042 import org.sonar.squid.api.*; 043 import org.sonar.squid.indexer.QueryByType; 044 import org.sonar.squid.measures.Metric; 045 046 import java.io.File; 047 import java.nio.charset.Charset; 048 import java.util.Collection; 049 import java.util.List; 050 051 public final class SquidExecutor { 052 053 private Squid squid; 054 private boolean sourceScanned = false; 055 private boolean bytecodeScanned = false; 056 private CheckFactory checkFactory; 057 058 public SquidExecutor(boolean analyzePropertyAccessors, String fieldNamesToExcludeFromLcom4Computation, CheckFactory checkFactory, 059 Charset sourcesCharset) { 060 JavaSquidConfiguration conf = createJavaSquidConfiguration(analyzePropertyAccessors, fieldNamesToExcludeFromLcom4Computation, 061 sourcesCharset); 062 squid = new Squid(conf); 063 this.checkFactory = checkFactory; 064 } 065 066 private JavaSquidConfiguration createJavaSquidConfiguration(boolean analyzePropertyAccessors, 067 String fieldNamesToExcludeFromLcom4Computation, 068 Charset sourcesCharset) { 069 JavaSquidConfiguration conf = new JavaSquidConfiguration(analyzePropertyAccessors, sourcesCharset); 070 071 if (!StringUtils.isBlank(fieldNamesToExcludeFromLcom4Computation)) { 072 for (String fieldName : fieldNamesToExcludeFromLcom4Computation.split(",")) { 073 if (!StringUtils.isBlank(fieldName)) { 074 conf.addFieldToExcludeFromLcom4Calculation(fieldName); 075 } 076 } 077 } 078 return conf; 079 } 080 081 public void scan(Collection<InputFile> sourceFiles, Collection<File> bytecodeFilesOrDirectories) { 082 for (Object checker : checkFactory.getChecks()) { 083 squid.registerVisitor((CodeVisitor) checker); 084 } 085 scanSources(sourceFiles); 086 if (sourceScanned) { 087 scanBytecode(bytecodeFilesOrDirectories); 088 } 089 squid.decorateSourceCodeTreeWith(Metric.values()); 090 scanSquidIndex(); 091 } 092 093 public void save(Project project, SensorContext context, NoSonarFilter noSonarFilter) { 094 if (sourceScanned) { 095 TimeProfiler profiler = new TimeProfiler(getClass()).start("Squid extraction"); 096 ResourceIndex resourceIndex = new ResourceIndex().loadSquidResources(squid, context, project); 097 098 boolean skipPackageDesignAnalysis = CoreProperties.DESIGN_SKIP_PACKAGE_DESIGN_DEFAULT_VALUE; 099 if (project.getConfiguration() != null) { 100 skipPackageDesignAnalysis = project.getConfiguration() 101 .getBoolean(CoreProperties.DESIGN_SKIP_PACKAGE_DESIGN_PROPERTY, CoreProperties.DESIGN_SKIP_PACKAGE_DESIGN_DEFAULT_VALUE); 102 } 103 104 List<Bridge> bridges = BridgeFactory.create(bytecodeScanned, skipPackageDesignAnalysis, context, checkFactory, resourceIndex, squid, noSonarFilter); 105 saveProject(resourceIndex, bridges); 106 savePackages(resourceIndex, bridges); 107 saveFiles(resourceIndex, bridges); 108 saveClasses(resourceIndex, bridges); 109 saveMethods(resourceIndex, bridges); 110 profiler.stop(); 111 } 112 } 113 114 private void saveProject(ResourceIndex resourceIndex, List<Bridge> bridges) { 115 Resource sonarResource = resourceIndex.get(squid.getProject()); 116 for (Bridge bridge : bridges) { 117 bridge.onProject(squid.getProject(), (Project) sonarResource); 118 } 119 } 120 121 private void savePackages(ResourceIndex resourceIndex, List<Bridge> bridges) { 122 Collection<SourceCode> packages = squid.search(new QueryByType(SourcePackage.class)); 123 for (SourceCode squidPackage : packages) { 124 Resource sonarPackage = resourceIndex.get(squidPackage); 125 for (Bridge bridge : bridges) { 126 bridge.onPackage((SourcePackage) squidPackage, sonarPackage); 127 } 128 } 129 } 130 131 private void saveFiles(ResourceIndex resourceIndex, List<Bridge> bridges) { 132 Collection<SourceCode> squidFiles = squid.search(new QueryByType(SourceFile.class)); 133 for (SourceCode squidFile : squidFiles) { 134 Resource sonarFile = resourceIndex.get(squidFile); 135 for (Bridge bridge : bridges) { 136 bridge.onFile((SourceFile) squidFile, sonarFile); 137 } 138 } 139 } 140 141 private void saveClasses(ResourceIndex resourceIndex, List<Bridge> bridges) { 142 Collection<SourceCode> squidClasses = squid.search(new QueryByType(SourceClass.class)); 143 for (SourceCode squidClass : squidClasses) { 144 Resource sonarClass = resourceIndex.get(squidClass); 145 // can be null with anonymous classes 146 if (sonarClass != null) { 147 for (Bridge bridge : bridges) { 148 bridge.onClass((SourceClass) squidClass, (JavaClass) sonarClass); 149 } 150 } 151 } 152 } 153 154 private void saveMethods(ResourceIndex resourceIndex, List<Bridge> bridges) { 155 Collection<SourceCode> squidMethods = squid.search(new QueryByType(SourceMethod.class)); 156 for (SourceCode squidMethod : squidMethods) { 157 JavaMethod sonarMethod = (JavaMethod) resourceIndex.get(squidMethod); 158 if (sonarMethod != null) { 159 for (Bridge bridge : bridges) { 160 bridge.onMethod((SourceMethod) squidMethod, sonarMethod); 161 } 162 } 163 } 164 } 165 166 void scanSources(Collection<InputFile> sourceFiles) { 167 if (sourceFiles != null && !sourceFiles.isEmpty()) { 168 TimeProfiler profiler = new TimeProfiler(getClass()).start("Java AST scan"); 169 JavaAstScanner sourceScanner = squid.register(JavaAstScanner.class); 170 sourceScanner.scanFiles(sourceFiles); 171 sourceScanned = true; 172 profiler.stop(); 173 174 } else { 175 sourceScanned = false; 176 } 177 } 178 179 void scanBytecode(Collection<File> bytecodeFilesOrDirectories) { 180 if (hasBytecode(bytecodeFilesOrDirectories)) { 181 TimeProfiler profiler = new TimeProfiler(getClass()).start("Java bytecode scan"); 182 BytecodeScanner bytecodeScanner = squid.register(BytecodeScanner.class); 183 bytecodeScanner.scan(bytecodeFilesOrDirectories); 184 bytecodeScanned = true; 185 profiler.stop(); 186 } else { 187 bytecodeScanned = false; 188 } 189 } 190 191 static boolean hasBytecode(Collection<File> bytecodeFilesOrDirectories) { 192 if (bytecodeFilesOrDirectories == null) { 193 return false; 194 } 195 for (File bytecodeFilesOrDirectory : bytecodeFilesOrDirectories) { 196 if (bytecodeFilesOrDirectory.exists() && 197 (bytecodeFilesOrDirectory.isFile() || 198 !FileUtils.listFiles(bytecodeFilesOrDirectory, new String[] { "class" }, true).isEmpty())) { 199 return true; 200 } 201 } 202 return false; 203 } 204 205 void scanSquidIndex() { 206 TimeProfiler profiler = new TimeProfiler(getClass()).start("Java Squid scan"); 207 SquidScanner squidScanner = squid.register(SquidScanner.class); 208 squidScanner.scan(); 209 profiler.stop(); 210 } 211 212 boolean isSourceScanned() { 213 return sourceScanned; 214 } 215 216 boolean isBytecodeScanned() { 217 return bytecodeScanned; 218 } 219 220 void flush() { 221 squid.flush(); 222 } 223 224 public Squid getSquid() { 225 return squid; 226 } 227 }