diff --git a/codan/org.eclipse.cdt.codan.core.cxx/META-INF/MANIFEST.MF b/codan/org.eclipse.cdt.codan.core.cxx/META-INF/MANIFEST.MF index 76a25897400..b1ce271023e 100644 --- a/codan/org.eclipse.cdt.codan.core.cxx/META-INF/MANIFEST.MF +++ b/codan/org.eclipse.cdt.codan.core.cxx/META-INF/MANIFEST.MF @@ -7,15 +7,13 @@ Bundle-Activator: org.eclipse.cdt.codan.core.cxx.Activator Require-Bundle: org.eclipse.core.runtime, org.eclipse.cdt.core, org.eclipse.cdt.codan.core, - org.eclipse.core.resources + org.eclipse.core.resources, + org.eclipse.core.filesystem Bundle-ActivationPolicy: lazy Export-Package: org.eclipse.cdt.codan.core.cxx, - org.eclipse.cdt.codan.core.cxx.internal.model; - x-friends:="org.eclipse.cdt.codan.checkers.ui, - org.eclipse.cdt.codan.ui, - org.eclipse.cdt.codan.ui.cxx", - org.eclipse.cdt.codan.core.cxx.internal.model.cfg; - x-friends:="org.eclipse.cdt.codan.core.test", + org.eclipse.cdt.codan.core.cxx.externaltool, + org.eclipse.cdt.codan.core.cxx.internal.model;x-friends:="org.eclipse.cdt.codan.checkers.ui,org.eclipse.cdt.codan.ui,org.eclipse.cdt.codan.ui.cxx", + org.eclipse.cdt.codan.core.cxx.internal.model.cfg;x-friends:="org.eclipse.cdt.codan.core.test", org.eclipse.cdt.codan.core.cxx.model Bundle-RequiredExecutionEnvironment: JavaSE-1.6 Bundle-Vendor: %Bundle-Vendor diff --git a/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/Activator.java b/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/Activator.java index 5251574a137..168bd3f2d0f 100644 --- a/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/Activator.java +++ b/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/Activator.java @@ -32,7 +32,7 @@ public class Activator extends Plugin { /* * (non-Javadoc) - * + * * @see * org.eclipse.core.runtime.Plugins#start(org.osgi.framework.BundleContext) */ @@ -44,7 +44,7 @@ public class Activator extends Plugin { /* * (non-Javadoc) - * + * * @see * org.eclipse.core.runtime.Plugin#stop(org.osgi.framework.BundleContext) */ @@ -56,7 +56,7 @@ public class Activator extends Plugin { /** * Returns the shared instance - * + * * @return the shared instance */ public static Activator getDefault() { @@ -65,7 +65,7 @@ public class Activator extends Plugin { /** * Logs the specified status with this plug-in's log. - * + * * @param status * status to log */ @@ -74,22 +74,36 @@ public class Activator extends Plugin { } /** - * Logs an internal error with the specified throwable - * - * @param e - * the exception to be logged + * Logs an internal error with the specified {@code Throwable}. + * + * @param t + * the {@code Throwable} to be logged */ - public static void log(Throwable e) { - log(new Status(IStatus.ERROR, PLUGIN_ID, 1, "Internal Error", e)); //$NON-NLS-1$ + public static void log(Throwable t) { + log(new Status(IStatus.ERROR, PLUGIN_ID, 1, "Internal Error", t)); //$NON-NLS-1$ } /** * Logs an internal error with the specified message. - * + * * @param message * the error message to log */ public static void log(String message) { log(new Status(IStatus.ERROR, PLUGIN_ID, 1, message, null)); } + + /** + * Logs an internal error with the specified message and {@code Throwable}. + * + * @param message + * the error message to log + * @param t + * the {@code Throwable} to be logged + * + * @since 2.1 + */ + public static void log(String message, Throwable t) { + log(new Status(IStatus.ERROR, PLUGIN_ID, 1, message, t)); + } } diff --git a/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/externaltool/AbstractExternalToolBasedChecker.java b/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/externaltool/AbstractExternalToolBasedChecker.java new file mode 100644 index 00000000000..6797af3a3ca --- /dev/null +++ b/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/externaltool/AbstractExternalToolBasedChecker.java @@ -0,0 +1,214 @@ +/******************************************************************************* + * Copyright (c) 2012 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Alex Ruiz (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.codan.core.cxx.externaltool; + +import static org.eclipse.cdt.core.ErrorParserContext.CODAN; + +import java.net.URI; + +import org.eclipse.cdt.codan.core.CodanRuntime; +import org.eclipse.cdt.codan.core.cxx.Activator; +import org.eclipse.cdt.codan.core.cxx.internal.externaltool.ExternalToolInvoker; +import org.eclipse.cdt.codan.core.model.AbstractCheckerWithProblemPreferences; +import org.eclipse.cdt.codan.core.model.CheckerLaunchMode; +import org.eclipse.cdt.codan.core.model.IProblem; +import org.eclipse.cdt.codan.core.model.IProblemLocation; +import org.eclipse.cdt.codan.core.model.IProblemLocationFactory; +import org.eclipse.cdt.codan.core.model.IProblemWorkingCopy; +import org.eclipse.cdt.codan.core.param.IProblemPreference; +import org.eclipse.cdt.codan.core.param.MapProblemPreference; +import org.eclipse.cdt.codan.core.param.RootProblemPreference; +import org.eclipse.cdt.codan.core.param.SharedRootProblemPreference; +import org.eclipse.cdt.core.ErrorParserManager; +import org.eclipse.cdt.core.IConsoleParser; +import org.eclipse.cdt.core.IMarkerGenerator; +import org.eclipse.cdt.core.ProblemMarkerInfo; +import org.eclipse.core.filesystem.URIUtil; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; + +/** + * Base class for checkers that invoke external command-line tools to perform code checking. + *

+ * A file, to be processed by this type of checker, must: + *

    + *
  1. be in the current active editor
  2. + *
  3. not have any unsaved changes
  4. + *
+ *

+ * By default, implementations of this checker are not allowed to run while the user types, since + * external tools cannot see unsaved changes. + * + * @since 2.1 + */ +public abstract class AbstractExternalToolBasedChecker extends AbstractCheckerWithProblemPreferences + implements IMarkerGenerator { + private final IInvocationParametersProvider parametersProvider; + private final ArgsSeparator argsSeparator; + private final ConfigurationSettings settings; + private final ExternalToolInvoker externalToolInvoker; + private final RootProblemPreference preferences; + + /** + * Constructor. + * @param settings user-configurable external tool configuration settings. + */ + public AbstractExternalToolBasedChecker(ConfigurationSettings settings) { + this(new InvocationParametersProvider(), new ArgsSeparator(), settings); + } + + /** + * Constructor. + * @param parametersProvider provides the parameters to pass when invoking the external tool. + * @param argsSeparator separates the arguments to pass to the external tool executable. These + * arguments are stored in a single {@code String}. + * @param settings user-configurable external tool configuration settings. + */ + public AbstractExternalToolBasedChecker(IInvocationParametersProvider parametersProvider, + ArgsSeparator argsSeparator, ConfigurationSettings settings) { + this.parametersProvider = parametersProvider; + this.argsSeparator = argsSeparator; + this.settings = settings; + externalToolInvoker = new ExternalToolInvoker(); + preferences = new SharedRootProblemPreference(); + } + + /** + * Returns {@code false} because this checker cannot run "as you type" by default. + * @return {@code false}. + */ + @Override + public boolean runInEditor() { + return false; + } + + @Override + public boolean processResource(IResource resource) { + process(resource); + return false; + } + + private void process(IResource resource) { + try { + InvocationParameters parameters = parametersProvider.createParameters(resource); + if (parameters != null) { + invokeExternalTool(parameters); + } + } catch (Throwable error) { + logResourceProcessingFailure(error, resource); + } + } + + private void invokeExternalTool(InvocationParameters parameters) throws Throwable { + updateConfigurationSettingsFromPreferences(parameters.getActualFile()); + IConsoleParser[] parsers = new IConsoleParser[] { createErrorParserManager(parameters) }; + try { + externalToolInvoker.invoke(parameters, settings, argsSeparator, parsers); + } catch (InvocationFailure error) { + handleInvocationFailure(error, parameters); + } + } + + private void updateConfigurationSettingsFromPreferences(IResource fileToProcess) { + IProblem problem = getProblemById(getReferenceProblemId(), fileToProcess); + MapProblemPreference preferences = (MapProblemPreference) problem.getPreference(); + settings.updateValuesFrom(preferences); + } + + private ErrorParserManager createErrorParserManager(InvocationParameters parameters) { + IProject project = parameters.getActualFile().getProject(); + URI workingDirectory = URIUtil.toURI(parameters.getWorkingDirectory()); + return new ErrorParserManager(project, workingDirectory, this, getParserIDs(), CODAN); + } + + /** + * @return the IDs of the parsers to use to parse the output of the external tool. + */ + protected abstract String[] getParserIDs(); + + /** + * Handles a failure reported when invoking the external tool. This implementation simply + * logs the failure. + * @param error the reported failure. + * @param parameters the parameters passed to the external tool executable. + */ + protected void handleInvocationFailure(InvocationFailure error, InvocationParameters parameters) { + logResourceProcessingFailure(error, parameters.getActualFile()); + } + + private void logResourceProcessingFailure(Throwable error, IResource resource) { + String location = resource.getLocation().toOSString(); + String msg = String.format("Unable to process resource %s", location); //$NON-NLS-1$ + Activator.log(msg, error); + } + + /** + * Returns the id of the problem used as reference to obtain this checker's preferences. All + * preferences in a external-tool-based checker are shared among its defined problems. + * @return the id of the problem used as reference to obtain this checker's preferences. + */ + protected abstract String getReferenceProblemId(); + + @Override + public void initPreferences(IProblemWorkingCopy problem) { + super.initPreferences(problem); + getLaunchModePreference(problem).enableInLaunchModes( + CheckerLaunchMode.RUN_ON_FULL_BUILD, + CheckerLaunchMode.RUN_ON_INC_BUILD, + CheckerLaunchMode.RUN_ON_FILE_SAVE, + CheckerLaunchMode.RUN_ON_DEMAND); + addPreference(problem, settings.getPath()); + addPreference(problem, settings.getArgs()); + } + + private void addPreference(IProblemWorkingCopy problem, SingleConfigurationSetting setting) { + IProblemPreference descriptor = (IProblemPreference) setting.getDescriptor(); + addPreference(problem, descriptor, setting.getDefaultValue()); + } + + @Override + protected void setDefaultPreferenceValue(IProblemWorkingCopy problem, String key, + Object defaultValue) { + MapProblemPreference map = getTopLevelPreference(problem); + map.setChildValue(key, defaultValue); + } + + @Override + public RootProblemPreference getTopLevelPreference(IProblem problem) { + RootProblemPreference map = (RootProblemPreference) problem.getPreference(); + if (map == null) { + map = preferences; + if (problem instanceof IProblemWorkingCopy) { + ((IProblemWorkingCopy) problem).setPreference(map); + } + } + return map; + } + + @Deprecated + @Override + public void addMarker(IResource file, int lineNumber, String description, int severity, + String variableName) { + addMarker(new ProblemMarkerInfo(file, lineNumber, description, severity, variableName)); + } + + @Override + public void addMarker(ProblemMarkerInfo info) { + reportProblem(getReferenceProblemId(), createProblemLocation(info), info.description); + } + + protected IProblemLocation createProblemLocation(ProblemMarkerInfo info) { + IProblemLocationFactory factory = CodanRuntime.getInstance().getProblemLocationFactory(); + return factory.createProblemLocation( + (IFile) info.file, info.startChar, info.endChar, info.lineNumber); + } +} diff --git a/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/externaltool/ArgsSeparator.java b/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/externaltool/ArgsSeparator.java new file mode 100644 index 00000000000..e3b37a83094 --- /dev/null +++ b/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/externaltool/ArgsSeparator.java @@ -0,0 +1,98 @@ +/******************************************************************************* + * Copyright (c) 2012 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Alex Ruiz (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.codan.core.cxx.externaltool; + +import java.util.ArrayList; +import java.util.List; +// import java.util.StringTokenizer; + +/** + * Separates the arguments, stored as a single {@code String}, to pass to an external tool. It uses + * an empty space as the delimiter and supports quoted arguments. + * + * @since 2.1 + */ +public class ArgsSeparator { + private static final char BACKSLASH = '\\'; + private static final char DOUBLE_QUOTE = '"'; + private static final char SINGLE_QUOTE = '\''; + private static final char SPACE = ' '; + + private static final String[] NO_ARGS = {}; + + public String[] splitArguments(String s) { + if (s == null || s.isEmpty()) { + return NO_ARGS; + } + ParserState state = ParserState.NORMAL; + StringBuilder current = new StringBuilder(); + List args = new ArrayList(); + boolean lastTokenInQuotes = false; + char previous = 0; + for (char c : s.toCharArray()) { + switch (state) { + case IN_SINGLE_QUOTE: + if (previous != BACKSLASH && c == SINGLE_QUOTE) { + lastTokenInQuotes = true; + state = ParserState.NORMAL; + } else { + previous = c; + current.append(c); + } + break; + case IN_DOUBLE_QUOTE: + if (previous != BACKSLASH && c == DOUBLE_QUOTE) { + lastTokenInQuotes = true; + state = ParserState.NORMAL; + } else { + previous = c; + current.append(c); + } + break; + default: + switch (c) { + case SINGLE_QUOTE: + if (previous != BACKSLASH) { + state = ParserState.IN_SINGLE_QUOTE; + } + break; + case DOUBLE_QUOTE: + if (previous != BACKSLASH) { + state = ParserState.IN_DOUBLE_QUOTE; + } + break; + case SPACE: + if (lastTokenInQuotes || current.length() != 0) { + args.add(current.toString()); + current.setLength(0); + } + break; + default: + previous = c; + current.append(c); + } + lastTokenInQuotes = false; + break; + } + } + if (lastTokenInQuotes || current.length() != 0) { + args.add(current.toString()); + } + if (state != ParserState.NORMAL) { + throw new IllegalArgumentException("Unbalanced quotes in " + s); //$NON-NLS-1$ + } + return args.toArray(new String[args.size()]); + } + + private static enum ParserState { + NORMAL, IN_SINGLE_QUOTE, IN_DOUBLE_QUOTE; + } +} diff --git a/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/externaltool/ConfigurationSettings.java b/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/externaltool/ConfigurationSettings.java new file mode 100644 index 00000000000..a983225b6c1 --- /dev/null +++ b/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/externaltool/ConfigurationSettings.java @@ -0,0 +1,79 @@ +/******************************************************************************* + * Copyright (c) 2012 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Alex Ruiz (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.codan.core.cxx.externaltool; + +import java.io.File; + +import org.eclipse.cdt.codan.core.cxx.internal.externaltool.ArgsSetting; +import org.eclipse.cdt.codan.core.cxx.internal.externaltool.PathSetting; +import org.eclipse.cdt.codan.core.param.MapProblemPreference; + +/** + * User-configurable external tool settings. + * + * @since 2.1 + */ +public final class ConfigurationSettings { + private final PathSetting path; + private final ArgsSetting args; + private final String externalToolName; + + /** + * Constructor. + * @param externalToolName the name of the external tool. The name of the external tool is + * used in the labels of the settings' input fields. For example, assuming that the external + * tool's name is "Cppcheck", the input field for entering the path of the executable + * will have the label "Cppcheck Path". + * @param defaultPath the default path of the external tool. + * @param defaultArgs the default arguments to pass when invoking the external tool. + */ + public ConfigurationSettings(String externalToolName, File defaultPath, String defaultArgs) { + this.externalToolName = externalToolName; + this.path = new PathSetting(externalToolName, defaultPath); + this.args = new ArgsSetting(externalToolName, defaultArgs); + } + + /** + * Returns the name of the external tool, to be displayed to the user. + * @return the name of the external tool, to be displayed to the user. + */ + public String getExternalToolName() { + return externalToolName; + } + + /** + * Returns the setting that specifies the path and name of the external tool to invoke. + * @return the setting that specifies the path and name of the external tool to invoke. + */ + public SingleConfigurationSetting getPath() { + return path; + } + + /** + * Returns the setting that specifies the arguments to pass when invoking the external tool. + * @return the setting that specifies the arguments to pass when invoking the external tool. + */ + public SingleConfigurationSetting getArgs() { + return args; + } + + /** + * Updates the values of the configuration settings value with the ones stored in the given + * preference map. + * @param preferences the given preference map that may contain the values to set. + * @throws ClassCastException if any of the values to set is not of the same type as the one + * supported by a setting. + */ + public void updateValuesFrom(MapProblemPreference preferences) { + path.updateValue(preferences); + args.updateValue(preferences); + } +} diff --git a/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/externaltool/IInvocationParametersProvider.java b/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/externaltool/IInvocationParametersProvider.java new file mode 100644 index 00000000000..6682a95305e --- /dev/null +++ b/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/externaltool/IInvocationParametersProvider.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * Copyright (c) 2012 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Alex Ruiz (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.codan.core.cxx.externaltool; + +import org.eclipse.core.resources.IResource; + +/** + * Provides the parameters to pass when invoking an external tool. + * + * @since 2.1 + */ +public interface IInvocationParametersProvider { + /** + * Creates the parameters to pass when invoking an external tool. + * @param fileToProcess the file to process. + * @return the created parameters. + * @throws Throwable if something goes wrong. + */ + InvocationParameters createParameters(IResource fileToProcess) throws Throwable; +} diff --git a/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/externaltool/InvocationFailure.java b/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/externaltool/InvocationFailure.java new file mode 100644 index 00000000000..e37a63d13f4 --- /dev/null +++ b/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/externaltool/InvocationFailure.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2012 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Alex Ruiz (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.codan.core.cxx.externaltool; + +/** + * Indicates that invocation of an external tool failed. + * + * @since 2.1 + */ +public class InvocationFailure extends Exception { + private static final long serialVersionUID = 6727101323050538885L; + + /** + * Constructor. + * @param message the detail message. + */ + public InvocationFailure(String message) { + super(message); + } + + /** + * Constructor. + * @param message the detail message. + * @param cause the cause (which is saved for later retrieval by + * the {@link #getCause()} method.) + */ + public InvocationFailure(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/externaltool/InvocationParameters.java b/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/externaltool/InvocationParameters.java new file mode 100644 index 00000000000..74dac3863f8 --- /dev/null +++ b/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/externaltool/InvocationParameters.java @@ -0,0 +1,88 @@ +/******************************************************************************* + * Copyright (c) 2012 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Alex Ruiz (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.codan.core.cxx.externaltool; + +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.IPath; + +/** + * Parameters to pass when invoking an external tool. + * + * @since 2.1 + */ +public final class InvocationParameters { + private final IResource originalFile; + private final IResource actualFile; + private final String actualFilePath; + private final IPath workingDirectory; + + /** + * Constructor. + * @param originalFile the original file to process. + * @param actualFile the actual file to process. + * @param actualFilePath the path of {@code actual}, in a format that the external tool can + * understand. + * @param workingDirectory the directory where the external tool should be executed. + * @see #getOriginalFile() + * @see #getActualFile() + */ + public InvocationParameters(IResource originalFile, IResource actualFile, String actualFilePath, + IPath workingDirectory) { + this.originalFile = originalFile; + this.actualFile = actualFile; + this.actualFilePath = actualFilePath; + this.workingDirectory = workingDirectory; + } + + /** + * Returns the original file to process. This is the file that triggered execution of + * a command-line tool when saved. + * @return the original file to process. + */ + public IResource getOriginalFile() { + return originalFile; + } + + /** + * Returns the actual file to process. It may not be the same as + * {@link #getOriginalFile()}, depending on how the external tool works. + *

+ * A good example is an external tool that can only process C++ source files but not header + * files. If the original file is a header file, the checker could potentially find + * a C++ file that includes such header and use it as the actual file to process. + *

+ *

+ * We still need to keep a reference to the actual file, in order to add markers to + * the editor in case of problems found. + *

+ * @return the actual file to process. + */ + public IResource getActualFile() { + return actualFile; + } + + /** + * Returns the path of {@link #getActualFile()}, in a format the external tool can + * understand. + * @return the path of the actual file to process. + */ + public String getActualFilePath() { + return actualFilePath; + } + + /** + * Returns the directory where the external tool should be executed. + * @return the directory where the external tool should be executed. + */ + public IPath getWorkingDirectory() { + return workingDirectory; + } +} diff --git a/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/externaltool/InvocationParametersProvider.java b/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/externaltool/InvocationParametersProvider.java new file mode 100644 index 00000000000..486ca98d20d --- /dev/null +++ b/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/externaltool/InvocationParametersProvider.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2012 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Alex Ruiz (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.codan.core.cxx.externaltool; + +import org.eclipse.core.resources.IResource; + +/** + * Default implementation of {@link InvocationParameters} + * + * @since 2.1 + */ +public class InvocationParametersProvider implements IInvocationParametersProvider { + /** + * Creates the parameters to pass when invoking an external tool. + *

+ * In this implementation: + *

    + *
  • the actual file to process is the same as the original file
  • + *
  • the path of the actual file is its absolute path in the file system
  • + *
  • the working directory is {@code null}
  • + *
+ * @param fileToProcess the file to process. + * @return the created parameters. + */ + @Override + public InvocationParameters createParameters(IResource fileToProcess) { + String path = fileToProcess.getLocation().toOSString(); + return new InvocationParameters(fileToProcess, fileToProcess, path, null); + } +} diff --git a/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/externaltool/SingleConfigurationSetting.java b/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/externaltool/SingleConfigurationSetting.java new file mode 100644 index 00000000000..8a0db6e63c0 --- /dev/null +++ b/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/externaltool/SingleConfigurationSetting.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2012 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Alex Ruiz (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.codan.core.cxx.externaltool; + +import org.eclipse.cdt.codan.core.param.IProblemPreference; +import org.eclipse.cdt.codan.core.param.IProblemPreferenceDescriptor; +import org.eclipse.cdt.codan.core.param.MapProblemPreference; + +/** + * Single external tool configuration setting. + * @param the type of the value this setting stores. + * + * @noextend This class is not intended to be extended by clients. + * @since 2.1 + */ +public class SingleConfigurationSetting { + private final IProblemPreferenceDescriptor descriptor; + private final T defaultValue; + private final Class valueType; + + private T value; + + /** + * Constructor. + * @param descriptor meta-data that tells the UI how to display this setting. + * @param defaultValue the setting's default value. + * @param valueType the type of the value to store (used for safe casting.) + */ + public SingleConfigurationSetting(IProblemPreferenceDescriptor descriptor, T defaultValue, + Class valueType) { + this.descriptor = descriptor; + this.defaultValue = defaultValue; + this.valueType = valueType; + } + + /** + * Returns this setting's value. + * @return this setting's value. + */ + public T getValue() { + return value; + } + + /** + * Returns the meta-data that tells the UI how to display this setting. + * @return the meta-data that tells the UI how to display this setting. + */ + public IProblemPreferenceDescriptor getDescriptor() { + return descriptor; + } + + /** + * Returns this setting's default value. + * @return this setting's default value. + */ + public T getDefaultValue() { + return defaultValue; + } + + /** + * Updates this setting's value with the one stored in the given preference map. + * @param preferences the given preference map that may contain the value to set. + * @throws ClassCastException if the value to set is not of the same type as the one supported + * by this setting. + */ + public void updateValue(MapProblemPreference preferences) { + IProblemPreference childDescriptor = preferences.getChildDescriptor(descriptor.getKey()); + if (childDescriptor != null) { + value = valueType.cast(childDescriptor.getValue()); + } + } +} diff --git a/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/internal/externaltool/ArgsSetting.java b/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/internal/externaltool/ArgsSetting.java new file mode 100644 index 00000000000..f4ee8ddc7e1 --- /dev/null +++ b/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/internal/externaltool/ArgsSetting.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2012 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Alex Ruiz (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.codan.core.cxx.internal.externaltool; + +import static org.eclipse.cdt.codan.core.cxx.internal.externaltool.Messages.ConfigurationSettings_args_format; +import static org.eclipse.cdt.codan.core.param.IProblemPreferenceDescriptor.PreferenceType.TYPE_STRING; + +import org.eclipse.cdt.codan.core.cxx.externaltool.SingleConfigurationSetting; +import org.eclipse.cdt.codan.core.param.BasicProblemPreference; +import org.eclipse.cdt.codan.core.param.IProblemPreferenceDescriptor; + +/** + * User-configurable setting that specifies the arguments to pass when invoking the external tool. + * The arguments are stored in a single {@code String}. + */ +public class ArgsSetting extends SingleConfigurationSetting { + static final String KEY = "externalToolArgs"; //$NON-NLS-1$ + + /** + * Constructor. + * @param externalToolName the name of the external tool. The name of the external tool is + * used in the label of this setting's input field. + * @param defaultValue the default value of the setting. + */ + public ArgsSetting(String externalToolName, String defaultValue) { + super(newPreferenceDescriptor(externalToolName), defaultValue, String.class); + } + + private static IProblemPreferenceDescriptor newPreferenceDescriptor(String externalToolName) { + String label = String.format(ConfigurationSettings_args_format, externalToolName); + return new BasicProblemPreference(KEY, label, TYPE_STRING); + } +} diff --git a/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/internal/externaltool/Command.java b/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/internal/externaltool/Command.java new file mode 100644 index 00000000000..d5e9ef8d86f --- /dev/null +++ b/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/internal/externaltool/Command.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (c) 2012 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Alex Ruiz (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.codan.core.cxx.internal.externaltool; + +import org.eclipse.core.runtime.IPath; + +/** + * The command to execute to invoke an external tool. + */ +class Command { + private final IPath path; + private final String[] args; + + Command(IPath path, String[] args) { + this.path = path; + this.args = args; + } + + IPath getPath() { + return path; + } + + String[] getArgs() { + return args; + } +} diff --git a/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/internal/externaltool/CommandBuilder.java b/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/internal/externaltool/CommandBuilder.java new file mode 100644 index 00000000000..2e128087367 --- /dev/null +++ b/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/internal/externaltool/CommandBuilder.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2012 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Alex Ruiz (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.codan.core.cxx.internal.externaltool; + +import java.io.File; + +import org.eclipse.cdt.codan.core.cxx.externaltool.ArgsSeparator; +import org.eclipse.cdt.codan.core.cxx.externaltool.ConfigurationSettings; +import org.eclipse.cdt.codan.core.cxx.externaltool.InvocationParameters; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; + +/** + * Creates the command to use to invoke an external tool. + */ +class CommandBuilder { + Command buildCommand(InvocationParameters parameters, ConfigurationSettings settings, + ArgsSeparator argsSeparator) { + IPath executablePath = executablePath(settings); + String[] args = argsToPass(parameters, settings, argsSeparator); + return new Command(executablePath, args); + } + + private IPath executablePath(ConfigurationSettings configurationSettings) { + File executablePath = configurationSettings.getPath().getValue(); + return new Path(executablePath.toString()); + } + + private String[] argsToPass(InvocationParameters parameters, + ConfigurationSettings configurationSettings, ArgsSeparator argsSeparator) { + String actualFilePath = parameters.getActualFilePath(); + String[] args = configuredArgs(configurationSettings, argsSeparator); + return addFilePathToArgs(actualFilePath, args); + } + + private String[] configuredArgs(ConfigurationSettings settings, ArgsSeparator argsSeparator) { + String args = settings.getArgs().getValue(); + return argsSeparator.splitArguments(args); + } + + private String[] addFilePathToArgs(String actualFilePath, String[] configuredArgs) { + int argCount = configuredArgs.length; + String[] allArgs = new String[argCount + 1]; + allArgs[0] = actualFilePath; + // Copy arguments + System.arraycopy(configuredArgs, 0, allArgs, 1, argCount); + return allArgs; + } +} \ No newline at end of file diff --git a/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/internal/externaltool/ExternalToolInvoker.java b/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/internal/externaltool/ExternalToolInvoker.java new file mode 100644 index 00000000000..f8726c488cc --- /dev/null +++ b/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/internal/externaltool/ExternalToolInvoker.java @@ -0,0 +1,98 @@ +/******************************************************************************* + * Copyright (c) 2012 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Alex Ruiz (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.codan.core.cxx.internal.externaltool; + +import org.eclipse.cdt.codan.core.cxx.externaltool.ArgsSeparator; +import org.eclipse.cdt.codan.core.cxx.externaltool.ConfigurationSettings; +import org.eclipse.cdt.codan.core.cxx.externaltool.InvocationFailure; +import org.eclipse.cdt.codan.core.cxx.externaltool.InvocationParameters; +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.CommandLauncher; +import org.eclipse.cdt.core.ICommandLauncher; +import org.eclipse.cdt.core.IConsoleParser; +import org.eclipse.cdt.core.resources.IConsole; +import org.eclipse.cdt.internal.core.ConsoleOutputSniffer; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.NullProgressMonitor; + +/** + * Invokes an external tool to perform checks on a single file. + */ +public class ExternalToolInvoker { + private static final String[] ENV = {}; + private static final NullProgressMonitor NULL_PROGRESS_MONITOR = new NullProgressMonitor(); + + private final CommandBuilder commandBuilder = new CommandBuilder(); + + /** + * Invokes an external tool. + * @param parameters the parameters to pass to the external tool executable. + * @param settings user-configurable settings. + * @param argsSeparator separates the arguments to pass to the external tool executable. These + * arguments are stored in a single {@code String}. + * @param parsers parse the output of the external tool. + * @throws InvocationFailure if the external tool could not be invoked or if the external tool + * itself reports that it cannot be executed (e.g. due to a configuration error). + * @throws Throwable if something else goes wrong. + */ + public void invoke(InvocationParameters parameters, ConfigurationSettings settings, + ArgsSeparator argsSeparator, IConsoleParser[] parsers) + throws InvocationFailure, Throwable { + Command command = commandBuilder.buildCommand(parameters, settings, argsSeparator); + try { + launchCommand(command, parsers, parameters); + } finally { + shutDown(parsers); + } + } + + private void launchCommand(Command command, IConsoleParser[] parsers, + InvocationParameters parameters) throws InvocationFailure, CoreException { + IProject project = parameters.getActualFile().getProject(); + IConsole c = startConsole(project); + ConsoleOutputSniffer sniffer = + new ConsoleOutputSniffer(c.getOutputStream(), c.getErrorStream(), parsers); + ICommandLauncher launcher = commandLauncher(project); + Process p = launcher.execute(command.getPath(), command.getArgs(), ENV, + parameters.getWorkingDirectory(), NULL_PROGRESS_MONITOR); + if (p == null) { + throw new InvocationFailure("Unable to launch external tool. Cause unknown."); //$NON-NLS-1$ + } + try { + p.getOutputStream().close(); + } catch (Throwable ignored) {} + try { + launcher.waitAndRead(sniffer.getOutputStream(), sniffer.getErrorStream(), NULL_PROGRESS_MONITOR); + } finally { + p.destroy(); + } + } + + private IConsole startConsole(IProject project) { + IConsole console = CCorePlugin.getDefault().getConsole(); + console.start(project); + return console; + } + + private ICommandLauncher commandLauncher(IProject project) { + ICommandLauncher launcher = new CommandLauncher(); + launcher.showCommand(true); + launcher.setProject(project); + return launcher; + } + + private void shutDown(IConsoleParser[] parsers) { + for (IConsoleParser parser : parsers) { + parser.shutdown(); + } + } +} diff --git a/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/internal/externaltool/Messages.java b/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/internal/externaltool/Messages.java new file mode 100644 index 00000000000..6cbca3443fa --- /dev/null +++ b/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/internal/externaltool/Messages.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2012 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Alex Ruiz (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.codan.core.cxx.internal.externaltool; + +import org.eclipse.osgi.util.NLS; + +public class Messages extends NLS { + public static String ConfigurationSettings_args_format; + public static String ConfigurationSettings_path_format; + public static String ConfigurationSettings_should_display_output; + + static { + NLS.initializeMessages(Messages.class.getName(), Messages.class); + } + + private Messages() { + } +} diff --git a/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/internal/externaltool/Messages.properties b/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/internal/externaltool/Messages.properties new file mode 100644 index 00000000000..577aec794fd --- /dev/null +++ b/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/internal/externaltool/Messages.properties @@ -0,0 +1,13 @@ +############################################################################### +# Copyright (c) 2012 Google, Inc and others. +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +# +# Contributors: +# Alex Ruiz (Google) - initial API and implementation +############################################################################### +ConfigurationSettings_args_format=%s Args: +ConfigurationSettings_path_format=%s Path: +ConfigurationSettings_should_display_output=Display Output in Console diff --git a/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/internal/externaltool/PathSetting.java b/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/internal/externaltool/PathSetting.java new file mode 100644 index 00000000000..adc458a4039 --- /dev/null +++ b/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/internal/externaltool/PathSetting.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (c) 2012 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Alex Ruiz (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.codan.core.cxx.internal.externaltool; + +import static org.eclipse.cdt.codan.core.cxx.internal.externaltool.Messages.ConfigurationSettings_path_format; +import static org.eclipse.cdt.codan.core.param.IProblemPreferenceDescriptor.PreferenceType.TYPE_FILE; + +import java.io.File; + +import org.eclipse.cdt.codan.core.cxx.externaltool.SingleConfigurationSetting; +import org.eclipse.cdt.codan.core.param.BasicProblemPreference; +import org.eclipse.cdt.codan.core.param.IProblemPreferenceDescriptor; + +/** + * User-configurable setting that specifies the path and name of an external tool's executable. + */ +public class PathSetting extends SingleConfigurationSetting { + static final String KEY = "externalToolPath"; //$NON-NLS-1$ + + /** + * Constructor. + * @param externalToolName the name of the external tool. The name of the external tool is + * used in the label of this setting's input field. + * @param defaultValue the default value of the setting. + */ + public PathSetting(String externalToolName, File defaultValue) { + super(newPreferenceDescriptor(externalToolName), defaultValue, File.class); + } + + private static IProblemPreferenceDescriptor newPreferenceDescriptor(String externalToolName) { + String label = String.format(ConfigurationSettings_path_format, externalToolName); + return new BasicProblemPreference(KEY, label, TYPE_FILE); + } +} diff --git a/codan/org.eclipse.cdt.codan.core.test/src/org/eclipse/cdt/codan/core/cxx/externaltool/ArgsSeparatorTest.java b/codan/org.eclipse.cdt.codan.core.test/src/org/eclipse/cdt/codan/core/cxx/externaltool/ArgsSeparatorTest.java new file mode 100644 index 00000000000..0623c6477a3 --- /dev/null +++ b/codan/org.eclipse.cdt.codan.core.test/src/org/eclipse/cdt/codan/core/cxx/externaltool/ArgsSeparatorTest.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2012 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Alex Ruiz (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.codan.core.cxx.externaltool; + +import static org.junit.Assert.assertArrayEquals; +import junit.framework.TestCase; + +/** + * Tests for {@link ArgsSeparator}. + */ +@SuppressWarnings("nls") +public class ArgsSeparatorTest extends TestCase { + private ArgsSeparator separator; + + @Override + protected void setUp() { + separator = new ArgsSeparator(); + } + + public void testWithSpaceAsDelimiter() { + String[] args = separator.splitArguments("abc def ghi"); + assertArrayEquals(new String[] { "abc", "def", "ghi" }, args); + } + + public void testWithSingleQuote() { + String[] args = separator.splitArguments("abc 'def ghi' jkl"); + assertArrayEquals(new String[] { "abc", "def ghi", "jkl" }, args); + } + + public void testWithDoubleQuote() { + String[] args = separator.splitArguments("abc \"def ghi\" jkl"); + assertArrayEquals(new String[] { "abc", "def ghi", "jkl" }, args); + } + + public void testWithEscapedSingleQuote() { + String[] args = separator.splitArguments("abc 'def \\' ghi' jkl"); + assertArrayEquals(new String[] { "abc", "def \\' ghi", "jkl" }, args); + } + + public void testWithEscapedDoubleQuote() { + String[] args = separator.splitArguments("abc 'def \\\" ghi' jkl"); + assertArrayEquals(new String[] { "abc", "def \\\" ghi", "jkl" }, args); + } +} diff --git a/codan/org.eclipse.cdt.codan.core/META-INF/MANIFEST.MF b/codan/org.eclipse.cdt.codan.core/META-INF/MANIFEST.MF index 332b1beefa8..3690b87d098 100644 --- a/codan/org.eclipse.cdt.codan.core/META-INF/MANIFEST.MF +++ b/codan/org.eclipse.cdt.codan.core/META-INF/MANIFEST.MF @@ -19,7 +19,8 @@ Export-Package: org.eclipse.cdt.codan.core, x-friends:="org.eclipse.cdt.codan.core, org.eclipse.cdt.codan.core.cxx, org.eclipse.cdt.codan.core.test, - org.eclipse.cdt.codan.ui", + org.eclipse.cdt.codan.ui, + org.eclipse.cdt.codan.ui.cxx", org.eclipse.cdt.codan.internal.core.cfg;x-friends:="org.eclipse.cdt.codan.core.cxx", org.eclipse.cdt.codan.internal.core.model; x-friends:="org.eclipse.cdt.codan.core.cxx, diff --git a/codan/org.eclipse.cdt.codan.core/plugin.xml b/codan/org.eclipse.cdt.codan.core/plugin.xml index a3af004ed87..27f4e1f212f 100644 --- a/codan/org.eclipse.cdt.codan.core/plugin.xml +++ b/codan/org.eclipse.cdt.codan.core/plugin.xml @@ -2,6 +2,7 @@ + + + + + + + + + Verifies that a checker should be executed on a given resource in a given launch mode. + + + + + + + + + + + + + + The fully qualified name of this extension point. + + + + + + + ID of the extension point (Simple ID). + + + + + + + Name of the extension point. + + + + + + + + + + + + + Specifies the implementation of ICheckerEnablementVerifier to use. + + + + + + + The implementation of ICheckerEnablementVerifier to use. + + + + + + + + + + + + + + + 2.1 + + + + + + + + + <extension point="org.eclipse.cdt.codan.core.checkerEnablement"> + <verifier class="org.eclipse.cdt.codan.internal.ui.CheckerEnablementVerifier" /> +</extension> + + + + + + + + + Plug-ins that want to extend this extension point must implement org.eclipse.cdt.codan.internal.core.ICheckerEnablementVerifier interface. + + + + + + + + + The default implementation of this extension point is org.eclipse.cdt.codan.internal.ui.CheckerEnablementVerifier. + + + + + diff --git a/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/core/model/AbstractChecker.java b/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/core/model/AbstractChecker.java index 3767d9005d8..374355aedaa 100644 --- a/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/core/model/AbstractChecker.java +++ b/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/core/model/AbstractChecker.java @@ -11,15 +11,15 @@ *******************************************************************************/ package org.eclipse.cdt.codan.core.model; +import java.util.ArrayList; +import java.util.List; + import org.eclipse.cdt.codan.core.CodanRuntime; import org.eclipse.cdt.codan.internal.core.CheckersRegistry; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.OperationCanceledException; -import java.util.ArrayList; -import java.util.List; - /** * Convenience implementation of IChecker interface. Has a default * implementation for common methods. @@ -37,10 +37,7 @@ public abstract class AbstractChecker implements IChecker { public AbstractChecker() { } - /** - * @return true if checker is enabled in context of resource, if returns - * false checker's "processResource" method won't be called - */ + @Deprecated @Override public boolean enabledInContext(IResource res) { return res.getType() == IResource.FILE; diff --git a/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/core/model/CheckerLaunchMode.java b/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/core/model/CheckerLaunchMode.java index 4f1e7559954..677bb75051c 100644 --- a/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/core/model/CheckerLaunchMode.java +++ b/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/core/model/CheckerLaunchMode.java @@ -1,3 +1,14 @@ +/******************************************************************************* + * Copyright (c) 2011, 2012 Alena Laskavaia and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Alena Laskavaia - initial API and implementation + * Alex Ruiz (Google) + *******************************************************************************/ package org.eclipse.cdt.codan.core.model; /** @@ -12,19 +23,25 @@ package org.eclipse.cdt.codan.core.model; */ public enum CheckerLaunchMode { /** - * checker run when full build is running + * Checker runs when full build is running. */ RUN_ON_FULL_BUILD, /** - * checker run when incremental build is running + * Checker runs when incremental build is running. */ RUN_ON_INC_BUILD, /** - * checker run in editor as you type + * Checker runs when a file is saved or opened. Checker will not run if the file is an editor + * with unsaved changes. + * @since 2.1 + */ + RUN_ON_FILE_SAVE, + /** + * Checker runs in editor as you type. */ RUN_AS_YOU_TYPE, /** - * checker run when explicit command is given + * Checker runs when explicit command is given. */ RUN_ON_DEMAND, } \ No newline at end of file diff --git a/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/core/model/IChecker.java b/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/core/model/IChecker.java index 8ef378a970d..3f1e0b66fb6 100644 --- a/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/core/model/IChecker.java +++ b/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/core/model/IChecker.java @@ -72,7 +72,10 @@ public interface IChecker { * * @param resource the resource to run on. * @return true if checker should be run on this resource. + * @deprecated Replaced by {@code CheckersRegistry.isCheckerEnabled((IChecker IResource, CheckerLaunchMode)} + * and {@code ICheckerEnablementVerifier.isCheckerEnabled(IChecker IResource, CheckerLaunchMode)}. */ + @Deprecated boolean enabledInContext(IResource resource); /** diff --git a/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/core/param/SharedRootProblemPreference.java b/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/core/param/SharedRootProblemPreference.java new file mode 100644 index 00000000000..09f6e266a80 --- /dev/null +++ b/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/core/param/SharedRootProblemPreference.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2012 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Alex Ruiz (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.codan.core.param; + +/** + * Preferences that can be shared among several problems. + * + * @since 2.1 + */ +public class SharedRootProblemPreference extends RootProblemPreference { + @Override + public Object clone() { + SharedRootProblemPreference map = (SharedRootProblemPreference) super.clone(); + // alruiz: sharing the internal hash is the only way I could make this work. + map.hash = hash; + return map; + } +} diff --git a/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/internal/core/CheckersRegistry.java b/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/internal/core/CheckersRegistry.java index 1e9a97d723b..ebd6a506424 100644 --- a/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/internal/core/CheckersRegistry.java +++ b/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/internal/core/CheckersRegistry.java @@ -15,6 +15,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; +import java.util.List; import java.util.Map; import org.eclipse.cdt.codan.core.CodanCorePlugin; @@ -51,7 +52,9 @@ import org.osgi.service.prefs.Preferences; public class CheckersRegistry implements Iterable, ICheckersRegistry { private static final String NAME_ATTR = "name"; //$NON-NLS-1$ private static final String ID_ATTR = "id"; //$NON-NLS-1$ - private static final String EXTENSION_POINT_NAME = "checkers"; //$NON-NLS-1$ + private static final String CLASS_ATTR = "class"; //$NON-NLS-1$ + private static final String CHECKERS_EXTENSION_POINT_NAME = "checkers"; //$NON-NLS-1$ + private static final String CHECKER_ENABLEMENT_EXTENSION_POINT_NAME = "checkerEnablement"; //$NON-NLS-1$ private static final String CHECKER_ELEMENT = "checker"; //$NON-NLS-1$ private static final String PROBLEM_ELEMENT = "problem"; //$NON-NLS-1$ private static final String CATEGORY_ELEMENT = "category"; //$NON-NLS-1$ @@ -60,19 +63,21 @@ public class CheckersRegistry implements Iterable, ICheckersRegistry { private Collection checkers = new ArrayList(); private static CheckersRegistry instance; private static boolean initialized = false; - private HashMap profiles = new HashMap(); - private HashMap> problemList = new HashMap>(); - private Map problemCheckerMapping = new HashMap(); + private final Map profiles = new HashMap(); + private final Map> problemList = new HashMap>(); + private final Map problemCheckerMapping = new HashMap(); + private final List checkerEnablementVerifiers = new ArrayList(); private CheckersRegistry() { instance = this; profiles.put(DEFAULT, new ProblemProfile(DEFAULT)); readCheckersRegistry(); + readCheckerEnablementVerifier(); initialized = true; } private void readCheckersRegistry() { - IExtensionPoint ep = Platform.getExtensionRegistry().getExtensionPoint(CodanCorePlugin.PLUGIN_ID, EXTENSION_POINT_NAME); + IExtensionPoint ep = getExtensionPoint(CHECKERS_EXTENSION_POINT_NAME); if (ep == null) return; IConfigurationElement[] elements = ep.getConfigurationElements(); @@ -141,7 +146,7 @@ public class CheckersRegistry implements Iterable, ICheckersRegistry { name = id; IChecker checkerObj = null; try { - Object checker = configurationElement.createExecutableExtension("class"); //$NON-NLS-1$ + Object checker = configurationElement.createExecutableExtension(CLASS_ATTR); checkerObj = (IChecker) checker; addChecker(checkerObj); } catch (CoreException e) { @@ -236,6 +241,21 @@ public class CheckersRegistry implements Iterable, ICheckersRegistry { return elementValue; } + private void readCheckerEnablementVerifier() { + IExtensionPoint ep = getExtensionPoint(CHECKER_ENABLEMENT_EXTENSION_POINT_NAME); + for (IConfigurationElement ce : ep.getConfigurationElements()) { + try { + checkerEnablementVerifiers.add((ICheckerEnablementVerifier) ce.createExecutableExtension(CLASS_ATTR)); + } catch (CoreException e) { + CodanCorePlugin.log(e); + } + } + } + + private IExtensionPoint getExtensionPoint(String extensionPointName) { + return Platform.getExtensionRegistry().getExtensionPoint(CodanCorePlugin.PLUGIN_ID, extensionPointName); + } + /* * (non-Javadoc) * @@ -459,7 +479,15 @@ public class CheckersRegistry implements Iterable, ICheckersRegistry { * @param mode * @return true if the checker should run. */ - public boolean isCheckerEnabledForLaunchMode(IChecker checker, IResource resource, CheckerLaunchMode mode) { + public boolean isCheckerEnabled(IChecker checker, IResource resource, CheckerLaunchMode mode) { + if (resource.getType() != IResource.FILE) { + return false; + } + for (ICheckerEnablementVerifier verifier : checkerEnablementVerifiers) { + if (!verifier.isCheckerEnabled(checker, resource, mode)) { + return false; + } + } IProblemProfile resourceProfile = getResourceProfile(resource); Collection refProblems = getRefProblems(checker); boolean enabled = false; diff --git a/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/internal/core/CodanBuilder.java b/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/internal/core/CodanBuilder.java index 4b712a0d80c..999d80790a8 100644 --- a/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/internal/core/CodanBuilder.java +++ b/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/internal/core/CodanBuilder.java @@ -140,8 +140,7 @@ public class CodanBuilder extends IncrementalProjectBuilder implements ICodanBui if (monitor.isCanceled()) return; if (doesCheckerSupportLaunchMode(checker, checkerLaunchMode) - && checker.enabledInContext(resource) - && chegistry.isCheckerEnabledForLaunchMode(checker, resource, checkerLaunchMode)) { + && chegistry.isCheckerEnabled(checker, resource, checkerLaunchMode)) { synchronized (checker) { try { checker.before(resource); diff --git a/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/internal/core/ICheckerEnablementVerifier.java b/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/internal/core/ICheckerEnablementVerifier.java new file mode 100644 index 00000000000..2a14f8fd491 --- /dev/null +++ b/codan/org.eclipse.cdt.codan.core/src/org/eclipse/cdt/codan/internal/core/ICheckerEnablementVerifier.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2012 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Alex Ruiz (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.codan.internal.core; + +import org.eclipse.cdt.codan.core.model.CheckerLaunchMode; +import org.eclipse.cdt.codan.core.model.IChecker; +import org.eclipse.core.resources.IResource; + +/** + * Verifies that an {@link IChecker} can be invoked. + */ +public interface ICheckerEnablementVerifier { + /** + * Indicates whether the given code checker can be invoked on the given resource in the given + * launch mode. + * @param checker the given code checker. + * @param resource the resource to be checked. + * @param mode the current launch mode. + * @return {@code true} if the given code checker can be invoked, {@code false} otherwise. + */ + public boolean isCheckerEnabled(IChecker checker, IResource resource, CheckerLaunchMode mode); +} diff --git a/codan/org.eclipse.cdt.codan.examples/.classpath b/codan/org.eclipse.cdt.codan.examples/.classpath index 64c5e31b7a2..4c62a8048e6 100644 --- a/codan/org.eclipse.cdt.codan.examples/.classpath +++ b/codan/org.eclipse.cdt.codan.examples/.classpath @@ -1,7 +1,7 @@ - + diff --git a/codan/org.eclipse.cdt.codan.examples/.settings/org.eclipse.jdt.core.prefs b/codan/org.eclipse.cdt.codan.examples/.settings/org.eclipse.jdt.core.prefs index f0c8e22e499..24697a7ea70 100644 --- a/codan/org.eclipse.cdt.codan.examples/.settings/org.eclipse.jdt.core.prefs +++ b/codan/org.eclipse.cdt.codan.examples/.settings/org.eclipse.jdt.core.prefs @@ -1,8 +1,7 @@ -#Wed Feb 23 19:44:01 EST 2011 eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5 -org.eclipse.jdt.core.compiler.compliance=1.5 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.compliance=1.6 org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning org.eclipse.jdt.core.compiler.problem.assertIdentifier=error org.eclipse.jdt.core.compiler.problem.autoboxing=ignore @@ -70,7 +69,7 @@ org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disa org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning -org.eclipse.jdt.core.compiler.source=1.5 +org.eclipse.jdt.core.compiler.source=1.6 org.eclipse.jdt.core.formatter.align_type_members_on_columns=false org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16 org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0 @@ -149,9 +148,12 @@ org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true org.eclipse.jdt.core.formatter.indentation.size=4 +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert -org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert diff --git a/codan/org.eclipse.cdt.codan.examples/META-INF/MANIFEST.MF b/codan/org.eclipse.cdt.codan.examples/META-INF/MANIFEST.MF index 481af5b6365..39ffaa2d7de 100644 --- a/codan/org.eclipse.cdt.codan.examples/META-INF/MANIFEST.MF +++ b/codan/org.eclipse.cdt.codan.examples/META-INF/MANIFEST.MF @@ -11,7 +11,7 @@ Require-Bundle: org.eclipse.core.runtime, org.eclipse.cdt.core, org.eclipse.core.resources, org.eclipse.cdt.codan.ui;bundle-version="1.0.0" -Bundle-RequiredExecutionEnvironment: J2SE-1.5 +Bundle-RequiredExecutionEnvironment: JavaSE-1.6 Bundle-ActivationPolicy: lazy Export-Package: org.eclipse.cdt.codan.examples, org.eclipse.cdt.codan.examples.checkers, diff --git a/codan/org.eclipse.cdt.codan.examples/OSGI-INF/l10n/bundle.properties b/codan/org.eclipse.cdt.codan.examples/OSGI-INF/l10n/bundle.properties index fb989db8b53..430384b2808 100644 --- a/codan/org.eclipse.cdt.codan.examples/OSGI-INF/l10n/bundle.properties +++ b/codan/org.eclipse.cdt.codan.examples/OSGI-INF/l10n/bundle.properties @@ -10,4 +10,15 @@ ############################################################################### #Properties file for org.eclipse.cdt.codan.examples Bundle-Vendor = Eclipse CDT -Bundle-Name = Code Analysis Checker Examples \ No newline at end of file +Bundle-Name = Code Analysis Checker Examples + +checker.name.Cppcheck = Cppcheck +problem.description.Cppcheck.Error = Errors reported by Cppcheck (http://cppcheck.sourceforge.net/) +problem.name.Cppcheck.Error = Errors +problem.description.Cppcheck.Warning = Warnings reported by Cppcheck (http://cppcheck.sourceforge.net/) +problem.name.Cppcheck.Warning = Warnings +problem.description.Cppcheck.Syntax = Syntax problems reported by Cppcheck (http://cppcheck.sourceforge.net/) +problem.name.Cppcheck.Syntax = Syntax Problems +problem.messagePattern.Cppcheck.all = {0} + +errorparser.name.cppcheck = Cppcheck Output Parser \ No newline at end of file diff --git a/codan/org.eclipse.cdt.codan.examples/plugin.xml b/codan/org.eclipse.cdt.codan.examples/plugin.xml index f12f7a82af9..dacd7a972f7 100644 --- a/codan/org.eclipse.cdt.codan.examples/plugin.xml +++ b/codan/org.eclipse.cdt.codan.examples/plugin.xml @@ -42,6 +42,42 @@ name="Search string error"> + + + + + + + + + + @@ -54,4 +90,17 @@ > + + + + + + diff --git a/codan/org.eclipse.cdt.codan.examples/src/org/eclipse/cdt/codan/examples/checkers/cppcheck/CppcheckChecker.java b/codan/org.eclipse.cdt.codan.examples/src/org/eclipse/cdt/codan/examples/checkers/cppcheck/CppcheckChecker.java new file mode 100644 index 00000000000..c82a4d7f74b --- /dev/null +++ b/codan/org.eclipse.cdt.codan.examples/src/org/eclipse/cdt/codan/examples/checkers/cppcheck/CppcheckChecker.java @@ -0,0 +1,69 @@ +/******************************************************************************* + * Copyright (c) 2012 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Alex Ruiz (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.codan.examples.checkers.cppcheck; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.cdt.codan.core.cxx.externaltool.AbstractExternalToolBasedChecker; +import org.eclipse.cdt.codan.core.cxx.externaltool.ConfigurationSettings; +import org.eclipse.cdt.core.ProblemMarkerInfo; + +/** + * Checker that invokes Cppcheck when a C/C++ file is + * saved. + */ +public class CppcheckChecker extends AbstractExternalToolBasedChecker { + private static final String TOOL_NAME = Messages.CppcheckChecker_toolName; + private static final String EXECUTABLE_NAME = "cppcheck"; //$NON-NLS-1$ + private static final String DEFAULT_ARGS = "--enable=all"; //$NON-NLS-1$ + + private static final String DESCRIPTION_FORMAT = "[" + TOOL_NAME + "] %s"; //$NON-NLS-1$ //$NON-NLS-2$ + + private static final String ERROR_PROBLEM_ID; + + // key: severity (error, warning, etc.) - value : problem ID associated to severity + private static final Map PROBLEM_IDS = new HashMap(); + + static { + ERROR_PROBLEM_ID = addProblemId(Severity.ERROR); + addProblemId(Severity.WARNING); + addProblemId(Severity.STYLE); + } + + private static String addProblemId(Severity severity) { + String problemId = "org.eclipse.cdt.codan.checkers.cppcheck." + severity; //$NON-NLS-1$ + PROBLEM_IDS.put(severity, problemId); + return problemId; + } + + public CppcheckChecker() { + super(new ConfigurationSettings(TOOL_NAME, new File(EXECUTABLE_NAME), DEFAULT_ARGS)); + } + + @Override + protected String[] getParserIDs() { + return new String[] { "org.eclipse.cdt.codan.checkers.externaltool.CppcheckChecker" }; //$NON-NLS-1$ + } + + @Override + public void addMarker(ProblemMarkerInfo info) { + Severity severity = Severity.findSeverity(info.severity); + String description = String.format(DESCRIPTION_FORMAT, info.description); + reportProblem(PROBLEM_IDS.get(severity), createProblemLocation(info), description); + } + + @Override + protected String getReferenceProblemId() { + return ERROR_PROBLEM_ID; + } +} diff --git a/codan/org.eclipse.cdt.codan.examples/src/org/eclipse/cdt/codan/examples/checkers/cppcheck/CppcheckOutputParser.java b/codan/org.eclipse.cdt.codan.examples/src/org/eclipse/cdt/codan/examples/checkers/cppcheck/CppcheckOutputParser.java new file mode 100644 index 00000000000..c9ff4ce889e --- /dev/null +++ b/codan/org.eclipse.cdt.codan.examples/src/org/eclipse/cdt/codan/examples/checkers/cppcheck/CppcheckOutputParser.java @@ -0,0 +1,54 @@ +/******************************************************************************* + * Copyright (c) 2012 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Alex Ruiz (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.codan.examples.checkers.cppcheck; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.cdt.core.ErrorParserManager; +import org.eclipse.cdt.core.IErrorParser; +import org.eclipse.cdt.core.ProblemMarkerInfo; +import org.eclipse.core.resources.IFile; + +/** + * Parses the output of Cppcheck. + */ +public class CppcheckOutputParser implements IErrorParser { + // sample line to parse: + // + // [/src/HelloWorld.cpp:19]: (style) The scope of the variable 'i' can be reduced + // ----------1--------- -2 --3-- ------------------4------------------------- + // + // groups: + // 1: file path and name + // 2: line where problem was found + // 3: problem severity + // 4: problem description + private static Pattern pattern = Pattern.compile("\\[(.*):(\\d+)\\]:\\s*\\((.*)\\)\\s*(.*)"); //$NON-NLS-1$ + + @Override + public boolean processLine(String line, ErrorParserManager eoParser) { + Matcher matcher = pattern.matcher(line); + if (!matcher.matches()) { + return false; + } + IFile fileName = eoParser.findFileName(matcher.group(1)); + if (fileName != null) { + int lineNumber = Integer.parseInt(matcher.group(2)); + String description = matcher.group(4); + int severity = Severity.findSeverityCode(matcher.group(3)); + ProblemMarkerInfo info = new ProblemMarkerInfo(fileName, lineNumber, description, severity, null); + eoParser.addProblemMarker(info); + return true; + } + return false; + } +} diff --git a/codan/org.eclipse.cdt.codan.examples/src/org/eclipse/cdt/codan/examples/checkers/cppcheck/Messages.java b/codan/org.eclipse.cdt.codan.examples/src/org/eclipse/cdt/codan/examples/checkers/cppcheck/Messages.java new file mode 100644 index 00000000000..b194e9f2b4f --- /dev/null +++ b/codan/org.eclipse.cdt.codan.examples/src/org/eclipse/cdt/codan/examples/checkers/cppcheck/Messages.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2012 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Alex Ruiz (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.codan.examples.checkers.cppcheck; + +import org.eclipse.osgi.util.NLS; + +public class Messages extends NLS { + public static String CppcheckChecker_toolName; + + static { + // initialize resource bundle + Class clazz = Messages.class; + NLS.initializeMessages(clazz.getName(), clazz); + } + + private Messages() { + } +} diff --git a/codan/org.eclipse.cdt.codan.examples/src/org/eclipse/cdt/codan/examples/checkers/cppcheck/Messages.properties b/codan/org.eclipse.cdt.codan.examples/src/org/eclipse/cdt/codan/examples/checkers/cppcheck/Messages.properties new file mode 100644 index 00000000000..8dda9c9c3ec --- /dev/null +++ b/codan/org.eclipse.cdt.codan.examples/src/org/eclipse/cdt/codan/examples/checkers/cppcheck/Messages.properties @@ -0,0 +1,11 @@ +############################################################################### +# Copyright (c) 2012 Google, Inc and others. +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +# +# Contributors: +# Alex Ruiz (Google) - initial API and implementation +############################################################################### +CppcheckChecker_toolName=Cppcheck diff --git a/codan/org.eclipse.cdt.codan.examples/src/org/eclipse/cdt/codan/examples/checkers/cppcheck/Severity.java b/codan/org.eclipse.cdt.codan.examples/src/org/eclipse/cdt/codan/examples/checkers/cppcheck/Severity.java new file mode 100644 index 00000000000..f8f91cfda46 --- /dev/null +++ b/codan/org.eclipse.cdt.codan.examples/src/org/eclipse/cdt/codan/examples/checkers/cppcheck/Severity.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2012 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Alex Ruiz (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.codan.examples.checkers.cppcheck; + +import org.eclipse.cdt.core.IMarkerGenerator; + +enum Severity { + ERROR(IMarkerGenerator.SEVERITY_ERROR_RESOURCE, "error"), //$NON-NLS-1$ + WARNING(IMarkerGenerator.SEVERITY_WARNING, "warning"), //$NON-NLS-1$ + STYLE(IMarkerGenerator.SEVERITY_INFO, "style"); //$NON-NLS-1$ + + private final int code; + private final String text; + + private Severity(int code, String text) { + this.code = code; + this.text = text; + } + + static int findSeverityCode(String text) { + for (Severity severity : values()) { + if (severity.text.equals(text)) { + return severity.code; + } + } + return STYLE.code; + } + + static Severity findSeverity(int code) { + for (Severity severity : values()) { + if (severity.code == code) { + return severity; + } + } + return STYLE; + } + + @Override + public String toString() { + return text; + } +} \ No newline at end of file diff --git a/codan/org.eclipse.cdt.codan.ui/META-INF/MANIFEST.MF b/codan/org.eclipse.cdt.codan.ui/META-INF/MANIFEST.MF index 9517be08f76..d8980d5d46d 100644 --- a/codan/org.eclipse.cdt.codan.ui/META-INF/MANIFEST.MF +++ b/codan/org.eclipse.cdt.codan.ui/META-INF/MANIFEST.MF @@ -14,7 +14,8 @@ Require-Bundle: org.eclipse.ui, org.eclipse.ui.ide, org.eclipse.cdt.ui, org.eclipse.core.filesystem, - org.eclipse.ui.console + org.eclipse.ui.console, + org.eclipse.ui.editors Bundle-RequiredExecutionEnvironment: JavaSE-1.6 Bundle-ActivationPolicy: lazy Export-Package: org.eclipse.cdt.codan.internal.ui;x-friends:="org.eclipse.cdt.codan.ui.cxx", diff --git a/codan/org.eclipse.cdt.codan.ui/plugin.xml b/codan/org.eclipse.cdt.codan.ui/plugin.xml index 73de0766544..9dbef631ff0 100644 --- a/codan/org.eclipse.cdt.codan.ui/plugin.xml +++ b/codan/org.eclipse.cdt.codan.ui/plugin.xml @@ -238,4 +238,8 @@ + + + diff --git a/codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/CheckerEnablementVerifier.java b/codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/CheckerEnablementVerifier.java new file mode 100644 index 00000000000..114456dc469 --- /dev/null +++ b/codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/CheckerEnablementVerifier.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2012 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Alex Ruiz (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.codan.internal.ui; + +import org.eclipse.cdt.codan.core.model.CheckerLaunchMode; +import org.eclipse.cdt.codan.core.model.IChecker; +import org.eclipse.cdt.codan.internal.core.ICheckerEnablementVerifier; +import org.eclipse.cdt.codan.ui.CodanEditorUtility; +import org.eclipse.core.resources.IResource; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IEditorReference; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.editors.text.TextEditor; + +/** + * Default implementation of {@link ICheckerEnablementVerifier}. + */ +public class CheckerEnablementVerifier implements ICheckerEnablementVerifier { + @Override + public boolean isCheckerEnabled(IChecker checker, IResource resource, CheckerLaunchMode mode) { + if (mode != CheckerLaunchMode.RUN_ON_FILE_SAVE) { + return true; + } + for (IWorkbenchWindow window : PlatformUI.getWorkbench().getWorkbenchWindows()) { + IWorkbenchPage page = window.getActivePage(); + for (IEditorReference reference : page.getEditorReferences()) { + IEditorPart editor = reference.getEditor(false); + if (!CodanEditorUtility.isResourceOpenInEditor(resource, editor)) { + continue; + } + if (editor instanceof TextEditor) { + TextEditor textEditor = (TextEditor) editor; + return !textEditor.isDirty(); + } + } + } + return false; + } +} diff --git a/codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/CodanUIMessages.java b/codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/CodanUIMessages.java index 0ed0f2917ce..cc8c5539b3f 100644 --- a/codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/CodanUIMessages.java +++ b/codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/CodanUIMessages.java @@ -87,6 +87,7 @@ public class CodanUIMessages extends NLS { public static String LaunchModesPropertyPage_RunOnDemand; public static String LaunchModesPropertyPage_RunOnFullBuild; public static String LaunchModesPropertyPage_RunOnIncrementalBuild; + public static String LaunchModesPropertyPage_RunOnFileSave; static { NLS.initializeMessages(CodanUIMessages.class.getName(), CodanUIMessages.class); diff --git a/codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/CodanUIMessages.properties b/codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/CodanUIMessages.properties index 2c02d8329c7..414ab1e20e3 100644 --- a/codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/CodanUIMessages.properties +++ b/codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/CodanUIMessages.properties @@ -96,3 +96,4 @@ LaunchModesPropertyPage_RunAsYouType=Run as you type LaunchModesPropertyPage_RunOnDemand=Run on demand LaunchModesPropertyPage_RunOnFullBuild=Run on full build LaunchModesPropertyPage_RunOnIncrementalBuild=Run on incremental build +LaunchModesPropertyPage_RunOnFileSave=Run on file save or open diff --git a/codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/preferences/LaunchModesPropertyPage.java b/codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/preferences/LaunchModesPropertyPage.java index 380862d445d..733f7c191c7 100644 --- a/codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/preferences/LaunchModesPropertyPage.java +++ b/codan/org.eclipse.cdt.codan.ui/src/org/eclipse/cdt/codan/internal/ui/preferences/LaunchModesPropertyPage.java @@ -37,7 +37,11 @@ public class LaunchModesPropertyPage extends FieldEditorPreferencePage { super(GRID); CheckersRegistry registry = CheckersRegistry.getInstance(); IChecker checker = registry.getCheckerForProblem(problem); - runInEditor = (checker != null) ? Checkers.canCheckerRunAsYouType(checker) : false; + if (checker != null) { + runInEditor = Checkers.canCheckerRunAsYouType(checker); + } else { + runInEditor = false; + } setPreferenceStore(prefStore); editors = new ArrayList(); } @@ -59,6 +63,7 @@ public class LaunchModesPropertyPage extends FieldEditorPreferencePage { addField(new BooleanFieldEditor(CheckerLaunchMode.RUN_ON_FULL_BUILD.name(), CodanUIMessages.LaunchModesPropertyPage_RunOnFullBuild, getFieldEditorParent())); addField(new BooleanFieldEditor(CheckerLaunchMode.RUN_ON_INC_BUILD.name(), CodanUIMessages.LaunchModesPropertyPage_RunOnIncrementalBuild, getFieldEditorParent())); addField(new BooleanFieldEditor(CheckerLaunchMode.RUN_ON_DEMAND.name(), CodanUIMessages.LaunchModesPropertyPage_RunOnDemand, getFieldEditorParent())); + addField(new BooleanFieldEditor(CheckerLaunchMode.RUN_ON_FILE_SAVE.name(), CodanUIMessages.LaunchModesPropertyPage_RunOnFileSave, getFieldEditorParent())); if (runInEditor) { addField(new BooleanFieldEditor(CheckerLaunchMode.RUN_AS_YOU_TYPE.name(), CodanUIMessages.LaunchModesPropertyPage_RunAsYouType, getFieldEditorParent())); } diff --git a/core/org.eclipse.cdt.core/META-INF/MANIFEST.MF b/core/org.eclipse.cdt.core/META-INF/MANIFEST.MF index b4582b3b0ab..9f82f059e8e 100644 --- a/core/org.eclipse.cdt.core/META-INF/MANIFEST.MF +++ b/core/org.eclipse.cdt.core/META-INF/MANIFEST.MF @@ -46,7 +46,8 @@ Export-Package: org.eclipse.cdt.core, org.eclipse.cdt.debug.core, org.eclipse.cdt.managedbuilder.core, org.eclipse.cdt.make.core, - org.eclipse.cdt.make.ui", + org.eclipse.cdt.make.ui, + org.eclipse.cdt.codan.core.cxx", org.eclipse.cdt.internal.core.browser;x-friends:="org.eclipse.cdt.ui", org.eclipse.cdt.internal.core.cdtvariables;x-internal:=true, org.eclipse.cdt.internal.core.dom;x-internal:=true, diff --git a/core/org.eclipse.cdt.core/schema/ErrorParser.exsd b/core/org.eclipse.cdt.core/schema/ErrorParser.exsd index 0ab67eae5a2..d2709fa6939 100644 --- a/core/org.eclipse.cdt.core/schema/ErrorParser.exsd +++ b/core/org.eclipse.cdt.core/schema/ErrorParser.exsd @@ -48,6 +48,7 @@ + @@ -147,6 +148,33 @@ + + + + Use this element to specify the context where an error parser can be used. If none is specified, a default context type will be "build". + +An error parser can be assigned to more than one context type. For example, an error parser can have two "context" elements, one for "build" and one for "codan". + + + + + + + The type of context where an error parser can be used. Valid values are "build" and "codan". + + + + + + + + + + + + + + diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/ErrorParserContext.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/ErrorParserContext.java new file mode 100644 index 00000000000..cbfefa9ba99 --- /dev/null +++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/ErrorParserContext.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2012 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Alex Ruiz (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.core; + +/** + * Indicates the context in which {@link org.eclipse.cdt.core.IErrorParser}s can be + * used. + * + * @since 5.4 + */ +public class ErrorParserContext { + public static final int BUILD = 1 << 0; + public static final int CODAN = 1 << 1; + + public static int getValue(String text) { + if ("build".equals(text)) { //$NON-NLS-1$ + return BUILD; + } + if ("codan".equals(text)) { //$NON-NLS-1$ + return CODAN; + } + throw new IllegalArgumentException("Unknown context value: " + text); //$NON-NLS-1$ + } +} diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/ErrorParserManager.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/ErrorParserManager.java index ee224f5fa4c..9f39ff8e319 100644 --- a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/ErrorParserManager.java +++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/ErrorParserManager.java @@ -14,6 +14,8 @@ *******************************************************************************/ package org.eclipse.cdt.core; +import static org.eclipse.cdt.core.ErrorParserContext.BUILD; + import java.io.IOException; import java.io.OutputStream; import java.net.URI; @@ -73,9 +75,9 @@ public class ErrorParserManager extends OutputStream implements IConsoleParser, private final IMarkerGenerator fMarkerGenerator; private Map fErrorParsers; - private ArrayList fErrors; + private final ArrayList fErrors; - private Vector fDirectoryStack; + private final Vector fDirectoryStack; private final URI fBaseDirectoryURI; private String previousLine; @@ -147,27 +149,46 @@ public class ErrorParserManager extends OutputStream implements IConsoleParser, * @since 5.1 */ public ErrorParserManager(IProject project, URI baseDirectoryURI, IMarkerGenerator markerGenerator, String[] parsersIDs) { + this(project, baseDirectoryURI, markerGenerator, parsersIDs, BUILD); + } + + /** + * URI based constructor. + * + * @param project - project being built. + * @param baseDirectoryURI - absolute location URI of working directory of where the build is performed. + * @param markerGenerator - marker generator able to create markers. + * @param parsersIDs - array of error parsers' IDs. + * @param context - context where the error parser will be used. Valid values are defined by + * {@link ErrorParserContext}. + * @see ErrorParserContext + * @since 5.4 + */ + public ErrorParserManager(IProject project, URI baseDirectoryURI, + IMarkerGenerator markerGenerator, String[] parsersIDs, int context) { fProject = project; fMarkerGenerator = markerGenerator; fDirectoryStack = new Vector(); fErrors = new ArrayList(); - enableErrorParsers(parsersIDs); + enableErrorParsers(parsersIDs, context); - if (baseDirectoryURI != null) + if (baseDirectoryURI != null) { fBaseDirectoryURI = baseDirectoryURI; - else if (project != null) + } else if (project != null) { fBaseDirectoryURI = project.getLocationURI(); - else + } + else { fBaseDirectoryURI = org.eclipse.core.filesystem.URIUtil.toURI(System.getProperty("user.dir")); // CWD //$NON-NLS-1$ + } } - private void enableErrorParsers(String[] parsersIDs) { + private void enableErrorParsers(String[] parsersIDs, int context) { if (parsersIDs == null) { parsersIDs = ErrorParserExtensionManager.getDefaultErrorParserIds(); } fErrorParsers = new LinkedHashMap(parsersIDs.length); for (String parsersID : parsersIDs) { - IErrorParser errorParser = getErrorParserCopy(parsersID); + IErrorParser errorParser = getErrorParserCopy(parsersID, context); if (errorParser!=null) { fErrorParsers.put(parsersID, new IErrorParser[] {errorParser} ); } @@ -196,8 +217,9 @@ public class ErrorParserManager extends OutputStream implements IConsoleParser, */ @Override public URI getWorkingDirectoryURI() { - if (!fDirectoryStack.isEmpty()) + if (!fDirectoryStack.isEmpty()) { return fDirectoryStack.lastElement(); + } // Fall back to the Project Location / Build directory return fBaseDirectoryURI; @@ -214,12 +236,13 @@ public class ErrorParserManager extends OutputStream implements IConsoleParser, if (dir != null) { URI uri; URI workingDirectoryURI = getWorkingDirectoryURI(); - if (!dir.isAbsolute()) + if (!dir.isAbsolute()) { uri = URIUtil.append(workingDirectoryURI, dir.toString()); - else { + } else { uri = toURI(dir); - if (uri == null) // Shouldn't happen; error logged + if (uri == null) { return; + } } pushDirectoryURI(uri); } @@ -235,10 +258,11 @@ public class ErrorParserManager extends OutputStream implements IConsoleParser, */ public void pushDirectoryURI(URI dir) { if (dir != null) { - if (dir.isAbsolute()) + if (dir.isAbsolute()) { fDirectoryStack.addElement(dir); - else + } else { fDirectoryStack.addElement(URIUtil.makeAbsolute(dir, getWorkingDirectoryURI())); + } } } @@ -331,8 +355,9 @@ outer: } if ((types & IErrorParser2.KEEP_LONGLINES) == 0) { // long lines are not given to parsers, unless it wants it - if (lineTrimmed.length() > 1000) + if (lineTrimmed.length() > 1000) { continue; + } } // standard behavior (pre 5.1) is to trim the line String lineToParse = lineTrimmed; @@ -349,21 +374,24 @@ outer: consume = curr.processLine(lineToParse, this); } catch (Exception e){ String id = ""; //$NON-NLS-1$ - if (parser instanceof IErrorParserNamed) + if (parser instanceof IErrorParserNamed) { id = ((IErrorParserNamed)parser).getId(); + } @SuppressWarnings("nls") String message = "Errorparser " + id + " failed parsing line [" + lineToParse + "]"; CCorePlugin.log(message, e); } finally { if (fErrors.size() > 0) { - if (marker==null) + if (marker==null) { marker = fErrors.get(0); + } fErrors.clear(); } } - if (consume) + if (consume) { break outer; + } } } outputLine(line, marker); @@ -377,7 +405,9 @@ outer: */ private void outputLine(String line, ProblemMarkerInfo marker) { String l = line + "\n"; //$NON-NLS-1$ - if ( outputStream == null ) return; + if ( outputStream == null ) { + return; + } try { if ( marker != null && outputStream instanceof IErrorMarkeredOutputStream ) { IErrorMarkeredOutputStream s = (IErrorMarkeredOutputStream) outputStream; @@ -417,8 +447,9 @@ outer: */ public IFile findFileName(String partialLoc) { if (partialLoc.equals(cachedFileName) && cachedWorkingDirectory != null && - org.eclipse.core.filesystem.URIUtil.equals(getWorkingDirectoryURI(), cachedWorkingDirectory)) + org.eclipse.core.filesystem.URIUtil.equals(getWorkingDirectoryURI(), cachedWorkingDirectory)) { return cachedFile; + } // To be able to parse Windows paths on Linux systems, see bug 263977 IPath path = new Path(partialLoc.replace('\\', IPath.SEPARATOR)); @@ -433,17 +464,20 @@ outer: if (fProject != null) { IProject[] prjs = new IProject[] { fProject }; files = ResourceLookup.findFilesByName(path, prjs, false); - if (files.length == 0) + if (files.length == 0) { files = ResourceLookup.findFilesByName(path, prjs, /* ignoreCase */ true); + } } if (files == null || files.length == 0) { IProject[] prjs = ResourcesPlugin.getWorkspace().getRoot().getProjects(); files = ResourceLookup.findFilesByName(path, prjs, false); - if (files.length == 0) + if (files.length == 0) { files = ResourceLookup.findFilesByName(path, prjs, /* ignoreCase */ true); + } } - if (files.length == 1) + if (files.length == 1) { file = files[0]; + } } // Could be cygwin path @@ -471,8 +505,9 @@ outer: } else { uri = toURI(path); - if (uri == null) // Shouldn't happen; error logged + if (uri == null) { return null; + } } return findFileInWorkspace(uri); } @@ -485,12 +520,14 @@ outer: * @since 5.1 */ protected IFile findFileInWorkspace(URI uri) { - if (!uri.isAbsolute()) + if (!uri.isAbsolute()) { uri = URIUtil.makeAbsolute(uri, getWorkingDirectoryURI()); + } IFile f = ResourceLookup.selectFileForLocationURI(uri, fProject); - if (f != null && f.isAccessible()) + if (f != null && f.isAccessible()) { return f; + } return null; } @@ -581,8 +618,9 @@ outer: public void addProblemMarker(ProblemMarkerInfo problemMarkerInfo){ fErrors.add(problemMarkerInfo); fMarkerGenerator.addMarker(problemMarkerInfo); - if (problemMarkerInfo.severity == IMarkerGenerator.SEVERITY_ERROR_RESOURCE) + if (problemMarkerInfo.severity == IMarkerGenerator.SEVERITY_ERROR_RESOURCE) { hasErrors = true; + } } /** @@ -632,8 +670,9 @@ outer: */ @Override public void flush() throws IOException { - if (outputStream != null) + if (outputStream != null) { outputStream.flush(); + } } /** @@ -668,8 +707,9 @@ outer: while ((i = buffer.indexOf('\n')) != -1) { String line = buffer.substring(0, i); // get rid of any trailing '\r' - if (line.endsWith("\r")) //$NON-NLS-1$ + if (line.endsWith("\r")) { //$NON-NLS-1$ line=line.substring(0,line.length()-1); + } processLine(line); previousLine = line; buffer = buffer.substring(i + 1); // skip the \n and advance @@ -718,8 +758,9 @@ outer: String uriString = path.toString(); // On Windows "C:/folder/" -> "/C:/folder/" - if (path.isAbsolute() && uriString.charAt(0) != IPath.SEPARATOR) - uriString = IPath.SEPARATOR + uriString; + if (path.isAbsolute() && uriString.charAt(0) != IPath.SEPARATOR) { + uriString = IPath.SEPARATOR + uriString; + } return EFSExtensionManager.getDefault().createNewURIFromPath(baseURI, uriString); } @@ -829,6 +870,20 @@ outer: return ErrorParserExtensionManager.getErrorParserCopy(id, false); } + /** + * @param id - ID of error parser + * @param context - context where the error parser will be used. Valid values are defined by + * {@link ErrorParserContext}. + * @return cloned copy of error parser or {@code null}. + * Note that {@link ErrorParserNamedWrapper} returns shallow copy with the same instance + * of underlying error parser. + * @see ErrorParserContext + * @since 5.4 + */ + public static IErrorParserNamed getErrorParserCopy(String id, int context) { + return ErrorParserExtensionManager.getErrorParserCopy(id, false, context); + } + /** * @param id - ID of error parser * @return cloned copy of error parser as defined by its extension point or {@code null}. @@ -860,5 +915,12 @@ outer: */ @Override public void shutdown() { + for (IErrorParser[] parsers : fErrorParsers.values()) { + for (IErrorParser parser : parsers) { + if (parser instanceof IErrorParser3) { + ((IErrorParser3) parser).streamFinished(); + } + } + } } } diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/IErrorParser3.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/IErrorParser3.java new file mode 100644 index 00000000000..61bfda836ba --- /dev/null +++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/IErrorParser3.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * Copyright (c) 2012 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Alex Ruiz (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.core; + +/** + * @since 5.4 + */ +public interface IErrorParser3 extends IErrorParser2 { + /** + * Notification that the stream of data to parse has ended. + */ + void streamFinished(); +} diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/ProblemMarkerInfo.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/ProblemMarkerInfo.java index b44f270c0b5..a81a635054b 100644 --- a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/ProblemMarkerInfo.java +++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/ProblemMarkerInfo.java @@ -35,6 +35,14 @@ public class ProblemMarkerInfo { public IResource file; public int lineNumber; + /** + * @since 5.4 + */ + public int startChar; + /** + * @since 5.4 + */ + public int endChar; public String description; public int severity; public String variableName; @@ -53,6 +61,24 @@ public class ProblemMarkerInfo { * @param variableName - the name of the variable involved in the error if any. */ public ProblemMarkerInfo(IResource file, int lineNumber, String description, int severity, String variableName) { + this(file, lineNumber, -1,-1, description, severity, variableName); + } + + /** + * Create a new {@link ProblemMarkerInfo} object. + * + * @param file - the file where the problem has occurred. + * @param lineNumber - the line number of the problem. + * @param startChar - start char of the problem. + * @param endChar - end char of the problem. + * @param description - a description of the problem. + * @param severity - the severity of the problem, see {@link IMarkerGenerator} + * for acceptable severity values. + * @param variableName - the name of the variable involved in the error if any. + * @since 5.4 + */ + public ProblemMarkerInfo(IResource file, int lineNumber, int startChar, int endChar, + String description, int severity, String variableName) { this.file = (file != null) ? file : ResourcesPlugin.getWorkspace().getRoot(); this.lineNumber = lineNumber; this.description = description; @@ -61,6 +87,8 @@ public class ProblemMarkerInfo { this.externalPath = null ; this.type = null; this.attributes = new HashMap(); + this.startChar = -1; + this.endChar = -1; } /** diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/errorparsers/ErrorParserExtensionManager.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/errorparsers/ErrorParserExtensionManager.java index df2a4d6c5bd..b3b58aa4a1a 100644 --- a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/errorparsers/ErrorParserExtensionManager.java +++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/errorparsers/ErrorParserExtensionManager.java @@ -11,24 +11,29 @@ package org.eclipse.cdt.internal.errorparsers; +import static org.eclipse.cdt.core.ErrorParserContext.BUILD; + import java.net.URI; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.TreeSet; import org.eclipse.cdt.core.CCorePlugin; import org.eclipse.cdt.core.ErrorParserManager; +import org.eclipse.cdt.core.ErrorParserContext; import org.eclipse.cdt.core.IErrorParser; import org.eclipse.cdt.core.IErrorParserNamed; import org.eclipse.cdt.core.IMarkerGenerator; import org.eclipse.cdt.core.errorparsers.ErrorParserNamedWrapper; import org.eclipse.cdt.core.errorparsers.RegexErrorParser; import org.eclipse.cdt.core.errorparsers.RegexErrorPattern; +import org.eclipse.cdt.internal.core.Pair; import org.eclipse.cdt.internal.core.XmlUtil; import org.eclipse.core.filesystem.URIUtil; import org.eclipse.core.runtime.CoreException; @@ -56,6 +61,7 @@ public class ErrorParserExtensionManager { private static final String NONE = ""; //$NON-NLS-1$ private static final String EXTENSION_POINT_ERROR_PARSER = "org.eclipse.cdt.core.ErrorParser"; //$NON-NLS-1$ + private static final String ELEM_CONTEXT = "context"; //$NON-NLS-1$ private static final String ELEM_PLUGIN = "plugin"; //$NON-NLS-1$ private static final String ELEM_EXTENSION = "extension"; //$NON-NLS-1$ private static final String ELEM_ERRORPARSER = "errorparser"; //$NON-NLS-1$ @@ -64,7 +70,9 @@ public class ErrorParserExtensionManager { private static final String ATTR_ID = "id"; //$NON-NLS-1$ private static final String ATTR_NAME = "name"; //$NON-NLS-1$ private static final String ATTR_POINT = "point"; //$NON-NLS-1$ + private static final String ATTR_TYPE = "type"; //$NON-NLS-1$ + private static final String ATTR_REGEX = "regex"; //$NON-NLS-1$ private static final String ATTR_SEVERITY = "severity"; //$NON-NLS-1$ private static final String ATTR_FILE = "file-expr"; //$NON-NLS-1$ @@ -78,8 +86,10 @@ public class ErrorParserExtensionManager { private static final String ATTR_VALUE_INFO = "Info"; //$NON-NLS-1$ private static final String ATTR_VALUE_IGNORE = "Ignore"; //$NON-NLS-1$ - private static final LinkedHashMap fExtensionErrorParsers = new LinkedHashMap(); - private static final LinkedHashMap fAvailableErrorParsers = new LinkedHashMap(); + // Key: error parser id. Value: pair of error parser and contexts value + private static final LinkedHashMap> fExtensionErrorParsers = new LinkedHashMap>(); + // Key: error parser id. Value: pair of error parser and contexts value + private static final LinkedHashMap> fAvailableErrorParsers = new LinkedHashMap>(); private static LinkedHashMap fUserDefinedErrorParsers = null; private static List fDefaultErrorParserIds = null; @@ -112,6 +122,18 @@ public class ErrorParserExtensionManager { } } + private static class ErrorParserAndContextComparator implements Comparator> { + private static final ErrorParserComparator DELEGATE = new ErrorParserComparator(); + + @Override + public int compare(Pair pair1, + Pair pair2) { + IErrorParserNamed parser1 = pair1.first; + IErrorParserNamed parser2 = pair2.first; + return DELEGATE.compare(parser1, parser2); + } + } + static { loadUserDefinedErrorParsers(); loadDefaultErrorParserIds(); @@ -202,12 +224,13 @@ public class ErrorParserExtensionManager { * @noreference This method is not intended to be referenced by clients. */ synchronized public static void loadErrorParserExtensions() { - Set sortedErrorParsers = new TreeSet(new ErrorParserComparator()); + Set> sortedErrorParsers = + new TreeSet>(new ErrorParserAndContextComparator()); loadErrorParserExtensions(Platform.getExtensionRegistry(), sortedErrorParsers); fExtensionErrorParsers.clear(); - for (IErrorParserNamed errorParser : sortedErrorParsers) { - fExtensionErrorParsers.put(errorParser.getId(), errorParser); + for (Pair pair : sortedErrorParsers) { + fExtensionErrorParsers.put(pair.first.getId(), pair); } recalculateAvailableErrorParsers(); } @@ -218,7 +241,8 @@ public class ErrorParserExtensionManager { * @param registry - extension registry * @param errorParsers - resulting set of error parsers */ - private static void loadErrorParserExtensions(IExtensionRegistry registry, Set errorParsers) { + private static void loadErrorParserExtensions(IExtensionRegistry registry, + Set> errorParsers) { errorParsers.clear(); IExtensionPoint extension = registry.getExtensionPoint(CCorePlugin.PLUGIN_ID, CCorePlugin.ERROR_PARSER_SIMPLE_ID); if (extension != null) { @@ -232,8 +256,9 @@ public class ErrorParserExtensionManager { if (cfgEl.getName().equals(ELEM_ERRORPARSER)) { IErrorParserNamed errorParser = createErrorParserCarcass(oldStyleId, oldStyleName, cfgEl); if (errorParser!=null) { - configureErrorParser(errorParser, cfgEl); - errorParsers.add(errorParser); + Pair configured = + configureErrorParser(errorParser, cfgEl); + errorParsers.add(configured); } } } @@ -254,42 +279,50 @@ public class ErrorParserExtensionManager { List ids = new ArrayList(); if (fDefaultErrorParserIds!=null) { for (String id : fDefaultErrorParserIds) { + Pair pair = null; IErrorParserNamed errorParser = null; if (fUserDefinedErrorParsers!=null) { errorParser = fUserDefinedErrorParsers.get(id); } - if (errorParser==null) { - errorParser = fExtensionErrorParsers.get(id); + if (errorParser != null) { + pair = errorParserForBuild(errorParser); + } else { + pair = fExtensionErrorParsers.get(id); } - if (errorParser!=null) { - fAvailableErrorParsers.put(id, errorParser); + if (pair != null) { + fAvailableErrorParsers.put(id, pair); ids.add(id); } } } // then the rest in the order defined by comparator - Set sortedErrorParsers = new TreeSet(new ErrorParserComparator()); + Set> sortedErrorParsers = + new TreeSet>(new ErrorParserAndContextComparator()); if (fUserDefinedErrorParsers!=null) { for (String id : fUserDefinedErrorParsers.keySet()) { if (!ids.contains(id)) { IErrorParserNamed errorParser = fUserDefinedErrorParsers.get(id); - sortedErrorParsers.add(errorParser); + sortedErrorParsers.add(errorParserForBuild(errorParser)); } } } for (String id : fExtensionErrorParsers.keySet()) { if (!ids.contains(id)) { - IErrorParserNamed errorParser = fExtensionErrorParsers.get(id); - sortedErrorParsers.add(errorParser); + Pair pair = fExtensionErrorParsers.get(id); + sortedErrorParsers.add(pair); } } - for (IErrorParserNamed errorParser : sortedErrorParsers) { - fAvailableErrorParsers.put(errorParser.getId(), errorParser); + for (Pair pairs : sortedErrorParsers) { + fAvailableErrorParsers.put(pairs.first.getId(), pairs); } } + private static Pair errorParserForBuild(IErrorParserNamed errorParser) { + return new Pair(errorParser, ErrorParserContext.BUILD); + } + /** * Serialize error parsers in workspace level storage. * @@ -544,7 +577,8 @@ public class ErrorParserExtensionManager { * @param cfgEl - extension configuration element * @throws CoreException */ - private static void configureErrorParser(IErrorParserNamed errorParser, IConfigurationElement cfgEl) throws CoreException { + private static Pair configureErrorParser( + IErrorParserNamed errorParser, IConfigurationElement cfgEl) throws CoreException { String id = cfgEl.getAttribute(ATTR_ID); if (id!=null && id.length()>0) errorParser.setId(id); @@ -569,6 +603,31 @@ public class ErrorParserExtensionManager { } } } + int contexts = contextTypes(cfgEl); + return new Pair(errorParser, contexts); + } + + /** + * Returns a bit mask of contexts where an error parser can be used. Valid values for context + * are defined in {@link ErrorParserContext}. + * + * @param errorParserElement represents an "errorparser" element in the extension point + * "org.eclipse.cdt.core.ErrorParser". + * @return the contexts in which an error parser can be used, or + * {@link ErrorParserContext#BUILD BUILD} is none is specified. + * @see ErrorParserContext + */ + private static int contextTypes(IConfigurationElement errorParserElement) { + IConfigurationElement[] contextElements = errorParserElement.getChildren(ELEM_CONTEXT); + if (contextElements.length == 0) { + return BUILD; + } + int contexts = 0; + for (IConfigurationElement contextElement : contextElements) { + String contextType = contextElement.getAttribute(ATTR_TYPE); + contexts = contexts | ErrorParserContext.getValue(contextType); + } + return contexts; } /** @@ -581,7 +640,8 @@ public class ErrorParserExtensionManager { * @return internal instance of error parser */ public static IErrorParser getErrorParserInternal(String id) { - IErrorParserNamed errorParser = fAvailableErrorParsers.get(id); + Pair pair = fAvailableErrorParsers.get(id); + IErrorParserNamed errorParser = pair.first; if (errorParser instanceof ErrorParserNamedWrapper) return ((ErrorParserNamedWrapper)errorParser).getErrorParser(); return errorParser; @@ -626,14 +686,25 @@ public class ErrorParserExtensionManager { * from workspace */ public static String[] getErrorParserAvailableIds() { - return fAvailableErrorParsers.keySet().toArray(new String[0]); + return getErrorParserIds(fAvailableErrorParsers, BUILD); } /** * @return IDs of error parsers contributed through error parser extension point. */ public static String[] getErrorParserExtensionIds() { - return fExtensionErrorParsers.keySet().toArray(new String[0]); + return getErrorParserIds(fExtensionErrorParsers, BUILD); + } + + private static String[] getErrorParserIds( + Map> parsers, int context) { + List ids = new ArrayList(); + for (Map.Entry> entry : parsers.entrySet()) { + if (isInContext(entry.getValue(), context)) { + ids.add(entry.getKey()); + } + } + return ids.toArray(new String[ids.size()]); } /** @@ -690,8 +761,26 @@ public class ErrorParserExtensionManager { * shallow copy with the same instance of underlying error parser. */ public static IErrorParserNamed getErrorParserCopy(String id, boolean isExtension) { - IErrorParserNamed errorParser = isExtension ? fExtensionErrorParsers.get(id) : fAvailableErrorParsers.get(id); + return getErrorParserCopy(id, isExtension, BUILD); + } + /** + * @param id - ID of error parser + * @param isExtension - if {@code true} get unmodified copy of error parser defined as extension + * @param context - context where the error parser will be used. Valid values are defined by + * {@link ErrorParserContext}. + * @return cloned copy of error parser. Note that {@link ErrorParserNamedWrapper} returns + * shallow copy with the same instance of underlying error parser. + * @see ErrorParserContext + */ + public static IErrorParserNamed getErrorParserCopy(String id, boolean isExtension, + int context) { + Pair pair = + isExtension ? fExtensionErrorParsers.get(id) : fAvailableErrorParsers.get(id); + if (!isInContext(pair, context)) { + return null; + } + IErrorParserNamed errorParser = pair.first; try { if (errorParser instanceof RegexErrorParser) { return (RegexErrorParser) ((RegexErrorParser)errorParser).clone(); @@ -703,5 +792,8 @@ public class ErrorParserExtensionManager { } return errorParser; } - + + private static boolean isInContext(Pair pair, int context) { + return (pair.second & context) != 0; + } } diff --git a/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/internal/core/Pair.java b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/internal/core/Pair.java new file mode 100644 index 00000000000..3be11ee6541 --- /dev/null +++ b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/internal/core/Pair.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2012 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Alex Ruiz (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.core; + +/** + * A pair of values. + */ +public class Pair { + public final F first; + public final S second; + + public Pair(F first, S second) { + this.first = first; + this.second = second; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("<"); //$NON-NLS-1$ + builder.append(first); + builder.append(">, <"); //$NON-NLS-1$ + builder.append(second); + builder.append(">"); //$NON-NLS-1$ + return builder.toString(); + } +}