diff --git a/core/org.eclipse.cdt.core/plugin.properties b/core/org.eclipse.cdt.core/plugin.properties index f06189e829e..9f72cc6d3a7 100755 --- a/core/org.eclipse.cdt.core/plugin.properties +++ b/core/org.eclipse.cdt.core/plugin.properties @@ -138,3 +138,5 @@ uncPathConverter.name = UNC Path Converter ScannerInfoExtensionLanguageSettingsProvider.name=Contributed ScannerInfo Entries PathEntryScannerInfoLanguageSettingsProvider.name=Contributed PathEntry Containers ReferencedProjectsLanguageSettingsProvider.name=Exported Entries from Referenced Projects + +problemMarkerFilter.name=Problem Marker Filter diff --git a/core/org.eclipse.cdt.core/plugin.xml b/core/org.eclipse.cdt.core/plugin.xml index 3d6764c38ec..9bfaba5143d 100644 --- a/core/org.eclipse.cdt.core/plugin.xml +++ b/core/org.eclipse.cdt.core/plugin.xml @@ -675,6 +675,7 @@ + diff --git a/core/org.eclipse.cdt.core/schema/ProblemMarkerFilter.exsd b/core/org.eclipse.cdt.core/schema/ProblemMarkerFilter.exsd new file mode 100644 index 00000000000..3a575f9f9e7 --- /dev/null +++ b/core/org.eclipse.cdt.core/schema/ProblemMarkerFilter.exsd @@ -0,0 +1,135 @@ + + + + + + + + + This extension point allows to filter out unneeded problem markers. + For example during building of Qt base project with QML files tool Qt Linguist + could report syntax errors in some qml file. These errors are presented as + "C/C++ Problems" in qml files because they match format CDT expects for errors. + If there is already installed plug-in that handles QML files it is a wise to ignore such + errors because they are already reported as "QML Problems" with more meaningful descriptions. + + + + + + + + + + + + + + + + + + + a fully qualified identifier of the target extension point + + + + + + + an optional identifier of the extension instance + + + + + + + an optional name of the extension instance + + + + + + + + + + + + + + + + + + Implementation of Problem Marker Filter that allows to filter out unneded or duplicit problem markers. + + + + + + + + + + + + + + + 8.3 + + + + + + + + + The following is an example of a qmakeEnvProvider contribution: +<p> +<pre> +<extension + point="org.eclipse.cdt.core.ProblemMarkerFilter" + id="example" + name="Example QMake Env Provider Extension"> + <problemMarkerFilter + class="com.example.internal.ProblemMarkerFilter"> + <enablement> + <with variable="projectNatures"> + <iterate operator="or"> + <equals value="com.example.my-nature"/> + </iterate> + </with> + </enablement> + </problemMarkerFilter> +</extension> +</pre> +</p> + + + + + + + + + The contributed class must implement <code>org.eclipse.cdt.core.IProblemMarkerFilter</code>. + + + + + + + + + Copyright (c) 2014 BlackBerry Limited 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 + + + + + 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 735d8ce086a..12c4ab08e0d 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 @@ -30,6 +30,7 @@ import org.eclipse.cdt.core.language.settings.providers.IWorkingDirectoryTracker import org.eclipse.cdt.core.resources.ACBuilder; import org.eclipse.cdt.internal.core.Cygwin; import org.eclipse.cdt.internal.core.IErrorMarkeredOutputStream; +import org.eclipse.cdt.internal.core.ProblemMarkerFilterManager; import org.eclipse.cdt.internal.core.resources.ResourceLookup; import org.eclipse.cdt.internal.errorparsers.ErrorParserExtensionManager; import org.eclipse.cdt.utils.EFSExtensionManager; @@ -588,6 +589,8 @@ outer: * @since 5.4 */ public void addProblemMarker(ProblemMarkerInfo problemMarkerInfo){ + if ( ! ProblemMarkerFilterManager.getInstance().acceptMarker(problemMarkerInfo) ) + return; fErrors.add(problemMarkerInfo); fMarkerGenerator.addMarker(problemMarkerInfo); if (problemMarkerInfo.severity == IMarkerGenerator.SEVERITY_ERROR_RESOURCE) { diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/IProblemMarkerFilter.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/IProblemMarkerFilter.java new file mode 100644 index 00000000000..9bdb22b8907 --- /dev/null +++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/IProblemMarkerFilter.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2014 BlackBerry Limited 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 + */ +package org.eclipse.cdt.core; + +/** + * The purpose of IProblemMarkerFilter is to provide filtering function for problem markers. + * ProblemMarkerFilter extension point are required to implements this interface. + * + * @since 5.6 + */ +public interface IProblemMarkerFilter { + + /** + * Decide if a problem marker should be reported or ignored. + * + * @param markerInfo description of the problem marker that is going to be reported + * @return true if markers should be reported, false if should be ignored + */ + boolean acceptMarker(ProblemMarkerInfo markerInfo); +} diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/ProblemMarkerFilterDesc.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/ProblemMarkerFilterDesc.java new file mode 100644 index 00000000000..b794a21f5df --- /dev/null +++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/ProblemMarkerFilterDesc.java @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2014 BlackBerry Limited 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 + */ +package org.eclipse.cdt.internal.core; + +import java.util.Arrays; + +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.IProblemMarkerFilter; +import org.eclipse.cdt.core.ProblemMarkerInfo; +import org.eclipse.core.expressions.EvaluationContext; +import org.eclipse.core.expressions.EvaluationResult; +import org.eclipse.core.expressions.Expression; +import org.eclipse.core.expressions.ExpressionConverter; +import org.eclipse.core.expressions.ExpressionTagNames; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; + +/** + * The purpose of ProblemMarkerFilterDesc is to manage information about + * one instance ProblemMarkerFilter extension point. + */ +class ProblemMarkerFilterDesc { + + /** + * XML attribute for name of class that implements this extension point + */ + private static final String ATTR_CLASS = "class"; //$NON-NLS-1$ + + /** + * Variable name for projectNatures for enablement expression + */ + private static final String VAR_PROJECTNATURES = "projectNatures"; //$NON-NLS-1$ + + /** + * Configuration element for this extension point + */ + private final IConfigurationElement element; + + /** + * Expression that allows conditionally enable/disable extension point + */ + private final Expression enablementExpression; + + /** + * Status of this extension point. + * False = disabled because of expression error + * True = enabled because of missing enablementExpression + * null = evaluate the expression for particular project + */ + private Boolean fStatus = null; + + /** + * Unique id of this extension point + */ + private String id; + + /** + * + */ + private IProblemMarkerFilter filter; + + /** + * Filter that accept any marker. + * + */ + private static final IProblemMarkerFilter NULL_FILTER = new IProblemMarkerFilter() { + + @Override + public boolean acceptMarker(ProblemMarkerInfo markerInfo) { + return true; + } + + }; + + /** + * Constructor + * + * @param element configuration element with optional enablementExpression + */ + ProblemMarkerFilterDesc(IConfigurationElement element) { + this.element = element; + + Expression expr = null; + IConfigurationElement[] children = element.getChildren(ExpressionTagNames.ENABLEMENT); + switch (children.length) { + case 0: + fStatus = Boolean.TRUE; + break; + case 1: + try { + ExpressionConverter parser = ExpressionConverter.getDefault(); + expr = parser.perform(children[0]); + } catch (CoreException e) { + CCorePlugin.log("Error in enablement expression of " + id, e); //$NON-NLS-1$ + fStatus = Boolean.FALSE; + } + break; + default: + CCorePlugin.log("Too many enablement expressions for " + id); //$NON-NLS-1$ + fStatus = Boolean.FALSE; + break; + } + enablementExpression = expr; + } + + /** + * Evaluate enablement expression + * + * @param project project for which we had to evaluate the expression + * @return value of enablement expression + */ + public boolean matches(IProject project) { + // If the enablement expression is missing or structurally invalid, then return immediately + if (fStatus != null) + return fStatus.booleanValue(); + + if (enablementExpression != null) + try { + EvaluationContext evalContext = new EvaluationContext(null, project); + String[] natures = project.getDescription().getNatureIds(); + evalContext.addVariable(VAR_PROJECTNATURES, Arrays.asList(natures)); + return enablementExpression.evaluate(evalContext) == EvaluationResult.TRUE; + } catch (CoreException e) { + CCorePlugin.log("Error while evaluating enablement expression for " + id, e); //$NON-NLS-1$ + } + + return false; + } + + /** + * Return filter interface + * @return Filter interface or NULL_FILER if filter could not be created + */ + IProblemMarkerFilter getFilter() { + if (filter == null) + synchronized (this) { + if (filter == null) { + try { + filter = (IProblemMarkerFilter) element.createExecutableExtension(ATTR_CLASS); + } catch (CoreException e) { + String id = element.getDeclaringExtension().getNamespaceIdentifier() + '.' + + element.getDeclaringExtension().getSimpleIdentifier(); + CCorePlugin.log("Error in class attribute of " + id, e); //$NON-NLS-1$ + + // mark the filter with an empty implementation to prevent future load attempts + filter = NULL_FILTER; + } + } + } + + return filter; + } + +} diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/ProblemMarkerFilterManager.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/ProblemMarkerFilterManager.java new file mode 100644 index 00000000000..5409cd262a8 --- /dev/null +++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/ProblemMarkerFilterManager.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2014 BlackBerry Limited 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 + */ +package org.eclipse.cdt.internal.core; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.WeakHashMap; + +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.ErrorParserManager; +import org.eclipse.cdt.core.IProblemMarkerFilter; +import org.eclipse.cdt.core.ProblemMarkerInfo; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExtensionRegistry; +import org.eclipse.core.runtime.Platform; + +/** + * The purpose of ProblemMarkerFilterManager is to manage ProblemMarkerFilter extension points. + * {@link ErrorParserManager} use this manager to filter out unnecessary problem markers + * + * @noextend This class is not intended to be subclassed by clients. + */ +public class ProblemMarkerFilterManager { + + /** + * Name of ProblemMarkerFilter extension point + */ + private final static String EXTENSION_POINT = "ProblemMarkerFilter"; //$NON-NLS-1$ + + /** + * Singleton instance of ProblemMarkerFilterManager + */ + private final static ProblemMarkerFilterManager INSTANCE = new ProblemMarkerFilterManager(); + + /** + * List of all executable extension registered in Extension Registry + */ + private final List filters = new ArrayList(); + + /** + * Cache of active filters for known projects. + * This cache allow to skip evaluation of enablementExpression for every marker. + */ + private final Map> filtersCache = new WeakHashMap>(); + + /** + * Return singleton instance of ProblemMarkerFilterManager + * + * @return singleton instance of ProblemMarkerFilterManager + */ + public static ProblemMarkerFilterManager getInstance() { + return INSTANCE; + } + + /** + * Constructor. + * + * Creates instances of executable extension for ProblemMarkerFilter extension point + * + */ + private ProblemMarkerFilterManager() { + IExtensionRegistry reg = Platform.getExtensionRegistry(); + IConfigurationElement[] extensions = reg + .getConfigurationElementsFor(CCorePlugin.PLUGIN_ID, EXTENSION_POINT); + for (int i = 0; i < extensions.length; i++) { + IConfigurationElement element = extensions[i]; + ProblemMarkerFilterDesc filterDesc = new ProblemMarkerFilterDesc(element); + filters.add(filterDesc); + } + } + + /** + * Called by {@link ErrorParserManager#addProblemMarker(ProblemMarkerInfo)} to filter out unnecessary problem markers + * + * Problem marker is ignored if any plug-in that implements ProblemMarkerFilter extension point rejects it. + * + * @see IProblemMarkerFilter#acceptMarker(ProblemMarkerInfo) + * + * @param markerInfo description of the problem marker that is going to be added + * @return true if markers should be reported, false if should be ignored + */ + public boolean acceptMarker(ProblemMarkerInfo markerInfo) { + IProject project = markerInfo.file.getProject(); + if (project == null || !project.isOpen()) + return true; + List enabledFilters = findEnabledFilters(project); + for (ProblemMarkerFilterDesc filterDesc: enabledFilters) { + if ( ! filterDesc.getFilter().acceptMarker(markerInfo) ) { + return false; + } + } + return true; + } + + /** + * Try to find enabled filter for project and cache results + * @param project project for which we want know enabled filters + * @return list of enabled filters + */ + private List findEnabledFilters(IProject project) { + synchronized (filtersCache) { + List result = filtersCache.get(project); + if (result == null) { + result = new ArrayList(); + for (ProblemMarkerFilterDesc filterDesc: filters) { + if ( filterDesc.matches(project) ) { + result.add(filterDesc); + } + } + filtersCache.put(project, result); + } + return result; + } + } + +}