/*
* Created on 14 avr. 2005
*
* Copyright (c) 2005, PMD for Eclipse Development Team
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * The end-user documentation included with the redistribution, if
* any, must include the following acknowledgement:
* "This product includes software developed in part by support from
* the Defense Advanced Research Project Agency (DARPA)"
* * Neither the name of "PMD for Eclipse Development Team" nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.sourceforge.pmd.eclipse.runtime.cmd;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;
import name.herlin.command.CommandException;
import net.sourceforge.pmd.Report;
import net.sourceforge.pmd.Rule;
import net.sourceforge.pmd.RuleSet;
import net.sourceforge.pmd.eclipse.plugin.PMDPlugin;
import net.sourceforge.pmd.eclipse.runtime.PMDRuntimeConstants;
import net.sourceforge.pmd.eclipse.runtime.builder.MarkerUtil;
import net.sourceforge.pmd.eclipse.util.IOUtil;
import net.sourceforge.pmd.renderers.Renderer;
import net.sourceforge.pmd.util.StringUtil;
import org.apache.log4j.Logger;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IPackageDeclaration;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
/**
* This command produce a report for a project using the specified renderer.
*
* @author Philippe Herlin
*
*/
public class RenderReportsCmd extends AbstractProjectCommand {
private static final long serialVersionUID = 1L;
private static final Logger log = Logger.getLogger(RenderReportsCmd.class);
/**
* Table containing the renderers indexed by the file name.
*/
private Map<String, Renderer> renderers = new HashMap<String, Renderer>();
/**
* Default Constructor
*/
public RenderReportsCmd() {
super("RenderReport", "Produce reports for a project");
setOutputProperties(false);
setReadOnly(false);
setTerminated(false);
}
/**
* Register a renderer and its associated file for processing.
*
* @param renderer the renderer
* @param reportFile the file name where the report will be saved
*/
public void registerRenderer(Renderer renderer, String reportFile) {
if (reportFile != null && renderer != null) {
renderers.put(reportFile, renderer);
}
}
/**
*
* @param report
* @param folder
* @param reportName
* @param renderer
* @throws IOException
* @throws CoreException
*/
private void render(Report report, IFolder folder, String reportName, Renderer renderer) throws IOException, CoreException {
StringWriter writer = new StringWriter();
String reportString = null;
try {
renderer.setWriter(writer);
renderer.start();
renderer.renderFileReport(report);
renderer.end();
reportString = writer.toString();
} finally {
IOUtil.closeQuietly(writer);
}
if (StringUtil.isEmpty(reportString)) {
log.debug("Missing content for report: " + reportName);
return;
}
log.debug(" Creating the report file");
IFile reportFile = folder.getFile(reportName);
InputStream contentsStream = new ByteArrayInputStream(reportString.getBytes());
if (reportFile.exists()) {
reportFile.setContents(contentsStream, true, false, getMonitor());
} else {
reportFile.create(contentsStream, true, getMonitor());
}
reportFile.refreshLocal(IResource.DEPTH_INFINITE, getMonitor());
contentsStream.close();
}
/**
* @see name.herlin.command.AbstractProcessableCommand#execute()
*/
@Override
public void execute() throws CommandException {
try {
log.debug("Starting RenderReport command");
log.debug(" Create a report object");
final Report report = createReport(project());
log.debug(" Getting the report folder");
final IFolder folder = getProjectFolder(PMDRuntimeConstants.REPORT_FOLDER);
if (!folder.exists()) {
folder.create(true, true, getMonitor());
}
for (Map.Entry<String, Renderer> entry: renderers.entrySet()) {
String reportName = entry.getKey();
Renderer renderer = entry.getValue();
log.debug(" Render the report");
render(report, folder, reportName, renderer);
}
} catch (CoreException e) {
log.debug("Core Exception: " + e.getMessage(), e);
throw new CommandException(e);
} catch (IOException e) {
log.debug("Core Exception: " + e.getMessage(), e);
throw new CommandException(e);
} finally {
log.debug("End of RenderReport command");
setTerminated(true);
}
}
/**
* @see name.herlin.command.Command#reset()
*/
@Override
public void reset() {
setProject(null);
renderers = new HashMap<String, Renderer>();
setTerminated(false);
}
/**
* @see name.herlin.command.Command#isReadyToExecute()
*/
@Override
public boolean isReadyToExecute() {
return super.isReadyToExecute() && !renderers.isEmpty();
}
private static void classAndPackageFrom(IMarker marker, FakeRuleViolation violation) throws JavaModelException {
ICompilationUnit unit = JavaCore.createCompilationUnitFrom((IFile) marker.getResource());
IPackageDeclaration[] packages = unit.getPackageDeclarations();
violation.setPackageName(packages.length > 0 ? packages[0].getElementName() : "(default)");
IType[] types = unit.getAllTypes();
violation.setClassName(types.length > 0 ? types[0].getElementName() : marker.getResource().getName());
}
/**
* Create a Report object from the markers of a project
* @param project
* @return
*/
private Report createReport(IProject project) throws CoreException {
Report report = new Report();
IMarker[] markers = MarkerUtil.findAllMarkers(project);
RuleSet ruleSet = PMDPlugin.getDefault().getPreferencesManager().getRuleSet();
boolean isJavaProject = project.hasNature(JavaCore.NATURE_ID);
for (IMarker marker : markers) {
String ruleName = marker.getAttribute(PMDRuntimeConstants.KEY_MARKERATT_RULENAME, "");
Rule rule = ruleSet.getRuleByName(ruleName);
FakeRuleViolation ruleViolation = createViolation(marker, rule);
if (isJavaProject && marker.getResource() instanceof IFile) {
classAndPackageFrom(marker, ruleViolation);
}
report.addRuleViolation(ruleViolation);
}
return report;
}
private static FakeRuleViolation createViolation(IMarker marker, Rule rule) {
// @PMD:REVIEWED:AvoidInstantiatingObjectsInLoops: by Herlin on 01/05/05 19:14
FakeRuleViolation ruleViolation = new FakeRuleViolation(rule);
// Fill in the rule violation object before adding it to the report
ruleViolation.setBeginLine(marker.getAttribute(IMarker.LINE_NUMBER, 0));
ruleViolation.setEndLine(marker.getAttribute(PMDRuntimeConstants.KEY_MARKERATT_LINE2, 0));
ruleViolation.setVariableName(marker.getAttribute(PMDRuntimeConstants.KEY_MARKERATT_LINE2, ""));
ruleViolation.setFilename(marker.getResource().getProjectRelativePath().toString());
ruleViolation.setDescription(marker.getAttribute(IMarker.MESSAGE, rule.getMessage()));
return ruleViolation;
}
}