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    }