/*
* Created on 24 nov. 2004
*
* Copyright (c) 2004, 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.properties.impl;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import net.sourceforge.pmd.Rule;
import net.sourceforge.pmd.RuleSet;
import net.sourceforge.pmd.eclipse.plugin.PMDPlugin;
import net.sourceforge.pmd.eclipse.runtime.properties.IProjectProperties;
import net.sourceforge.pmd.eclipse.runtime.properties.IProjectPropertiesManager;
import net.sourceforge.pmd.eclipse.runtime.properties.PropertiesException;
import net.sourceforge.pmd.eclipse.runtime.writer.IRuleSetWriter;
import net.sourceforge.pmd.eclipse.runtime.writer.WriterException;
import net.sourceforge.pmd.eclipse.util.IOUtil;
import net.sourceforge.pmd.util.StringUtil;
import org.apache.log4j.Logger;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.ui.IWorkingSet;
/**
* Implementation of a project properties information structure
*
* @author Philippe Herlin
*
*/
public class ProjectPropertiesImpl implements IProjectProperties {
private static final Logger log = Logger.getLogger(ProjectPropertiesImpl.class);
private static final String PROJECT_RULESET_FILE = ".ruleset";
private final IProjectPropertiesManager projectPropertiesManager;
private final IProject project;
private boolean needRebuild;
private boolean pmdEnabled;
private boolean ruleSetStoredInProject;
private String ruleSetFile;
private RuleSet projectRuleSet;
private long projectRuleFileLastModified = 0;
private IWorkingSet projectWorkingSet;
private boolean includeDerivedFiles;
private boolean violationsAsErrors = true;
private boolean fullBuildEnabled = true; // default in case didn't come from properties
private Set<String> buildPathExcludePatterns = new HashSet<String>();
private Set<String> buildPathIncludePatterns = new HashSet<String>();
/**
* The default constructor takes a project as an argument
*/
public ProjectPropertiesImpl(final IProject project, IProjectPropertiesManager projectPropertiesManager) {
super();
this.project = project;
this.projectPropertiesManager = projectPropertiesManager;
this.projectRuleSet = PMDPlugin.getDefault().getPreferencesManager().getRuleSet();
determineBuildPathIncludesExcludes();
}
/**
* Determines the included and excluded paths configured for the build path of this eclipse project.
*/
private void determineBuildPathIncludesExcludes() {
IClasspathEntry source = PMDPlugin.buildSourceClassPathEntryFor(project);
if (source != null) {
try {
String basePath = new File(project.getWorkspace().getRoot().getLocation().toOSString()
+ java.io.File.separator + source.getPath().toOSString()).getCanonicalPath();
if (!basePath.endsWith(File.separator)) {
basePath += File.separator;
}
if (source.getExclusionPatterns() != null) {
for (IPath path : source.getExclusionPatterns()) {
String pathString = path.toOSString();
if (!pathString.endsWith(File.separator)) {
pathString += File.separator;
}
buildPathExcludePatterns.add(basePath + pathString + ".*");
}
}
if (source.getInclusionPatterns() != null) {
for (IPath path : source.getInclusionPatterns()) {
String pathString = path.toOSString();
if (!pathString.endsWith(File.separator)) {
pathString += File.separator;
}
buildPathIncludePatterns.add(basePath + pathString + ".*");
}
}
} catch (IOException e) {
log.error("Couldn't determine build class path", e);
}
}
}
/**
* @see net.sourceforge.pmd.eclipse.runtime.properties.IProjectProperties#getProject()
*/
public IProject getProject() {
return this.project;
}
/**
* @see net.sourceforge.pmd.eclipse.runtime.properties.IProjectProperties#isPmdEnabled()
*/
public boolean isPmdEnabled() {
return this.pmdEnabled;
}
/**
* @see net.sourceforge.pmd.eclipse.runtime.properties.IProjectProperties#setPmdEnabled(boolean)
*/
public void setPmdEnabled(final boolean pmdEnabled) {
log.debug("Enable PMD for project " + this.project.getName() + ": " + this.pmdEnabled);
if (this.pmdEnabled != pmdEnabled) {
this.pmdEnabled = pmdEnabled;
this.needRebuild |= pmdEnabled;
}
}
/**
* @see net.sourceforge.pmd.eclipse.runtime.properties.IProjectProperties#getProjectRuleSet()
*/
public RuleSet getProjectRuleSet() throws PropertiesException {
return cloneRuleSet();
}
/**
* @see net.sourceforge.pmd.eclipse.runtime.properties.IProjectProperties#setProjectRuleSet(net.sourceforge.pmd.RuleSet)
*/
public void setProjectRuleSet(final RuleSet projectRuleSet) throws PropertiesException {
log.debug("Set a rule set for project " + this.project.getName());
if (projectRuleSet == null) {
throw new PropertiesException("Setting a project rule set to null"); // TODO NLS
}
this.needRebuild |= !this.projectRuleSet.getRules().equals(projectRuleSet.getRules());
this.projectRuleSet = projectRuleSet;
if (this.ruleSetStoredInProject) {
File f = getResolvedRuleSetFile();
if (f != null) {
projectRuleFileLastModified = f.lastModified();
}
}
}
/**
* @see net.sourceforge.pmd.eclipse.runtime.properties.IProjectProperties#isRuleSetStoredInProject()
*/
public boolean isRuleSetStoredInProject() {
return this.ruleSetStoredInProject;
}
/**
* @see net.sourceforge.pmd.eclipse.runtime.properties.IProjectProperties#setRuleSetStoredInProject(boolean)
*/
public void setRuleSetStoredInProject(final boolean ruleSetStoredInProject) throws PropertiesException {
log.debug("Set rule set stored in project for project " + this.project.getName() + ": " + ruleSetStoredInProject);
this.needRebuild |= this.ruleSetStoredInProject != ruleSetStoredInProject;
this.ruleSetStoredInProject = ruleSetStoredInProject;
if (this.ruleSetStoredInProject) {
if (!isRuleSetFileExist()) {
throw new PropertiesException("The project ruleset file cannot be found for project " + this.project.getName()); // TODO NLS
}
File f = getResolvedRuleSetFile();
if (f != null) {
projectRuleFileLastModified = f.lastModified();
}
}
}
/**
* @see net.sourceforge.pmd.eclipse.runtime.properties.IProjectProperties#getRuleSetFile()
*/
public String getRuleSetFile() {
return StringUtil.isEmpty(ruleSetFile) ? PROJECT_RULESET_FILE : ruleSetFile;
}
/**
* @see net.sourceforge.pmd.eclipse.runtime.properties.IProjectProperties#setRuleSetFile(String)
*/
public void setRuleSetFile(String ruleSetFile) throws PropertiesException {
log.debug("Set rule set file for project " + project.getName() + ": " + ruleSetFile);
needRebuild |= this.ruleSetFile == null || !this.ruleSetFile.equals(ruleSetFile);
this.ruleSetFile = ruleSetFile;
if (ruleSetStoredInProject) {
if (!isRuleSetFileExist()) {
throw new PropertiesException("The project ruleset file cannot be found for project " + project.getName()); // TODO NLS
}
File f = getResolvedRuleSetFile();
if (f != null) {
projectRuleFileLastModified = f.lastModified();
}
}
}
/**
* @see net.sourceforge.pmd.eclipse.runtime.properties.IProjectProperties#getProjectWorkingSet()
*/
public IWorkingSet getProjectWorkingSet() {
return this.projectWorkingSet;
}
/**
* @see net.sourceforge.pmd.eclipse.runtime.properties.IProjectProperties#setProjectWorkingSet(org.eclipse.ui.IWorkingSet)
*/
public void setProjectWorkingSet(final IWorkingSet projectWorkingSet) {
log.debug("Set working set for project " + project.getName() + ": "
+ (projectWorkingSet == null ? "none" : projectWorkingSet.getName()));
needRebuild |= projectWorkingSet == null ? this.projectWorkingSet != null:!projectWorkingSet.equals(this.projectWorkingSet);
this.projectWorkingSet = projectWorkingSet;
}
/**
* @see net.sourceforge.pmd.eclipse.runtime.properties.IProjectProperties#isNeedRebuild()
*/
public boolean isNeedRebuild() {
log.debug("Query if project " + project.getName() + " need rebuild : " + (pmdEnabled && needRebuild));
log.debug(" PMD Enabled = " + pmdEnabled);
log.debug(" Project need rebuild = " + needRebuild);
if (ruleSetStoredInProject) {
File f = getResolvedRuleSetFile();
if (f != null) {
needRebuild |= f.lastModified() > projectRuleFileLastModified;
}
}
return pmdEnabled && needRebuild;
}
/**
* @see net.sourceforge.pmd.eclipse.runtime.properties.IProjectProperties#setNeedRebuild()
*/
public void setNeedRebuild(final boolean needRebuild) {
log.debug("Set if rebuild is needed for project " + project.getName() + ": " + needRebuild);
this.needRebuild = needRebuild;
}
/**
* @see net.sourceforge.pmd.eclipse.runtime.properties.IProjectProperties#isRuleSetFileExist()
*/
public final boolean isRuleSetFileExist() {
return getResolvedRuleSetFile().exists();
}
/**
* @see net.sourceforge.pmd.eclipse.runtime.properties.IProjectProperties#getResolvedRuleSetFile()
*/
public File getResolvedRuleSetFile() {
// Check as project-relative path
IFile file = project.getFile(getRuleSetFile());
File f = getExistingFileOrNull(file);
if (f == null) {
// Check as workspace-relative path
IWorkspaceRoot workspaceRoot = project.getWorkspace().getRoot();
try {
IFile workspaceFile = workspaceRoot.getFile(new Path(getRuleSetFile()));
f = getExistingFileOrNull(workspaceFile);
} catch (IllegalArgumentException e) {
// Fall back to below
}
if (f == null) {
// Fall back to file system path
f = new File(getRuleSetFile());
}
}
return f;
}
private File getExistingFileOrNull(IFile file) {
boolean exists = file.exists() && file.isAccessible();
File result = null;
if (exists) {
File f = new File(file.getLocation().toOSString());
// For some reason IFile says exists when it doesn't! So double check.
if (f.exists()) {
result = f;
}
}
return result;
}
/**
* Create a project ruleset file from the current configured rules
*
*/
public void createDefaultRuleSetFile() throws PropertiesException {
log.info("Create a default rule set file for project " + this.project.getName());
ByteArrayOutputStream baos = null;
ByteArrayInputStream bais = null;
try {
IRuleSetWriter writer = PMDPlugin.getDefault().getRuleSetWriter();
baos = new ByteArrayOutputStream();
writer.write(baos, projectRuleSet);
final IFile file = project.getFile(PROJECT_RULESET_FILE);
if (file.exists() && file.isAccessible()) {
throw new PropertiesException("Project ruleset file already exists");
} else {
bais = new ByteArrayInputStream(baos.toByteArray());
file.create(bais, true, null);
}
} catch (WriterException e) {
throw new PropertiesException(e);
} catch (CoreException e) {
throw new PropertiesException(e);
} finally {
IOUtil.closeQuietly(baos);
IOUtil.closeQuietly(bais);
}
}
/**
* @see net.sourceforge.pmd.eclipse.runtime.properties.IProjectProperties#isIncludeDerivedFiles()
*/
public boolean isIncludeDerivedFiles() {
return includeDerivedFiles;
}
/**
* @see net.sourceforge.pmd.eclipse.runtime.properties.IProjectProperties#setIncludeDerivedFiles(boolean)
*/
public void setIncludeDerivedFiles(boolean includeDerivedFiles) {
log.debug("Set if derived files should be included: " + includeDerivedFiles);
this.needRebuild |= this.includeDerivedFiles != includeDerivedFiles;
this.includeDerivedFiles = includeDerivedFiles;
}
/**
* @see net.sourceforge.pmd.eclipse.model.PMDPluginModel#sync()
*/
public void sync() throws PropertiesException {
log.info("Commit properties for project " + project.getName());
projectPropertiesManager.storeProjectProperties(this);
}
/**
* Clone the PMD ruleset.
* @return a pmd ruleSetClone.
*/
private RuleSet cloneRuleSet() {
final RuleSet clonedRuleSet = new RuleSet();
clonedRuleSet.setName(projectRuleSet.getName());
clonedRuleSet.setDescription(projectRuleSet.getDescription());
for (Rule rule: projectRuleSet.getRules()) {
clonedRuleSet.addRule(rule);
}
clonedRuleSet.addExcludePatterns(projectRuleSet.getExcludePatterns());
clonedRuleSet.addIncludePatterns(projectRuleSet.getIncludePatterns());
return clonedRuleSet;
}
/**
* @see net.sourceforge.pmd.eclipse.runtime.properties.IProjectProperties#violationsAsErrors()
*/
public boolean violationsAsErrors() throws PropertiesException {
return this.violationsAsErrors;
}
public void setViolationsAsErrors(boolean violationsAsErrors) throws PropertiesException {
log.debug("Set to handle violations as errors: " + violationsAsErrors);
needRebuild |= this.violationsAsErrors != violationsAsErrors;
this.violationsAsErrors = violationsAsErrors;
}
/**
* @see net.sourceforge.pmd.eclipse.runtime.properties.IProjectProperties#isFullBuildEnabled()
*/
public boolean isFullBuildEnabled() throws PropertiesException {
return fullBuildEnabled;
}
/**
* @see net.sourceforge.pmd.eclipse.runtime.properties.IProjectProperties#setFullBuildEnabled(boolean)
*/
public void setFullBuildEnabled(boolean fullBuildEnabled)
throws PropertiesException {
log.debug("Set if run at full build for project " + project.getName() + ": " + fullBuildEnabled);
if (this.fullBuildEnabled != fullBuildEnabled){
this.fullBuildEnabled = fullBuildEnabled;
if (this.fullBuildEnabled){
needRebuild = true;
}
}
}
/**
* Provide some help to folks using the debugger and logging
*/
public String toString(){
String projectName = "n/a";
String projectRuleSetName = "n/a";
String projectWorkingSetName = "n/a";
if (project != null ){ projectName = project.getName();}
if (projectRuleSet!= null){ projectRuleSetName = projectRuleSet.getName();}
if (projectWorkingSet!=null){ projectWorkingSetName = projectWorkingSet.getName();}
return "fullBuildEnabled:"+fullBuildEnabled
+ " includeDerivedFiles:"+includeDerivedFiles+" pmdEnabled:"+pmdEnabled
+ " project:"+projectName+ " projectRuleSet:"+projectRuleSetName
+ " projectWorkingSet:"+projectWorkingSetName + " ruleSetFile:"+ruleSetFile
+ " ruleSetStoredInProject:"+ruleSetStoredInProject
+ " violationsAsErrors: "+violationsAsErrors;
}
public Set<String> getBuildPathExcludePatterns() {
return buildPathExcludePatterns;
}
public Set<String> getBuildPathIncludePatterns() {
return buildPathIncludePatterns;
}
}