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.pmd;
021
022import com.google.common.annotations.VisibleForTesting;
023import com.google.common.io.Closeables;
024import net.sourceforge.pmd.Report;
025import net.sourceforge.pmd.RuleContext;
026import net.sourceforge.pmd.RuleSet;
027import net.sourceforge.pmd.RuleSetFactory;
028import net.sourceforge.pmd.RuleSets;
029import org.sonar.api.BatchExtension;
030import org.sonar.api.profiles.RulesProfile;
031import org.sonar.api.resources.InputFile;
032import org.sonar.api.resources.Java;
033import org.sonar.api.resources.Project;
034import org.sonar.api.resources.ProjectFileSystem;
035import org.sonar.api.utils.TimeProfiler;
036import org.sonar.java.api.JavaUtils;
037
038import java.io.ByteArrayInputStream;
039import java.io.InputStream;
040import java.nio.charset.Charset;
041import java.util.List;
042
043public class PmdExecutor implements BatchExtension {
044  private final Project project;
045  private final ProjectFileSystem projectFileSystem;
046  private final RulesProfile rulesProfile;
047  private final PmdProfileExporter pmdProfileExporter;
048  private final PmdConfiguration pmdConfiguration;
049
050  public PmdExecutor(Project project, ProjectFileSystem projectFileSystem, RulesProfile rulesProfile, PmdProfileExporter pmdProfileExporter, PmdConfiguration pmdConfiguration) {
051    this.project = project;
052    this.projectFileSystem = projectFileSystem;
053    this.rulesProfile = rulesProfile;
054    this.pmdProfileExporter = pmdProfileExporter;
055    this.pmdConfiguration = pmdConfiguration;
056  }
057
058  public Report execute() {
059    TimeProfiler profiler = new TimeProfiler().start("Execute PMD " + PmdVersion.getVersion());
060
061    ClassLoader initialClassLoader = Thread.currentThread().getContextClassLoader();
062    try {
063      Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
064
065      return executePmd();
066    } finally {
067      Thread.currentThread().setContextClassLoader(initialClassLoader);
068      profiler.stop();
069    }
070  }
071
072  private Report executePmd() {
073    Report report = new Report();
074
075    RuleContext context = new RuleContext();
076    context.setReport(report);
077
078    PmdTemplate pmdFactory = createPmdTemplate();
079    executeRules(pmdFactory, context, projectFileSystem.mainFiles(Java.KEY), PmdConstants.REPOSITORY_KEY);
080    executeRules(pmdFactory, context, projectFileSystem.testFiles(Java.KEY), PmdConstants.TEST_REPOSITORY_KEY);
081
082    pmdConfiguration.dumpXmlReport(report);
083
084    return report;
085  }
086
087  public void executeRules(PmdTemplate pmdFactory, RuleContext ruleContext, List<InputFile> files, String repositoryKey) {
088    if (files.isEmpty()) {
089      return; // Nothing to analyse
090    }
091
092    RuleSets rulesets = createRulesets(repositoryKey);
093    if (rulesets.getAllRules().isEmpty()) {
094      return; // No rule
095    }
096
097    Charset encoding = projectFileSystem.getSourceCharset();
098
099    for (InputFile file : files) {
100      pmdFactory.process(file, encoding, rulesets, ruleContext);
101    }
102  }
103
104  private RuleSets createRulesets(String repositoryKey) {
105    String rulesXml = pmdProfileExporter.exportProfile(repositoryKey, rulesProfile);
106
107    pmdConfiguration.dumpXmlRuleSet(repositoryKey, rulesXml);
108
109    return new RuleSets(readRuleSet(rulesXml));
110  }
111
112  private static RuleSet readRuleSet(String rulesXml) {
113    InputStream rulesInput = null;
114    try {
115      rulesInput = new ByteArrayInputStream(rulesXml.getBytes());
116
117      return new RuleSetFactory().createRuleSet(rulesInput);
118    } finally {
119      Closeables.closeQuietly(rulesInput);
120    }
121  }
122
123  @VisibleForTesting
124  PmdTemplate createPmdTemplate() {
125    return new PmdTemplate(JavaUtils.getSourceVersion(project));
126  }
127}