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 */
020package org.sonar.plugins.squid;
021
022import org.apache.commons.io.FileUtils;
023import org.apache.commons.lang.StringUtils;
024import org.sonar.api.CoreProperties;
025import org.sonar.api.batch.SensorContext;
026import org.sonar.api.checks.CheckFactory;
027import org.sonar.api.checks.NoSonarFilter;
028import org.sonar.api.resources.InputFile;
029import org.sonar.api.resources.Project;
030import org.sonar.api.resources.Resource;
031import org.sonar.api.utils.TimeProfiler;
032import org.sonar.java.api.JavaClass;
033import org.sonar.java.api.JavaMethod;
034import org.sonar.java.ast.JavaAstScanner;
035import org.sonar.java.bytecode.BytecodeScanner;
036import org.sonar.java.squid.JavaSquidConfiguration;
037import org.sonar.java.squid.SquidScanner;
038import org.sonar.plugins.squid.bridges.Bridge;
039import org.sonar.plugins.squid.bridges.BridgeFactory;
040import org.sonar.plugins.squid.bridges.ResourceIndex;
041import org.sonar.squid.Squid;
042import org.sonar.squid.api.*;
043import org.sonar.squid.indexer.QueryByType;
044import org.sonar.squid.measures.Metric;
045
046import java.io.File;
047import java.nio.charset.Charset;
048import java.util.Collection;
049import java.util.List;
050
051public 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}