001 /*
002 * Sonar, open source software quality management tool.
003 * Copyright (C) 2008-2012 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 }