package net.sourceforge.pmd.eclipse.plugin;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import net.sourceforge.pmd.PMDConfiguration;
import net.sourceforge.pmd.RuleSet;
import net.sourceforge.pmd.RuleSetFactory;
import net.sourceforge.pmd.RuleSetNotFoundException;
import net.sourceforge.pmd.eclipse.core.IRuleSetManager;
import net.sourceforge.pmd.eclipse.core.ext.RuleSetsExtensionProcessor;
import net.sourceforge.pmd.eclipse.core.impl.RuleSetManagerImpl;
import net.sourceforge.pmd.eclipse.runtime.cmd.JavaProjectClassLoader;
import net.sourceforge.pmd.eclipse.runtime.preferences.IPreferences;
import net.sourceforge.pmd.eclipse.runtime.preferences.IPreferencesFactory;
import net.sourceforge.pmd.eclipse.runtime.preferences.IPreferencesManager;
import net.sourceforge.pmd.eclipse.runtime.preferences.impl.PreferencesFactoryImpl;
import net.sourceforge.pmd.eclipse.runtime.properties.IProjectProperties;
import net.sourceforge.pmd.eclipse.runtime.properties.IProjectPropertiesManager;
import net.sourceforge.pmd.eclipse.runtime.properties.IPropertiesFactory;
import net.sourceforge.pmd.eclipse.runtime.properties.PropertiesException;
import net.sourceforge.pmd.eclipse.runtime.properties.impl.PropertiesFactoryImpl;
import net.sourceforge.pmd.eclipse.runtime.writer.IAstWriter;
import net.sourceforge.pmd.eclipse.runtime.writer.IRuleSetWriter;
import net.sourceforge.pmd.eclipse.runtime.writer.impl.WriterFactoryImpl;
import net.sourceforge.pmd.eclipse.ui.RuleLabelDecorator;
import net.sourceforge.pmd.eclipse.ui.ShapePainter;
import net.sourceforge.pmd.eclipse.ui.nls.StringKeys;
import net.sourceforge.pmd.eclipse.ui.nls.StringTable;
import net.sourceforge.pmd.eclipse.util.ResourceManager;
import net.sourceforge.pmd.lang.LanguageRegistry;
import net.sourceforge.pmd.lang.LanguageVersion;
import net.sourceforge.pmd.lang.java.JavaLanguageModule;
import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.Layout;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;
import org.apache.log4j.RollingFileAppender;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.ImageRegistry;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IDecoratorManager;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
/**
* The activator class controls the plug-in life cycle
*/
public class PMDPlugin extends AbstractUIPlugin {
private static File pluginFolder;
private FileChangeReviewer changeReviewer;
private Map<RGB, Color> coloursByRGB = new HashMap<RGB, Color>();
public static final String PLUGIN_ID = "net.sourceforge.pmd.eclipse.plugin";
private static Map<IProject, IJavaProject> JavaProjectsByIProject = new HashMap<IProject, IJavaProject>();
// The shared instance
private static PMDPlugin plugin;
public static String VERSION = "unknown";
private static final Integer[] priorityValues = new Integer[] { Integer.valueOf(1), Integer.valueOf(2),
Integer.valueOf(3), Integer.valueOf(4), Integer.valueOf(5) };
private static final Logger log = Logger.getLogger(PMDPlugin.class);
private StringTable stringTable; // NOPMD by Herlin on 11/10/06 00:22
public static final String ROOT_LOG_ID = "net.sourceforge.pmd";
private static final String PMD_ECLIPSE_APPENDER_NAME = "PMDEclipseAppender";
private IPreferencesFactory preferencesFactory = new PreferencesFactoryImpl();
private IPropertiesFactory propertiesFactory = new PropertiesFactoryImpl();
private final IRuleSetManager ruleSetManager = new RuleSetManagerImpl(); // NOPMD:SingularField
/**
* The constructor
*/
public PMDPlugin() {
}
public Color colorFor(RGB rgb) {
Color color = coloursByRGB.get(rgb);
if (color != null)
return color;
color = new Color(null, rgb.red, rgb.green, rgb.blue);
coloursByRGB.put(rgb, color);
return color;
}
public static void setJavaClassLoader(PMDConfiguration config, IProject project) {
IPreferences preferences = getDefault().loadPreferences();
try {
if (preferences.isProjectBuildPathEnabled() && project.hasNature(JavaCore.NATURE_ID)) {
config.setClassLoader(new JavaProjectClassLoader(config.getClass().getClassLoader(), project));
}
} catch (CoreException e) {
throw new RuntimeException(e);
}
}
/**
* Return the Java language version for the resources found within the
* specified project or null if it isn't a Java project or a Java version we
* don't support yet.
*
* @param project
* @return
*/
public static LanguageVersion javaVersionFor(IProject project) {
IJavaProject jProject = JavaProjectsByIProject.get(project);
if (jProject == null) {
jProject = JavaCore.create(project);
JavaProjectsByIProject.put(project, jProject);
}
if (jProject.exists()) {
String compilerCompliance = jProject.getOption(JavaCore.COMPILER_COMPLIANCE, true);
return LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion(compilerCompliance);
}
return null;
}
public static IClasspathEntry buildSourceClassPathEntryFor(IProject project) {
IJavaProject jProject = JavaProjectsByIProject.get(project);
if (jProject == null) {
jProject = JavaCore.create(project);
JavaProjectsByIProject.put(project, jProject);
}
if (jProject.exists()) {
try {
if (jProject.getRawClasspath() != null) {
for (IClasspathEntry entry : jProject.getRawClasspath()) {
if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
return entry;
}
}
}
} catch (JavaModelException e) {
log.error("Couldn't determine source classpath", e);
}
}
return null;
}
private void disposeResources() {
disposeAll(coloursByRGB.values());
}
public static void disposeAll(Collection<Color> colors) {
for (Color color : colors)
color.dispose();
}
public static File getPluginFolder() {
if (pluginFolder == null) {
URL url = Platform.getBundle(PLUGIN_ID).getEntry("/");
try {
url = FileLocator.resolve(url);
} catch (IOException ex) {
ex.printStackTrace();
}
pluginFolder = new File(url.getPath());
}
return pluginFolder;
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext
* )
*/
public void start(BundleContext context) throws Exception {
super.start(context);
plugin = this;
// this needs to be executed before the preferences are loaded, because
// the standard
// rulesets are needed for the default active rules.
registerStandardRuleSets();
IPreferences prefs = loadPreferences();
configureLogs(prefs);
registerAdditionalRuleSets();
fileChangeListenerEnabled(prefs.isCheckAfterSaveEnabled());
// if a project is deleted, remove the cached project properties
ResourcesPlugin.getWorkspace().addResourceChangeListener(new IResourceChangeListener() {
@Override
public void resourceChanged(IResourceChangeEvent arg0) {
if (arg0.getType() == IResourceChangeEvent.PRE_DELETE && arg0.getResource() instanceof IProject) {
getPropertiesManager().removeProjectProperties((IProject) arg0.getResource());
}
}
});
VERSION = context.getBundle().getHeaders().get("Bundle-Version");
}
public void fileChangeListenerEnabled(boolean flag) {
IWorkspace workspace = ResourcesPlugin.getWorkspace();
if (flag) {
if (changeReviewer == null)
changeReviewer = new FileChangeReviewer();
workspace.addResourceChangeListener(changeReviewer);
} else {
if (changeReviewer != null) {
workspace.removeResourceChangeListener(changeReviewer);
changeReviewer = null;
}
}
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext
* )
*/
public void stop(BundleContext context) throws Exception {
fileChangeListenerEnabled(false);
plugin = null;
disposeResources();
ShapePainter.disposeAll();
ResourceManager.dispose();
super.stop(context);
}
/**
* Returns the shared instance
*
* @return the shared instance
*/
public static PMDPlugin getDefault() {
return plugin;
}
/**
* Returns an image descriptor for the image file at the given plug-in
* relative path.
*
* @param path
* the path
* @return the image descriptor
*/
public static ImageDescriptor getImageDescriptor(String path) {
return AbstractUIPlugin.imageDescriptorFromPlugin("net.sourceforge.pmd.eclipse.plugin", path);
}
/**
* Get an image corresponding to the severity
*/
public Image getImage(String key, String iconPath) {
ImageRegistry registry = getImageRegistry();
Image image = registry.get(key);
if (image == null) {
ImageDescriptor descriptor = getImageDescriptor(iconPath);
if (descriptor != null) {
registry.put(key, descriptor);
image = registry.get(key);
}
}
return image;
}
/**
* Helper method to log error
*
* @see IStatus
*/
public void logError(String message, Throwable t) {
getLog().log(new Status(IStatus.ERROR, getBundle().getSymbolicName(), 0, message + t.getMessage(), t));
if (log != null) {
log.error(message, t);
}
}
/**
* Helper method to log error
*
* @see IStatus
*/
public void logError(IStatus status) {
getLog().log(status);
if (log != null) {
log.error(status.getMessage(), status.getException());
}
}
/**
* Helper method to display error
*/
public void showError(final String message, final Throwable t) {
logError(message, t);
Display.getDefault().syncExec(new Runnable() {
public void run() {
String errTitle = getStringTable().getString(StringKeys.ERROR_TITLE);
MessageDialog.openError(Display.getCurrent().getActiveShell(), errTitle,
message + "\n" + String.valueOf(t));
}
});
}
/**
* Helper method to display a non-logged user error
*/
public void showUserError(final String message) {
Display.getDefault().syncExec(new Runnable() {
public void run() {
String errTitle = getStringTable().getString(StringKeys.ERROR_TITLE);
MessageDialog.openError(Display.getCurrent().getActiveShell(), errTitle, message);
}
});
}
/**
* @return an instance of the string table
*/
public StringTable getStringTable() {
if (stringTable == null) {
stringTable = new StringTable();
}
return stringTable;
}
/**
* @return the priority values
* @deprecated
*/
public Integer[] getPriorityValues() {
return priorityValues;
}
/**
* Load the PMD plugin preferences
*/
public IPreferences loadPreferences() {
return getPreferencesManager().loadPreferences();
}
/**
* @return the plugin preferences manager
*/
public IPreferencesManager getPreferencesManager() {
return preferencesFactory.getPreferencesManager();
}
/**
* @return the plugin project properties manager
*/
public IProjectPropertiesManager getPropertiesManager() {
return propertiesFactory.getProjectPropertiesManager();
}
/**
* @param project
* a workspace project
* @return the PMD properties for that project
*/
public IProjectProperties loadProjectProperties(IProject project) throws PropertiesException {
return getPropertiesManager().loadProjectProperties(project);
}
/**
* Helper method to log information
*
* @see IStatus
*/
public void logInformation(String message) {
getLog().log(new Status(IStatus.INFO, getBundle().getSymbolicName(), 0, message, null));
}
public void logWarn(String message) {
getLog().log(new Status(IStatus.WARNING, getBundle().getSymbolicName(), 0, message, null));
}
/**
* @return an instance of an AST writer
*/
public IAstWriter getAstWriter() {
return new WriterFactoryImpl().getAstWriter();
}
/**
* @return an instance of a ruleset writer
*/
public IRuleSetWriter getRuleSetWriter() {
return new WriterFactoryImpl().getRuleSetWriter();
}
/**
* Apply the log preferences
*/
public void applyLogPreferences(IPreferences preferences) {
Logger log = Logger.getLogger(ROOT_LOG_ID);
log.setLevel(preferences.getLogLevel());
RollingFileAppender appender = (RollingFileAppender) log.getAppender(PMD_ECLIPSE_APPENDER_NAME);
if (appender == null) {
configureLogs(preferences);
} else if (!appender.getFile().equals(preferences.getLogFileName())) {
appender.setFile(preferences.getLogFileName());
appender.activateOptions();
}
}
/**
* Configure the logging
*
*/
private void configureLogs(IPreferences preferences) {
try {
Layout layout = new PatternLayout("%d{yyyy/MM/dd HH:mm:ss,SSS} %-5p %-32c{1} %m%n");
RollingFileAppender appender = new RollingFileAppender(layout, preferences.getLogFileName());
appender.setName(PMD_ECLIPSE_APPENDER_NAME);
appender.setMaxBackupIndex(1);
appender.setMaxFileSize("10MB");
Logger.getRootLogger().addAppender(new ConsoleAppender(layout));
Logger.getRootLogger().setLevel(Level.WARN);
Logger.getRootLogger().setAdditivity(false);
Logger.getLogger(ROOT_LOG_ID).addAppender(appender);
Logger.getLogger(ROOT_LOG_ID).setLevel(preferences.getLogLevel());
Logger.getLogger(ROOT_LOG_ID).setAdditivity(false);
} catch (IOException e) {
logError("IO Exception when configuring logging.", e);
}
}
/**
* @return the ruleset manager instance
*/
public final IRuleSetManager getRuleSetManager() {
return ruleSetManager;
}
/**
* Logs inside the Eclipse environment
*
* @param severity
* the severity of the log (IStatus code)
* @param message
* the message to log
* @param t
* a possible throwable, may be null
*/
public final void log(final int severity, final String message, final Throwable t) {
final Bundle bundle = getBundle();
if (bundle != null) {
getLog().log(new Status(severity, bundle.getSymbolicName(), 0, message, t));
}
// TODO : when bundle is not created yet (ie at startup), we cannot log
// ; find a way to log.
}
/**
* Registering the standard rulesets
*
*/
private void registerStandardRuleSets() {
final RuleSetFactory factory = new RuleSetFactory();
try {
Iterator<RuleSet> iterator = factory.getRegisteredRuleSets();
final IRuleSetManager manager = getRuleSetManager();
RuleSet ruleSet;
while (iterator.hasNext()) {
ruleSet = iterator.next();
manager.registerRuleSet(ruleSet);
manager.registerDefaultRuleSet(ruleSet);
}
} catch (RuleSetNotFoundException e) {
log(IStatus.WARNING, "Problem getting all registered PMD RuleSets", e);
}
}
/**
* Register additional rulesets that may be provided by a fragment. Find
* extension points implementation and call them
*
*/
private void registerAdditionalRuleSets() {
try {
final RuleSetsExtensionProcessor processor = new RuleSetsExtensionProcessor(getRuleSetManager());
processor.process();
} catch (CoreException e) {
log(IStatus.ERROR, "Error when processing RuleSets extensions", e);
}
}
public RuleLabelDecorator ruleLabelDecorator() {
IDecoratorManager mgr = getWorkbench().getDecoratorManager();
// TODO don't use a raw string...urgh
return (RuleLabelDecorator) mgr.getBaseLabelProvider("net.sourceforge.pmd.eclipse.plugin.RuleLabelDecorator");
}
public void changedFiles(Collection<IFile> changedFiles) {
RuleLabelDecorator rld = ruleLabelDecorator();
if (rld == null)
return;
Collection<IResource> withParents = new HashSet<IResource>(changedFiles.size() * 2);
withParents.addAll(changedFiles);
for (IFile file : changedFiles) {
IResource parent = file.getParent();
while (parent != null) {
withParents.add(parent);
parent = parent.getParent();
}
}
rld.changed(withParents);
}
private void addFilesTo(IResource resource, Collection<IResource> allKids) {
if (resource instanceof IFile) {
allKids.add(resource);
return;
}
if (resource instanceof IFolder) {
IFolder folder = (IFolder) resource;
IResource[] kids = null;
try {
kids = folder.members();
} catch (CoreException e) {
e.printStackTrace();
}
addKids(allKids, kids);
allKids.add(folder);
return;
}
if (resource instanceof IProject) {
IProject project = (IProject) resource;
IResource[] kids = null;
try {
kids = project.members();
} catch (CoreException e) {
e.printStackTrace();
}
addKids(allKids, kids);
allKids.add(project);
return;
}
}
private void addKids(Collection<IResource> allKids, IResource[] kids) {
if (kids == null)
return;
for (IResource irc : kids) {
if (irc instanceof IFile) {
allKids.add(irc);
continue;
}
if (irc instanceof IFolder) {
addFilesTo(irc, allKids);
}
}
}
public void removedMarkersIn(IResource resource) {
RuleLabelDecorator decorator = ruleLabelDecorator();
if (decorator == null)
return;
Collection<IResource> changes = new ArrayList<IResource>();
addFilesTo(resource, changes);
decorator.changed(changes);
}
}