diff options
author | Andrew Gvozdev | 2009-09-11 02:09:38 +0000 |
---|---|---|
committer | Andrew Gvozdev | 2009-09-11 02:09:38 +0000 |
commit | dbbdbd8f95d06a6f008c5f066d1161bfc4e86a62 (patch) | |
tree | 9c125a727d3aa1c50dcae06431e6afe0b8bb841c /core/org.eclipse.cdt.core | |
parent | 64a0f77fafae191bbacd572be2223df88ee64219 (diff) | |
download | org.eclipse.cdt-dbbdbd8f95d06a6f008c5f066d1161bfc4e86a62.tar.gz org.eclipse.cdt-dbbdbd8f95d06a6f008c5f066d1161bfc4e86a62.tar.xz org.eclipse.cdt-dbbdbd8f95d06a6f008c5f066d1161bfc4e86a62.zip |
bug 109139: Generic Error parser
(RegexErrorParser)
Diffstat (limited to 'core/org.eclipse.cdt.core')
10 files changed, 1690 insertions, 45 deletions
diff --git a/core/org.eclipse.cdt.core/plugin.properties b/core/org.eclipse.cdt.core/plugin.properties index 02fe04af25c..63f637feb3b 100644 --- a/core/org.eclipse.cdt.core/plugin.properties +++ b/core/org.eclipse.cdt.core/plugin.properties @@ -48,6 +48,7 @@ CDTGNUAssemblerErrorParser.name=CDT GNU Assembler Error Parser CDTGNULinkerErrorParser.name=CDT GNU Linker Error Parser CDTGNUMakeErrorParser.name=CDT GNU Make Error Parser CDTVisualCErrorParser.name=CDT Visual C Error Parser +CDTRegexErrorParser.name=CDT Regular Expression Error Parser PathEntryContainerInitializer=Path Entry Container Initializer diff --git a/core/org.eclipse.cdt.core/plugin.xml b/core/org.eclipse.cdt.core/plugin.xml index 6174b57e379..1af203884e5 100644 --- a/core/org.eclipse.cdt.core/plugin.xml +++ b/core/org.eclipse.cdt.core/plugin.xml @@ -144,6 +144,8 @@ name="%CDTGNUMakeErrorParser.name" point="org.eclipse.cdt.core.ErrorParser"> <errorparser + id="org.eclipse.cdt.core.MakeErrorParser" + name="%CDTGNUMakeErrorParser.name" class="org.eclipse.cdt.internal.errorparsers.MakeErrorParser"> </errorparser> </extension> @@ -152,6 +154,8 @@ name="%CDTGNUCErrorParser.name" point="org.eclipse.cdt.core.ErrorParser"> <errorparser + id="org.eclipse.cdt.core.GCCErrorParser" + name="%CDTGNUCErrorParser.name" class="org.eclipse.cdt.internal.errorparsers.GCCErrorParser"> </errorparser> </extension> @@ -160,6 +164,8 @@ name="%CDTGNUAssemblerErrorParser.name" point="org.eclipse.cdt.core.ErrorParser"> <errorparser + id="org.eclipse.cdt.core.GASErrorParser" + name="%CDTGNUAssemblerErrorParser.name" class="org.eclipse.cdt.internal.errorparsers.GASErrorParser"> </errorparser> </extension> @@ -168,6 +174,8 @@ name="%CDTGNULinkerErrorParser.name" point="org.eclipse.cdt.core.ErrorParser"> <errorparser + id="org.eclipse.cdt.core.GLDErrorParser" + name="%CDTGNULinkerErrorParser.name" class="org.eclipse.cdt.internal.errorparsers.GLDErrorParser"> </errorparser> </extension> @@ -176,9 +184,20 @@ name="%CDTVisualCErrorParser.name" point="org.eclipse.cdt.core.ErrorParser"> <errorparser + id="org.eclipse.cdt.core.VCErrorParser" + name="%CDTVisualCErrorParser.name" class="org.eclipse.cdt.internal.errorparsers.VCErrorParser"> </errorparser> </extension> + <extension + id="RegexErrorParser" + name="%CDTRegexErrorParser.name" + point="org.eclipse.cdt.core.ErrorParser"> + <errorparser + id="org.eclipse.cdt.core.RegexErrorParser" + name="%CDTRegexErrorParser.name"> + </errorparser> + </extension> <!-- =================================================================================== --> <!-- CDT customized problem markers: C Problem markers --> <!-- =================================================================================== --> diff --git a/core/org.eclipse.cdt.core/schema/ErrorParser.exsd b/core/org.eclipse.cdt.core/schema/ErrorParser.exsd index a4ce450e13c..0ab67eae5a2 100644 --- a/core/org.eclipse.cdt.core/schema/ErrorParser.exsd +++ b/core/org.eclipse.cdt.core/schema/ErrorParser.exsd @@ -23,14 +23,14 @@ <attribute name="id" type="string" use="required"> <annotation> <documentation> - + ID of the extension point (Simple ID) </documentation> </annotation> </attribute> <attribute name="name" type="string" use="required"> <annotation> <documentation> - + Name of the extension point </documentation> </annotation> </attribute> @@ -46,19 +46,107 @@ <element name="errorparser"> <complexType> - <attribute name="class" type="string" use="required"> + <sequence> + <element ref="pattern" minOccurs="0" maxOccurs="unbounded"/> + </sequence> + <attribute name="id" type="string"> + <annotation> + <documentation> + ID of the error parser. If attribute is missing error parser ID is constructed appending Simple ID of extension to plugin ID. + </documentation> + </annotation> + </attribute> + <attribute name="name" type="string"> + <annotation> + <documentation> + Name of the error parser. If this attribute is missing extension name is taken. + </documentation> + </annotation> + </attribute> + <attribute name="class" type="string" use="default" value="org.eclipse.cdt.core.errorparsers.RegexErrorParser"> <annotation> <documentation> a fully qualified name of the Java class that implements <samp>org.eclipse.cdt.core.IErrorParser</samp> interface. </documentation> <appInfo> - <meta.attribute kind="java" basedOn=":org.eclipse.cdt.core.IErrorParser"/> + <meta.attribute kind="java" basedOn=":org.eclipse.cdt.core.errorparsers.IErrorParser"/> </appInfo> </annotation> </attribute> </complexType> </element> + <element name="pattern"> + <annotation> + <documentation> + Use element "pattern" to configure RegexErrorParser. + </documentation> + </annotation> + <complexType> + <attribute name="severity" use="required"> + <annotation> + <documentation> + Attribute "severity" specifies which severity should be used to display the marker in Problems View. There are 3 levels of severity, "Error", "Warning" and "Info". "Ignore" lets stop evaluating the line by the rest of patterns without showing up in Problems View. + </documentation> + </annotation> + <simpleType> + <restriction base="string"> + <enumeration value="Error"> + </enumeration> + <enumeration value="Warning"> + </enumeration> + <enumeration value="Info"> + </enumeration> + <enumeration value="Ignore"> + </enumeration> + </restriction> + </simpleType> + </attribute> + <attribute name="regex" type="string" use="default" value="(.*)"> + <annotation> + <documentation> + Java regular expression to define capturing groups for file-expr, line-expr and description-expr. + </documentation> + </annotation> + </attribute> + <attribute name="file-expr" type="string"> + <annotation> + <documentation> + "Replacement" expression composed from capturing groups defined in regex to define the file. + </documentation> + </annotation> + </attribute> + <attribute name="line-expr" type="string"> + <annotation> + <documentation> + "Replacement" expression composed from capturing groups defined in regex to define the line in file. + </documentation> + </annotation> + </attribute> + <attribute name="description-expr" type="string" use="default" value="$1"> + <annotation> + <documentation> + "Replacement" expression composed from capturing groups defined in regex to define the description (i.e. "$1: $2"). It is possible to specify more than one capturing group in such expression. + </documentation> + </annotation> + </attribute> + <attribute name="variable-expr" type="string"> + <annotation> + <documentation> + "Replacement" expression composed from capturing groups defined in regex to define variable. The value will be assigned to marker attributes but is not used by CDT currently. + </documentation> + </annotation> + </attribute> + <attribute name="eat-processed-line" type="boolean" use="required"> + <annotation> + <documentation> + The attribute defines if a line matched by the pattern is prevented or allowed to be processed by the rest of patterns. "No" allows several patterns to evaluate one line. + </documentation> + </annotation> + </attribute> + </complexType> + </element> + <annotation> <appInfo> <meta.section type="since"/> @@ -107,7 +195,8 @@ public class SampleErrorParser extends AbstractErrorParser {<br/> <documentation> Plug-ins that want to extend this extension point must implement <samp>org.eclipse.cdt.core.IErrorParser</samp> interface. <br/> -It is recommended to extend <samp>org.eclipse.cdt.core.errorparsers.AbstractErrorParser</samp> for most cases. +For most cases it is sufficient to configure RegexErrorParser which is provided by default. +Another good choice is to extend <samp>org.eclipse.cdt.core.errorparsers.AbstractErrorParser</samp> as done in the example. <br/> ErrorParsers dealing with multi-line messages should implement <samp>org.eclipse.cdt.core.IErrorParser2</samp> interface. </documentation> @@ -132,8 +221,7 @@ All rights reserved. This program and the accompanying materials<br/> are made available under the terms of the Eclipse Public License v1.0<br/> which accompanies this distribution, and is available at<br/> http://www.eclipse.org/legal/epl-v10.html<br/> - </documentation> </annotation> -</schema>
\ No newline at end of file +</schema> diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/CCorePlugin.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/CCorePlugin.java index e73c0eefb19..b3627d36a99 100644 --- a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/CCorePlugin.java +++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/CCorePlugin.java @@ -19,7 +19,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; -import java.util.List; import java.util.Map; import java.util.MissingResourceException; import java.util.ResourceBundle; @@ -60,6 +59,7 @@ import org.eclipse.cdt.internal.core.pdom.PDOMManager; import org.eclipse.cdt.internal.core.resources.ResourceLookup; import org.eclipse.cdt.internal.core.settings.model.CProjectDescriptionManager; import org.eclipse.cdt.internal.core.settings.model.ExceptionFactory; +import org.eclipse.cdt.internal.errorparsers.ErrorParserExtensionManager; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IProjectDescription; import org.eclipse.core.resources.IResource; @@ -118,7 +118,13 @@ public class CCorePlugin extends Plugin { public static final String PREF_INDEXER = "indexer"; //$NON-NLS-1$ public static final String DEFAULT_INDEXER = IPDOMManager.ID_FAST_INDEXER; + /** + * Name of the extension point for contributing an error parser + */ public final static String ERROR_PARSER_SIMPLE_ID = "ErrorParser"; //$NON-NLS-1$ + /** + * Full unique name of the extension point for contributing an error parser + */ public final static String ERROR_PARSER_UNIQ_ID = PLUGIN_ID + "." + ERROR_PARSER_SIMPLE_ID; //$NON-NLS-1$ // default store for pathentry @@ -886,41 +892,29 @@ public class CCorePlugin extends Plugin { } /** - * Array of error parsers ids. + * @deprecated since CDT 6.1. Use {@link ErrorParserManager#getErrorParserAvailableIds()} instead + * @return array of error parsers ids */ + @Deprecated public String[] getAllErrorParsersIDs() { - IExtensionPoint extension = Platform.getExtensionRegistry().getExtensionPoint(CCorePlugin.PLUGIN_ID, ERROR_PARSER_SIMPLE_ID); - String[] empty = new String[0]; - if (extension != null) { - IExtension[] extensions = extension.getExtensions(); - ArrayList<String> list = new ArrayList<String>(extensions.length); - for (IExtension e : extensions) - list.add(e.getUniqueIdentifier()); - return list.toArray(empty); - } - return empty; + ErrorParserExtensionManager.loadErrorParserExtensions(); + return ErrorParserExtensionManager.getErrorParserAvailableIds(); } - + + /** + * @deprecated since CDT 6.1. Use {@link ErrorParserManager#getErrorParserCopy(String)} instead + * @param id - id of error parser + * @return array of error parsers + */ + @Deprecated public IErrorParser[] getErrorParser(String id) { - IErrorParser[] empty = new IErrorParser[0]; - try { - IExtensionPoint extension = Platform.getExtensionRegistry().getExtensionPoint(CCorePlugin.PLUGIN_ID, ERROR_PARSER_SIMPLE_ID); - if (extension != null) { - IExtension[] extensions = extension.getExtensions(); - List<IErrorParser> list = new ArrayList<IErrorParser>(extensions.length); - for (IExtension e : extensions) { - String parserID = e.getUniqueIdentifier(); - if ((id == null && parserID != null) || (id != null && id.equals(parserID))) { - for (IConfigurationElement ce : e.getConfigurationElements()) - list.add((IErrorParser)ce.createExecutableExtension("class")); //$NON-NLS-1$ - } - } - return list.toArray(empty); - } - } catch (CoreException e) { - log(e); + ErrorParserExtensionManager.loadErrorParserExtensions(); + IErrorParser errorParser = ErrorParserExtensionManager.getErrorParserInternal(id); + if (errorParser == null) { + return new IErrorParser[] {}; + } else { + return new IErrorParser[] { errorParser }; } - return empty; } public IScannerInfoProvider getScannerInfoProvider(IProject project) { 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 115752d03ae..bbf08b37f46 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 @@ -23,8 +23,10 @@ import java.util.List; import java.util.Map; import java.util.Vector; +import org.eclipse.cdt.core.errorparsers.ErrorParserNamedWrapper; import org.eclipse.cdt.core.resources.ACBuilder; import org.eclipse.cdt.internal.core.resources.ResourceLookup; +import org.eclipse.cdt.internal.errorparsers.ErrorParserExtensionManager; import org.eclipse.cdt.utils.CygPath; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; @@ -36,6 +38,7 @@ import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.URIUtil; +import org.osgi.service.prefs.BackingStoreException; /** * The purpose of ErrorParserManager is to delegate the work of error parsing @@ -46,10 +49,22 @@ import org.eclipse.core.runtime.URIUtil; * @noextend This class is not intended to be subclassed by clients. */ public class ErrorParserManager extends OutputStream { + /** + * The list of error parsers stored in .project for 3.X projects + * as key/value pair with key="org.eclipse.cdt.core.errorOutputParser" + * @deprecated since CDT 4.0. + */ + @Deprecated + public final static String PREF_ERROR_PARSER = CCorePlugin.PLUGIN_ID + ".errorOutputParser"; //$NON-NLS-1$ + + /** + * Delimiter for error parsers presented in one string. + * @since 5.2 + */ + public final static char ERROR_PARSER_DELIMITER = ';'; private int nOpens; - - public final static String PREF_ERROR_PARSER = CCorePlugin.PLUGIN_ID + ".errorOutputParser"; //$NON-NLS-1$ + private int lineCounter=0; private final IProject fProject; private final IMarkerGenerator fMarkerGenerator; @@ -143,12 +158,14 @@ public class ErrorParserManager extends OutputStream { private void enableErrorParsers(String[] parsersIDs) { if (parsersIDs == null) { - parsersIDs = CCorePlugin.getDefault().getAllErrorParsersIDs(); + parsersIDs = ErrorParserExtensionManager.getDefaultErrorParserIds(); } fErrorParsers = new LinkedHashMap<String, IErrorParser[]>(parsersIDs.length); for (String parsersID : parsersIDs) { - IErrorParser[] parsers = CCorePlugin.getDefault().getErrorParser(parsersID); - fErrorParsers.put(parsersID, parsers); + IErrorParser errorParser = ErrorParserExtensionManager.getErrorParserCopy(parsersID); + if (errorParser!=null) { + fErrorParsers.put(parsersID, new IErrorParser[] {errorParser} ); + } } } @@ -169,7 +186,7 @@ public class ErrorParserManager extends OutputStream { } /** - * Return the current URI location where the build is being performed + * @return the current URI location where the build is being performed * @since 5.1 */ public URI getWorkingDirectoryURI() { @@ -292,9 +309,14 @@ public class ErrorParserManager extends OutputStream { String lineTrimmed = line.trim(); + lineCounter++; for (IErrorParser[] parsers : fErrorParsers.values()) { - for (IErrorParser curr : parsers) { + for (IErrorParser parser : parsers) { + IErrorParser curr = parser; + if (parser instanceof ErrorParserNamedWrapper) { + curr = ((ErrorParserNamedWrapper)parser).getErrorParser(); + } int types = IErrorParser2.NONE; if (curr instanceof IErrorParser2) { types = ((IErrorParser2) curr).getProcessLineBehaviour(); @@ -324,6 +346,14 @@ public class ErrorParserManager extends OutputStream { } } } + + /** + * @return counter counting processed lines of output + * @since 5.2 + */ + public int getLineCounter() { + return lineCounter; + } /** * Returns the file with the given (partial) location if that file can be uniquely identified. @@ -693,4 +723,78 @@ public class ErrorParserManager extends OutputStream { public boolean hasErrors() { return hasErrors; } + + /** + * Set and store in workspace area user defined error parsers. + * + * @param errorParsers - array of user defined error parsers + * @throws CoreException in case of problems + * @since 5.2 + */ + public static void setUserDefinedErrorParsers(IErrorParserNamed[] errorParsers) throws CoreException { + ErrorParserExtensionManager.setUserDefinedErrorParsers(errorParsers); + } + + /** + * @return available error parsers IDs which include contributed through extension + user defined ones + * from workspace + * @since 5.2 + */ + public static String[] getErrorParserAvailableIds() { + return ErrorParserExtensionManager.getErrorParserAvailableIds(); + } + + /** + * @return IDs of error parsers contributed through error parser extension point. + * @since 5.2 + */ + public static String[] getErrorParserExtensionIds() { + return ErrorParserExtensionManager.getErrorParserExtensionIds(); + } + + /** + * Set and store default error parsers IDs to be used if error parser list is empty. + * + * @param ids - default error parsers IDs + * @throws BackingStoreException in case of problem with storing + * @since 5.2 + */ + public static void setDefaultErrorParserIds(String[] ids) throws BackingStoreException { + ErrorParserExtensionManager.setDefaultErrorParserIds(ids); + } + + /** + * @return default error parsers IDs to be used if error parser list is empty. + * @since 5.2 + */ + public static String[] getDefaultErrorParserIds() { + return ErrorParserExtensionManager.getDefaultErrorParserIds(); + } + + /** + * @param id - ID of error parser + * @return cloned copy of error parser. Note that {@link ErrorParserNamedWrapper} returns + * shallow copy with the same instance of underlying error parser. + * @since 5.2 + */ + public static IErrorParserNamed getErrorParserCopy(String id) { + return ErrorParserExtensionManager.getErrorParserCopy(id); + } + + /** + * @param ids - array of error parser IDs + * @return error parser IDs delimited with error parser delimiter ";" + * @since 5.2 + */ + public static String toDelimitedString(String[] ids) { + String result=""; //$NON-NLS-1$ + for (String id : ids) { + if (result.length()==0) { + result = id; + } else { + result += ERROR_PARSER_DELIMITER + id; + } + } + return result; + } } diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/IErrorParserNamed.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/IErrorParserNamed.java new file mode 100644 index 00000000000..462d58b4281 --- /dev/null +++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/IErrorParserNamed.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2009 Andrew Gvozdev (Quoin 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: + * Andrew Gvozdev (Quoin Inc.) - Initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.core; + +/** + * Extension of IErrorParser interface to attach id and names to an error parser. + * Clients must implement {@link Object#clone} and {@link Object#equals} methods to avoid slicing. + * @since 5.2 + */ +public interface IErrorParserNamed extends IErrorParser, Cloneable { + /** + * Set error parser ID. + * @param id of error parser + */ + public void setId(String id); + + /** + * Set error parser name. + * @param name of error parser + */ + public void setName(String name); + + /** + * @return id of error parser + */ + public String getId(); + + /** + * @return name of error parser + */ + public String getName(); +} diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/errorparsers/ErrorParserNamedWrapper.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/errorparsers/ErrorParserNamedWrapper.java new file mode 100644 index 00000000000..bf43ffb47a6 --- /dev/null +++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/errorparsers/ErrorParserNamedWrapper.java @@ -0,0 +1,109 @@ +/******************************************************************************* + * Copyright (c) 2009, 2009 Andrew Gvozdev (Quoin 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: + * Andrew Gvozdev (Quoin Inc.) - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.core.errorparsers; + +import org.eclipse.cdt.core.ErrorParserManager; +import org.eclipse.cdt.core.IErrorParser; +import org.eclipse.cdt.core.IErrorParserNamed; +import org.eclipse.core.runtime.Assert; + +/** + * Class to wrap any {@link IErrorParser} to {@link IErrorParserNamed}. + * @since 5.2 + */ +public class ErrorParserNamedWrapper implements IErrorParserNamed { + private String fId; + private String fName; + private final IErrorParser fErrorParser; + + /** + * Constructor. + * + * @param id - assigned ID + * @param name - assigned name. + * @param errorParser - error parser to assign name and ID. + */ + public ErrorParserNamedWrapper(String id, String name, IErrorParser errorParser) { + Assert.isNotNull(errorParser); + + this.fId = id; + this.fName = name; + this.fErrorParser = errorParser; + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.core.IErrorParser#processLine(java.lang.String, org.eclipse.cdt.core.ErrorParserManager) + */ + public boolean processLine(String line, ErrorParserManager epm) { + return fErrorParser.processLine(line, epm); + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.core.IErrorParserNamed#getId() + */ + public String getId() { + return fId; + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.core.IErrorParserNamed#getName() + */ + public String getName() { + return fName; + } + + /** + * @return original error parser which is being wrapped + */ + public IErrorParser getErrorParser() { + return fErrorParser; + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.core.IErrorParserNamed#setId(java.lang.String) + */ + public void setId(String id) { + this.fId = id; + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.core.IErrorParserNamed#setName(java.lang.String) + */ + public void setName(String name) { + this.fName = name; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object o) { + if (o instanceof ErrorParserNamedWrapper) { + ErrorParserNamedWrapper that = (ErrorParserNamedWrapper)o; + return this.fId.equals(that.fId) + && this.fName.equals(that.fName) + // can't be more specific than that since IErrorParser may not implement equals()... + && this.getClass()==that.getClass(); + } + return false; + + } + + /* (non-Javadoc) + * @see java.lang.Object#clone() + */ + @Override + public Object clone() throws CloneNotSupportedException { + // shallow copy since IErrorParser is not {@link Cloneable} in general. + return new ErrorParserNamedWrapper(fId, fName, fErrorParser); + } +} diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/errorparsers/RegexErrorParser.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/errorparsers/RegexErrorParser.java new file mode 100644 index 00000000000..e1852d7e7fd --- /dev/null +++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/errorparsers/RegexErrorParser.java @@ -0,0 +1,179 @@ +/******************************************************************************* + * Copyright (c) 2009, 2009 Andrew Gvozdev (Quoin 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: + * Andrew Gvozdev (Quoin Inc.) - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.core.errorparsers; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.ErrorParserManager; +import org.eclipse.cdt.core.IErrorParser; +import org.eclipse.cdt.core.IErrorParserNamed; + +/** + * {@code RegexerrorParser} is an error parser designed to use regular expressions in order + * to parse build output to produce Errors, Warnings or Infos in Problems View. + * + * Clients may extend this class. As it implements {@link Cloneable} interface those clients + * must implement {@link Object#clone} and {@link Object#equals} methods to avoid slicing. + * Hint to implementers: if you want to extend it with customized {@link RegexErrorPattern} + * it is possible to inject it in {@link #addPattern(RegexErrorPattern)}. + * + * @see IErrorParser + * @since 5.2 + */ +public class RegexErrorParser implements IErrorParserNamed, Cloneable { + private String fId; + private String fName; + private final List<RegexErrorPattern> fPatterns= new ArrayList<RegexErrorPattern>(); + + /** + * Default constructor will initialize the error parser with the name of the class + * using reflection mechanism. + */ + public RegexErrorParser() { + fName = this.getClass().getSimpleName(); + fId = this.getClass().getCanonicalName(); + } + + /** + * Constructor to initialize ID and name of the error parser. + * + * @param id - ID of the error parser. + * @param name - name of the error parser. + */ + public RegexErrorParser(String id, String name) { + fName = name; + fId = id; + } + + /** + * Set error parser ID. + * + * @param id of error parser + */ + public void setId(String id) { + fId = id; + } + + /** + * Set error parser name. + * + * @param name of error parser + */ + public void setName(String name) { + fName = name; + } + + /** + * Add new {@link RegexErrorPattern}. + * + * @param pattern - new pattern + */ + public void addPattern(RegexErrorPattern pattern) { + fPatterns.add(pattern); + } + + /** + * Remove error pattern from processing. + * + * @param pattern - error pattern to remove + */ + public void removePattern(RegexErrorPattern pattern) { + fPatterns.remove(pattern); + } + + /** + * Remove all error patterns. + */ + public void clearPatterns() { + fPatterns.clear(); + } + + /** + * Method toString() for debugging purposes. + */ + @Override + public String toString() { + return "id="+fId+", name="+fName; //$NON-NLS-1$//$NON-NLS-2$ + } + + /** + * @return id of error parser + */ + public String getId() { + return fId; + } + + /** + * @return name of error parser + */ + public String getName() { + return fName; + } + + /** + * @return array of error patterns of this error parser. + */ + public RegexErrorPattern[] getPatterns() { + return fPatterns.toArray(new RegexErrorPattern[0]); + } + + + /** + * Parse a line of build output and register errors/warnings/infos for + * Problems view in internal list of {@link ErrorParserManager}. + * + * @param line - line of the input + * @param epManager - error parsers manager + * @return true if error parser recognized and accepted line, false otherwise + */ + public boolean processLine(String line, ErrorParserManager epManager) { + for (RegexErrorPattern pattern : fPatterns) + try { + if (pattern.processLine(line, epManager)) + return true; + } catch (Exception e){ + String message = "Error parsing line [" + line + "]"; //$NON-NLS-1$//$NON-NLS-2$ + CCorePlugin.log(message, e); + } + + return false; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object o) { + if (o instanceof RegexErrorParser) { + RegexErrorParser that = (RegexErrorParser)o; + return this.fId.equals(that.fId) + && this.fName.equals(that.fName) + && this.fPatterns.equals(that.fPatterns); + } + return false; + + } + + /* (non-Javadoc) + * @see java.lang.Object#clone() + */ + @Override + public Object clone() throws CloneNotSupportedException { + RegexErrorParser that = new RegexErrorParser(fId, fName); + for (RegexErrorPattern pattern : fPatterns) { + that.addPattern((RegexErrorPattern)pattern.clone()); + } + return that; + } +} diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/errorparsers/RegexErrorPattern.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/errorparsers/RegexErrorPattern.java new file mode 100644 index 00000000000..95cc485fea7 --- /dev/null +++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/errorparsers/RegexErrorPattern.java @@ -0,0 +1,385 @@ +/******************************************************************************* + * Copyright (c) 2009, 2009 Andrew Gvozdev (Quoin 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: + * Andrew Gvozdev (Quoin Inc.) - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.core.errorparsers; + +import java.io.File; +import java.io.IOException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.cdt.core.ErrorParserManager; +import org.eclipse.cdt.core.IMarkerGenerator; +import org.eclipse.cdt.utils.CygPath; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; + +/** + * <p>RegexErrorPattern specifies a regular expression and rules how to create markers for + * Problems View. It is used by {@link RegexErrorParser} to process build output. + * + * <p>Regex pattern used by this class is Java regular expression and defines capturing groups. + * Those capturing groups are used in file, line, description expressions to get the values. + * <p>For example: pattern <b>"(../../..) (.*):(\d*): (Error:.*)"</b> could go along with + * file-expression <b>"$2"</b>, line-expression <b>"$3"</b> and description-expression <b>"$1 $4"</b>. + * + * <p>Note: variable name is being stored in marker tag. However currently it is not being used. + * + * <p>Severity could be one of: + * <br> - {@link IMarkerGenerator#SEVERITY_INFO}, + * <br> - {@link IMarkerGenerator#SEVERITY_WARNING}, + * <br> - {@link IMarkerGenerator#SEVERITY_ERROR_RESOURCE}, + * <br> - {@link IMarkerGenerator#SEVERITY_ERROR_BUILD} + * <br> - {@link RegexErrorPattern#SEVERITY_SKIP} + * <br/>{@code SEVERITY_SKIP} means that output line is checked to match the pattern + * but won't be parsed to create a marker. It is useful with conjunction with + * {@code eatProcessedLine=true} to filter out certain lines. + * + * <p>{@code eatProcessedLine} specifies if the current output line is being passed + * to the rest of patterns for further processing or consumed by the pattern. + * + * <p>Clients may extend this class. As it implements {@link Cloneable} interface those clients + * must implement {@link Object#clone} and {@link Object#equals} methods to avoid slicing. + * @since 5.2 + */ +public class RegexErrorPattern implements Cloneable { + /** + * Additional "severity" flag which tells if qualified output line should be ignored. + */ + public static final int SEVERITY_SKIP = -1; + private static final String EMPTY_STR=""; //$NON-NLS-1$ + + private Pattern pattern; + private String fileExpression; + private String lineExpression; + private String descriptionExpression; + private String varNameExpression; + private int severity; + private boolean eatProcessedLine; + + private static boolean isCygwin = true; + + /** + * Constructor. + * + * @param pattern - regular expression describing the capturing groups + * @param fileExpression - capturing group expression defining file name + * @param lineExpression - capturing group expression defining line number + * @param descriptionExpression - capturing group expression defining description + * @param varNameExpression -capturing group expression defining variable name + * @param severity - severity, one of + * <br>{@link IMarkerGenerator#SEVERITY_INFO}, + * <br>{@link IMarkerGenerator#SEVERITY_WARNING}, + * <br>{@link IMarkerGenerator#SEVERITY_ERROR_RESOURCE}, + * <br>{@link IMarkerGenerator#SEVERITY_ERROR_BUILD} + * <br>{@link RegexErrorPattern#SEVERITY_SKIP} + * @param eat - defines whether to consume output line avoiding further processing by other patterns + * + * <p>See general description for this class {@link RegexErrorPattern} for more details. + */ + public RegexErrorPattern(String pattern, + String fileExpression, + String lineExpression, + String descriptionExpression, + String varNameExpression, + int severity, + boolean eat) { + this.pattern = Pattern.compile(pattern!=null ? pattern : EMPTY_STR); + this.fileExpression = fileExpression!=null ? fileExpression : EMPTY_STR; + this.lineExpression = lineExpression!=null ? lineExpression : EMPTY_STR; + this.descriptionExpression = descriptionExpression!=null ? descriptionExpression : EMPTY_STR; + this.varNameExpression = varNameExpression!=null ? varNameExpression : EMPTY_STR; + this.severity = severity; + this.eatProcessedLine = eat; + } + + /** + * @return regular expression pattern + */ + public String getPattern() { + return pattern.toString(); + } + + /** + * @return expression defining file name + */ + public String getFileExpression() { + return fileExpression; + } + + /** + * @return expression defining line number + */ + public String getLineExpression() { + return lineExpression; + } + + /** + * @return expression defining description + */ + public String getDescriptionExpression() { + return descriptionExpression; + } + + /** + * @return expression defining variable name + */ + public String getVarNameExpression() { + return varNameExpression; + } + + /** + * @return severity of the marker, one of: + * <br>{@link IMarkerGenerator#SEVERITY_INFO}, + * <br>{@link IMarkerGenerator#SEVERITY_WARNING}, + * <br>{@link IMarkerGenerator#SEVERITY_ERROR_RESOURCE}, + * <br>{@link IMarkerGenerator#SEVERITY_ERROR_BUILD} + */ + public int getSeverity() { + return severity; + } + + /** + * @return whether output line is consumed and not processed further by other patterns + */ + public boolean isEatProcessedLine() { + return eatProcessedLine; + } + + /** + * @param pattern - regular expression pattern describing the capturing groups + */ + public void setPattern(String pattern) { + this.pattern = Pattern.compile(pattern); + } + + /** + * @param fileExpression - capturing group expression defining file name + */ + public void setFileExpression(String fileExpression) { + this.fileExpression = fileExpression; + } + + /** + * @param lineExpression - capturing group expression defining line number + */ + public void setLineExpression(String lineExpression) { + this.lineExpression = lineExpression; + } + + /** + * @param descriptionExpression - capturing group expression defining description + */ + public void setDescriptionExpression(String descriptionExpression) { + this.descriptionExpression = descriptionExpression; + } + + /** + * @param varNameExpression -capturing group expression defining variable name + */ + public void setVarNameExpression(String varNameExpression) { + this.varNameExpression = varNameExpression; + } + + /** + * @param severity - severity, one of + * <br>{@link IMarkerGenerator#SEVERITY_INFO}, + * <br>{@link IMarkerGenerator#SEVERITY_WARNING}, + * <br>{@link IMarkerGenerator#SEVERITY_ERROR_RESOURCE}, + * <br>{@link IMarkerGenerator#SEVERITY_ERROR_BUILD} + * <br>{@link RegexErrorPattern#SEVERITY_SKIP} + */ + public void setSeverity(int severity) { + this.severity = severity; + } + + /** + * @param eatProcessedLine - whether to consume output line avoiding further processing by other patterns + */ + public void setEatProcessedLine(boolean eatProcessedLine) { + this.eatProcessedLine = eatProcessedLine; + } + + /** + * @param input - input line. + * @return matcher to interpret the input line. + */ + private Matcher getMatcher(CharSequence input) { + return pattern.matcher(input); + } + + private String parseStr(Matcher matcher, String str) { + if (str!=null) + return matcher.replaceAll(str); + return null; + } + /** + * @param matcher - matcher to parse the input line. + * @return parsed file name or {@code null}. + */ + protected String getFileName(Matcher matcher) { + return parseStr(matcher, fileExpression); + } + + /** + * @param matcher - matcher to parse the input line. + * @return parsed line number or {@code 0}. + */ + protected int getLineNum(Matcher matcher) { + if (lineExpression != null) + try { + return Integer.valueOf(matcher.replaceAll(lineExpression)).intValue(); + } catch (NumberFormatException e) { + } + return 0; + } + + /** + * @param matcher - matcher to parse the input line. + * @return parsed description or {@code null}. + */ + protected String getDesc(Matcher matcher) { + return parseStr(matcher, descriptionExpression); + } + + /** + * @param matcher - matcher to parse the input line. + * @return parsed variable name or {@code null}. + */ + protected String getVarName(Matcher matcher) { + return parseStr(matcher, varNameExpression); + } + + /** + * @param matcher - matcher to parse the input line. + * @return severity of the problem. + */ + protected int getSeverity(Matcher matcher) { + return severity; + } + + /** + * Parse a line of build output and register error/warning for + * Problems view. + * + * @param line - one line of output. + * @param eoParser - {@link ErrorParserManager}. + * @return {@code true} if error/warning/info problem was found. + */ + public boolean processLine(String line, ErrorParserManager eoParser) { + Matcher matcher = getMatcher(line); + if (!matcher.find()) + return false; + + recordError(matcher, eoParser); + return eatProcessedLine; + } + + /** + * Register the error in {@link ErrorParserManager}. + * + * @param matcher - matcher to parse the input line. + * @param eoParser - {@link ErrorParserManager}. + * @return {@code true} indicating that error was found. + */ + protected boolean recordError(Matcher matcher, ErrorParserManager eoParser) { + int severity = getSeverity(matcher); + if (severity == SEVERITY_SKIP) + return true; + + String fileName = getFileName(matcher); + int lineNum = getLineNum(matcher); + String desc = getDesc(matcher); + String varName = getVarName(matcher); + IPath externalPath = null ; + + IResource file = null; + if (fileName != null) { + file = eoParser.findFileName(fileName); + + if (file == null) { + // If the file is not found in the workspace we attach the problem to the project + // and add the external path to the file. + file = eoParser.getProject(); + externalPath = getLocation(fileName); + } + } + + eoParser.generateExternalMarker(file, lineNum, desc, severity, varName, externalPath); + return true; + } + + /** + * If the file designated by filename exists, return the IPath representation of the filename + * If it does not exist, try cygpath translation + * + * @param filename - file name + * @return location (outside of the workspace). + */ + private IPath getLocation(String filename) { + IPath path = new Path(filename); + File file = path.toFile() ; + if (!file.exists() && isCygwin && path.isAbsolute()) { + CygPath cygpath = null ; + try { + cygpath = new CygPath("cygpath"); //$NON-NLS-1$ + String cygfilename = cygpath.getFileName(filename); + IPath convertedPath = new Path(cygfilename); + file = convertedPath.toFile() ; + if (file.exists()) { + path = convertedPath; + } + } catch (UnsupportedOperationException e) { + isCygwin = false; + } catch (IOException e) { + } finally { + if (null!=cygpath) { + cygpath.dispose(); + } + } + } + return path ; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object o) { + if (o instanceof RegexErrorPattern) { + RegexErrorPattern that = (RegexErrorPattern)o; + return this.pattern.toString().equals(that.pattern.toString()) + && this.fileExpression.equals(that.fileExpression) + && this.lineExpression.equals(that.lineExpression) + && this.descriptionExpression.equals(that.descriptionExpression) + && this.varNameExpression.equals(that.varNameExpression) + && this.severity==that.severity + && this.eatProcessedLine==that.eatProcessedLine; + } + return false; + + } + + /* (non-Javadoc) + * @see java.lang.Object#clone() + */ + @Override + public Object clone() throws CloneNotSupportedException { + return new RegexErrorPattern(pattern.toString(), + fileExpression, + lineExpression, + descriptionExpression, + varNameExpression, + severity, + eatProcessedLine); + } +} 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 new file mode 100644 index 00000000000..d2e4ba56b9b --- /dev/null +++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/errorparsers/ErrorParserExtensionManager.java @@ -0,0 +1,726 @@ +/******************************************************************************* + * Copyright (c) 2009, 2009 Andrew Gvozdev (Quoin 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: + * Andrew Gvozdev (Quoin Inc.) - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.internal.errorparsers; + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; +import java.util.Map.Entry; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.ErrorParserManager; +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.XmlUtil; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExtension; +import org.eclipse.core.runtime.IExtensionPoint; +import org.eclipse.core.runtime.IExtensionRegistry; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.preferences.IEclipsePreferences; +import org.eclipse.core.runtime.preferences.InstanceScope; +import org.osgi.service.prefs.BackingStoreException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + +/** + * ErrorParserExtensionManager manages error parser extensions, serialization and preferences + * + */ +public class ErrorParserExtensionManager { + private static final String STORAGE_ERRORPARSER_EXTENSIONS = "model.extensions.xml"; //$NON-NLS-1$ + private static final String PREFERENCE_ERRORPARSER_DEFAULT_IDS = "errorparser.default.ids"; //$NON-NLS-1$ + 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_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$ + private static final String ELEM_PATTERN = "pattern"; //$NON-NLS-1$ + private static final String ATTR_CLASS = "class"; //$NON-NLS-1$ + 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_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$ + private static final String ATTR_LINE = "line-expr"; //$NON-NLS-1$ + private static final String ATTR_DESCRIPTION = "description-expr"; //$NON-NLS-1$ + private static final String ATTR_VARIABLE = "variable-expr"; //$NON-NLS-1$ + private static final String ATTR_EAT_LINE = "eat-processed-line"; //$NON-NLS-1$ + + private static final String ATTR_VALUE_WARNING = "Warning"; //$NON-NLS-1$ + private static final String ATTR_VALUE_ERROR = "Error"; //$NON-NLS-1$ + 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<String, IErrorParserNamed> fExtensionErrorParsers = new LinkedHashMap<String, IErrorParserNamed>(); + private static final LinkedHashMap<String, IErrorParserNamed> fAvailableErrorParsers = new LinkedHashMap<String, IErrorParserNamed>(); + private static LinkedHashMap<String, IErrorParserNamed> fUserDefinedErrorParsers = null; + private static List<String> fDefaultErrorParserIds = null; + + static { + loadUserDefinedErrorParsers(); + loadDefaultErrorParserIds(); + loadErrorParserExtensions(); + } + + /** + * Load user defined error parsers from workspace preference storage. + * + * @noreference This method is not intended to be referenced by clients. + */ + synchronized public static void loadUserDefinedErrorParsers() { + fUserDefinedErrorParsers = null; + Document doc = null; + try { + doc = loadXml(getStoreLocation(STORAGE_ERRORPARSER_EXTENSIONS)); + } catch (Exception e) { + CCorePlugin.log("Can't load preferences from file "+STORAGE_ERRORPARSER_EXTENSIONS, e); //$NON-NLS-1$ + } + + if (doc!=null) { + Set<IErrorParserNamed> errorParsers = new LinkedHashSet<IErrorParserNamed>(); + loadErrorParserExtensions(doc, errorParsers); + + if (errorParsers.size()>0) { + fUserDefinedErrorParsers = new LinkedHashMap<String, IErrorParserNamed>(); + for (IErrorParserNamed errorParser : errorParsers) { + fUserDefinedErrorParsers.put(errorParser.getId(), errorParser); + } + } + } + recalculateAvailableErrorParsers(); + } + + /** + * Load XML from file to DOM Document. + * + * @param location - location of XML file + * @return new loaded XML Document or {@code null} if file does not exist + * @throws ParserConfigurationException + * @throws SAXException + * @throws IOException + */ + private static Document loadXml(IPath location) throws ParserConfigurationException, SAXException, IOException { + java.io.File storeFile = location.toFile(); + if (storeFile.exists()) { + InputStream xmlStream = new FileInputStream(storeFile); + DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + return builder.parse(xmlStream); + } + return null; + } + + /** + * Parse error parser contributed extensions from XML document. + * + * @param doc - source XML + * @param errorParsers - resulting list of error parsers + */ + private static void loadErrorParserExtensions(Document doc, Set<IErrorParserNamed> errorParsers) { + errorParsers.clear(); + NodeList extentionNodes = doc.getElementsByTagName(ELEM_EXTENSION); + for (int iext=0;iext<extentionNodes.getLength();iext++) { + Node extentionNode = extentionNodes.item(iext); + if(extentionNode.getNodeType() != Node.ELEMENT_NODE) + continue; + + NodeList errorparserNodes = extentionNode.getChildNodes(); + for (int ierp=0;ierp<errorparserNodes.getLength();ierp++) { + Node errorparserNode = errorparserNodes.item(ierp); + if(errorparserNode.getNodeType() != Node.ELEMENT_NODE || ! ELEM_ERRORPARSER.equals(errorparserNode.getNodeName())) + continue; + + NamedNodeMap errorParserAttributes = errorparserNode.getAttributes(); + String className = determineNodeValue(errorParserAttributes.getNamedItem(ATTR_CLASS)); + + try { + IErrorParserNamed errorParser = createErrorParserCarcass(className, Platform.getExtensionRegistry()); + if (errorParser!=null) { + configureErrorParser(errorParser, errorparserNode); + errorParsers.add(errorParser); + } + } catch (Exception e) { + CCorePlugin.log("Can't create class ["+className+"] while trying to load error parser extension", e); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + } + } + + /** + * Load workspace default error parser IDs to be used if no error parsers specified. + * + * @noreference This method is not intended to be referenced by clients. + */ + synchronized public static void loadDefaultErrorParserIds() { + fDefaultErrorParserIds = null; + IEclipsePreferences preferences = new InstanceScope().getNode(CCorePlugin.PLUGIN_ID); + String ids = preferences.get(PREFERENCE_ERRORPARSER_DEFAULT_IDS, NONE); + if (ids.equals(NONE)) { + return; + } + + fDefaultErrorParserIds = Arrays.asList(ids.split(String.valueOf(ErrorParserManager.ERROR_PARSER_DELIMITER))); + } + + /** + * Load error parser contributed extensions. + * + * @noreference This method is not intended to be referenced by clients. + */ + synchronized public static void loadErrorParserExtensions() { + // sort by name - for the error parsers taken from platform extensions + Set<IErrorParserNamed> sortedErrorParsers = new TreeSet<IErrorParserNamed>(new Comparator<IErrorParserNamed>() { + public int compare(IErrorParserNamed errorParser1, IErrorParserNamed errorParser2) { + return errorParser1.getName().compareTo(errorParser2.getName()); + } + }); + + loadErrorParserExtensions(Platform.getExtensionRegistry(), sortedErrorParsers); + + fExtensionErrorParsers.clear(); + for (IErrorParserNamed errorParser : sortedErrorParsers) { + fExtensionErrorParsers.put(errorParser.getId(), errorParser); + } + recalculateAvailableErrorParsers(); + } + + /** + * Load error parser contributed extensions from extension registry. + * + * @param registry - extension registry + * @param errorParsers - resulting set of error parsers + */ + private static void loadErrorParserExtensions(IExtensionRegistry registry, Set<IErrorParserNamed> errorParsers) { + errorParsers.clear(); + IExtensionPoint extension = registry.getExtensionPoint(CCorePlugin.PLUGIN_ID, CCorePlugin.ERROR_PARSER_SIMPLE_ID); + if (extension != null) { + IExtension[] extensions = extension.getExtensions(); + for (IExtension ext : extensions) { + try { + String extensionID = ext.getUniqueIdentifier(); + String oldStyleId = extensionID; + String oldStyleName = ext.getLabel(); + for (IConfigurationElement cfgEl : ext.getConfigurationElements()) { + if (cfgEl.getName().equals(ELEM_ERRORPARSER)) { + IErrorParserNamed errorParser = createErrorParserCarcass(oldStyleId, oldStyleName, cfgEl); + if (errorParser!=null) { + configureErrorParser(errorParser, cfgEl); + errorParsers.add(errorParser); + } + } + } + } catch (Exception e) { + CCorePlugin.log("Cannot load ErrorParser extension " + ext.getUniqueIdentifier(), e); //$NON-NLS-1$ + } + } + } + } + + /** + * Populate the list of available error parsers where workspace level user defined parsers + * overwrite contributed through error parser extension point. + */ + private static void recalculateAvailableErrorParsers() { + fAvailableErrorParsers.clear(); + if (fUserDefinedErrorParsers!=null) { + fAvailableErrorParsers.putAll(fUserDefinedErrorParsers); + } + for (IErrorParserNamed errorParser : fExtensionErrorParsers.values()) { + String id = errorParser.getId(); + if (!fAvailableErrorParsers.containsKey(id)) { + fAvailableErrorParsers.put(id, errorParser); + } + } + } + + /** + * Serialize error parsers in workspace level storage. + * + * @throws CoreException if something goes wrong + */ + public static void serializeUserDefinedErrorParsers() throws CoreException { + try { + DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + Document doc = builder.newDocument(); + Element elementPlugin = doc.createElement(ELEM_PLUGIN); + doc.appendChild(elementPlugin); + + if (fUserDefinedErrorParsers!=null) { + for (Entry<String, IErrorParserNamed> entry: fUserDefinedErrorParsers.entrySet()) { + IErrorParserNamed errorParser = entry.getValue(); + addErrorParserExtension(elementPlugin, errorParser); + } + } + + serializeXml(doc, getStoreLocation(STORAGE_ERRORPARSER_EXTENSIONS)); + + } catch (Exception e) { + throw new CoreException(new Status(IStatus.ERROR, "Failed serializing to file " + STORAGE_ERRORPARSER_EXTENSIONS, CCorePlugin.PLUGIN_ID, e)); //$NON-NLS-1$ + } + } + + /** + * Utility method to convert severity to string for the purpose of serializing in XML. + * + * @param severity - severity + * @return string representation + */ + private static String severityToString(int severity) { + switch (severity) { + case IMarkerGenerator.SEVERITY_INFO: + return ATTR_VALUE_INFO; + case IMarkerGenerator.SEVERITY_WARNING: + return ATTR_VALUE_WARNING; + case IMarkerGenerator.SEVERITY_ERROR_BUILD: + case IMarkerGenerator.SEVERITY_ERROR_RESOURCE: + return ATTR_VALUE_ERROR; + } + return ATTR_VALUE_IGNORE; + } + + /** + * Utility method to de-serialize severity from XML. + * + * @param attrSeverity - string representation of the severity + * @return severity + */ + private static int stringToSeverity(String attrSeverity) { + if (ATTR_VALUE_ERROR.equals(attrSeverity)) + return IMarkerGenerator.SEVERITY_ERROR_RESOURCE; + if (ATTR_VALUE_WARNING.equals(attrSeverity)) + return IMarkerGenerator.SEVERITY_WARNING; + if (ATTR_VALUE_INFO.equals(attrSeverity)) + return IMarkerGenerator.SEVERITY_INFO; + + return RegexErrorPattern.SEVERITY_SKIP; + } + + /** + * Add error parser extension to XML fragment, normally under <plugin/> element. + * + * @param elementPlugin - element where to add error parser extension + * @param errorParserNamed - error parser to add + */ + private static void addErrorParserExtension(Element elementPlugin, IErrorParserNamed errorParserNamed) { + String id = errorParserNamed.getId(); + String name = errorParserNamed.getName(); + String simpleId = getSimpleId(id); + + IErrorParser errorParser = errorParserNamed; + if (errorParser instanceof ErrorParserNamedWrapper) + errorParser = ((ErrorParserNamedWrapper)errorParser).getErrorParser(); + + Document doc = elementPlugin.getOwnerDocument(); + + // <extension/> + Element elementExtension = doc.createElement(ELEM_EXTENSION); + elementExtension.setAttribute(ATTR_ID, simpleId); + elementExtension.setAttribute(ATTR_NAME, name); + elementExtension.setAttribute(ATTR_POINT, EXTENSION_POINT_ERROR_PARSER); + + elementPlugin.appendChild(elementExtension); + + // <errorparser/> + Element elementErrorParser = doc.createElement(ELEM_ERRORPARSER); + elementErrorParser.setAttribute(ATTR_ID, id); + elementErrorParser.setAttribute(ATTR_NAME, name); + elementErrorParser.setAttribute(ATTR_CLASS, errorParser.getClass().getCanonicalName()); + + elementExtension.appendChild(elementErrorParser); + + if (errorParserNamed instanceof RegexErrorParser) { + RegexErrorParser regexErrorParser = (RegexErrorParser)errorParserNamed; + RegexErrorPattern[] patterns = regexErrorParser.getPatterns(); + + for (RegexErrorPattern pattern : patterns) { + // <pattern/> + Element elementPattern = doc.createElement(ELEM_PATTERN); + elementPattern.setAttribute(ATTR_SEVERITY, severityToString(pattern.getSeverity())); + elementPattern.setAttribute(ATTR_REGEX, pattern.getPattern()); + elementPattern.setAttribute(ATTR_FILE, pattern.getFileExpression()); + elementPattern.setAttribute(ATTR_LINE, pattern.getLineExpression()); + elementPattern.setAttribute(ATTR_DESCRIPTION, pattern.getDescriptionExpression()); + elementPattern.setAttribute(ATTR_EAT_LINE, String.valueOf(pattern.isEatProcessedLine())); + + elementErrorParser.appendChild(elementPattern); + } + + } + } + + /** + * Determine simple ID of error parser as last segment of full or unique ID. + * + * @param uniqueId - full ID of error parser + * @return simple ID of error parser + */ + private static String getSimpleId(String uniqueId) { + String simpleId = uniqueId; + int dot = uniqueId.lastIndexOf('.'); + if (dot>=0) { + simpleId = uniqueId.substring(dot+1); + } + return simpleId; + } + + /** + * Serialize XML Document in a file. + * + * @param doc - XML to serialize + * @param location - location of the file + * @throws IOException in case of problems with file I/O + * @throws TransformerException in case of problems with XML output + */ + synchronized private static void serializeXml(Document doc, IPath location) throws IOException, TransformerException { + + java.io.File storeFile = location.toFile(); + if (!storeFile.exists()) { + storeFile.createNewFile(); + } + OutputStream fileStream = new FileOutputStream(storeFile); + + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + Transformer transformer = transformerFactory.newTransformer(); + transformer.setOutputProperty(OutputKeys.METHOD, "xml"); //$NON-NLS-1$ + transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); //$NON-NLS-1$ + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$ + + XmlUtil.prettyFormat(doc); + DOMSource source = new DOMSource(doc); + StreamResult result = new StreamResult(new FileOutputStream(storeFile)); + transformer.transform(source, result); + + fileStream.close(); + } + + /** + * Save the list of default error parsers in preferences. + * + * @throws BackingStoreException in case of problem storing + */ + public static void serializeDefaultErrorParserIds() throws BackingStoreException { + IEclipsePreferences preferences = new InstanceScope().getNode(CCorePlugin.PLUGIN_ID); + String ids = NONE; + if (fDefaultErrorParserIds!=null) { + ids = ErrorParserManager.toDelimitedString(fDefaultErrorParserIds.toArray(new String[0])); + } + + preferences.put(PREFERENCE_ERRORPARSER_DEFAULT_IDS, ids); + preferences.flush(); + } + + /** + * @param store - name of the store + * @return location of the store in the plug-in state area + */ + private static IPath getStoreLocation(String store) { + return CCorePlugin.getDefault().getStateLocation().append(store); + } + + /** + * Creates empty non-configured error parser from extension point definition looking at "class" attribute. + * ID and name of error parser are assigned from first extension point encountered. + * + * @param className - full qualified class name of error parser. + * @param registry - extension registry + * @return new non-configured error parser + */ + private static IErrorParserNamed createErrorParserCarcass(String className, IExtensionRegistry registry) { + if (className==null || className.length()==0 || className.equals(RegexErrorParser.class.getName())) + return new RegexErrorParser(); + + try { + IExtensionPoint extension = registry.getExtensionPoint(CCorePlugin.PLUGIN_ID, CCorePlugin.ERROR_PARSER_SIMPLE_ID); + if (extension != null) { + IExtension[] extensions = extension.getExtensions(); + for (IExtension ext : extensions) { + String extensionID = ext.getUniqueIdentifier(); + String oldStyleId = extensionID; + String oldStyleName = ext.getLabel(); + for (IConfigurationElement cfgEl : ext.getConfigurationElements()) { + if (cfgEl.getName().equals(ELEM_ERRORPARSER) && className.equals(cfgEl.getAttribute(ATTR_CLASS))) { + return createErrorParserCarcass(oldStyleId, oldStyleName, cfgEl); + } + } + } + } + } catch (Exception e) { + CCorePlugin.log("Error creating error parser", e); //$NON-NLS-1$ + } + return null; + } + + /** + * Creates empty non-configured error parser as executable extension from extension point definition. + * If "class" attribute is empty RegexErrorParser is created. + * + * @param initialId - nominal ID of error parser + * @param initialName - nominal name of error parser + * @param ce - configuration element with error parser definition + * @return new non-configured error parser + * @throws CoreException in case of failure + */ + private static IErrorParserNamed createErrorParserCarcass(String initialId, String initialName, IConfigurationElement ce) throws CoreException { + IErrorParserNamed errorParser = null; + if (ce.getAttribute(ATTR_CLASS)!=null) { + IErrorParser ep = (IErrorParser)ce.createExecutableExtension(ATTR_CLASS); + if (ep instanceof IErrorParserNamed) { + errorParser = (IErrorParserNamed)ep; + errorParser.setId(initialId); + errorParser.setName(initialName); + } else if (ep!=null) { + errorParser = new ErrorParserNamedWrapper(initialId, initialName, ep); + } + } + if (errorParser==null) { + errorParser = new RegexErrorParser(initialId, initialName); + } + return errorParser; + } + + /** + * Configure error parser from XML error parser node. + * + * @param errorParser - error parser to configure + * @param errorparserNode - XML error parser node + */ + private static void configureErrorParser(IErrorParserNamed errorParser, Node errorparserNode) { + NamedNodeMap errorParserAttributes = errorparserNode.getAttributes(); + String id = determineNodeValue(errorParserAttributes.getNamedItem(ATTR_ID)); + String name = determineNodeValue(errorParserAttributes.getNamedItem(ATTR_NAME)); + errorParser.setId(id); + errorParser.setName(name); + if (errorParser instanceof RegexErrorParser) { + RegexErrorParser regexErrorParser = (RegexErrorParser)errorParser; + + NodeList patternNodes = errorparserNode.getChildNodes(); + for (int ipat=0;ipat<patternNodes.getLength();ipat++) { + Node patternNode = patternNodes.item(ipat); + if(patternNode.getNodeType() != Node.ELEMENT_NODE || ! ELEM_PATTERN.equals(patternNode.getNodeName())) + continue; + + NamedNodeMap patternAttributes = patternNode.getAttributes(); + String attrSeverity = determineNodeValue(patternAttributes.getNamedItem(ATTR_SEVERITY)); + String regex = determineNodeValue(patternAttributes.getNamedItem(ATTR_REGEX)); + String fileExpr = determineNodeValue(patternAttributes.getNamedItem(ATTR_FILE)); + String lineExpr = determineNodeValue(patternAttributes.getNamedItem(ATTR_LINE)); + String DescExpr = determineNodeValue(patternAttributes.getNamedItem(ATTR_DESCRIPTION)); + String attrEatLine = determineNodeValue(patternAttributes.getNamedItem(ATTR_EAT_LINE)); + + int severity = stringToSeverity(attrSeverity); + + boolean eatLine = ! Boolean.FALSE.toString().equals(attrEatLine); // if null default to true + regexErrorParser.addPattern(new RegexErrorPattern(regex, fileExpr, lineExpr, DescExpr, null, + severity, eatLine)); + } + } + } + + /** + * @param node + * @return node value or {@code null} + */ + private static String determineNodeValue(Node node) { + return node!=null ? node.getNodeValue() : null; + } + + /** + * Configure error parser from extension configuration element. + * + * @param errorParser - error parser to configure + * @param cfgEl - extension configuration element + * @throws CoreException + */ + private static void configureErrorParser(IErrorParserNamed errorParser, IConfigurationElement cfgEl) throws CoreException { + String id = cfgEl.getAttribute(ATTR_ID); + if (id!=null && id.length()>0) + errorParser.setId(id); + String name = cfgEl.getAttribute(ATTR_NAME); + if (name!=null && name.length()>0) + errorParser.setName(name); + + if (errorParser instanceof RegexErrorParser) { + RegexErrorParser regexErrorParser = (RegexErrorParser)errorParser; + + for (IConfigurationElement cepat : cfgEl.getChildren()) { + if (cepat.getName().equals(ELEM_PATTERN)) { + + boolean eat = ! Boolean.FALSE.toString().equals(cepat.getAttribute(ATTR_EAT_LINE)); + regexErrorParser.addPattern(new RegexErrorPattern(cepat.getAttribute(ATTR_REGEX), + cepat.getAttribute(ATTR_FILE), + cepat.getAttribute(ATTR_LINE), + cepat.getAttribute(ATTR_DESCRIPTION), + cepat.getAttribute(ATTR_VARIABLE), + stringToSeverity(cepat.getAttribute(ATTR_SEVERITY)), + eat)); + } + } + } + } + + /** + * Return error parser as stored in internal list. + * + * @noreference This method is not intended to be referenced by clients. + * Use {@link #getErrorParserCopy(String)} instead. + * + * @param id - ID of error parser + * @return internal instance of error parser + */ + public static IErrorParser getErrorParserInternal(String id) { + IErrorParserNamed errorParser = fAvailableErrorParsers.get(id); + if (errorParser instanceof ErrorParserNamedWrapper) + return ((ErrorParserNamedWrapper)errorParser).getErrorParser(); + return errorParser; + } + + /** + * Set and store in workspace area user defined error parsers. + * + * @param errorParsers - array of user defined error parsers + * @throws CoreException in case of problems + */ + public static void setUserDefinedErrorParsers(IErrorParserNamed[] errorParsers) throws CoreException { + setUserDefinedErrorParsersInternal(errorParsers); + serializeUserDefinedErrorParsers(); + } + + /** + * Internal method to set user defined error parsers in memory. + * + * @noreference This method is not intended to be referenced by clients. + * Use {@link #setUserDefinedErrorParsers(IErrorParserNamed[])}. + * + * @param errorParsers - array of user defined error parsers + */ + public static void setUserDefinedErrorParsersInternal(IErrorParserNamed[] errorParsers) { + if (errorParsers==null) { + fUserDefinedErrorParsers = null; + } else { + fUserDefinedErrorParsers= new LinkedHashMap<String, IErrorParserNamed>(); + // set customized list + for (IErrorParserNamed errorParser : errorParsers) { + fUserDefinedErrorParsers.put(errorParser.getId(), errorParser); + } + } + recalculateAvailableErrorParsers(); + } + + /** + * @return available error parsers IDs which include contributed through extension + user defined ones + * from workspace + */ + public static String[] getErrorParserAvailableIds() { + return fAvailableErrorParsers.keySet().toArray(new String[0]); + } + + /** + * @return IDs of error parsers contributed through error parser extension point. + */ + public static String[] getErrorParserExtensionIds() { + return fExtensionErrorParsers.keySet().toArray(new String[0]); + } + + /** + * Set and store default error parsers IDs to be used if error parser list is empty. + * + * @param ids - default error parsers IDs + * @throws BackingStoreException in case of problem with storing + */ + public static void setDefaultErrorParserIds(String[] ids) throws BackingStoreException { + setDefaultErrorParserIdsInternal(ids); + serializeDefaultErrorParserIds(); + } + + /** + * Set default error parsers IDs in internal list. + * + * @noreference This method is not intended to be referenced by clients. + * Use {@link #setDefaultErrorParserIds(String[])}. + * + * @param ids - default error parsers IDs + */ + public static void setDefaultErrorParserIdsInternal(String[] ids) { + if (ids==null) { + fDefaultErrorParserIds = null; + } else { + fDefaultErrorParserIds = new ArrayList<String>(Arrays.asList(ids)); + } + } + + /** + * @return default error parsers IDs to be used if error parser list is empty. + */ + public static String[] getDefaultErrorParserIds() { + if (fDefaultErrorParserIds==null) { + return fAvailableErrorParsers.keySet().toArray(new String[0]); + } + return fDefaultErrorParserIds.toArray(new String[0]); + } + + /** + * @param id - ID of error parser + * @return cloned copy of error parser. Note that {@link ErrorParserNamedWrapper} returns + * shallow copy with the same instance of underlying error parser. + */ + public static IErrorParserNamed getErrorParserCopy(String id) { + IErrorParserNamed errorParser = fAvailableErrorParsers.get(id); + + try { + if (errorParser instanceof RegexErrorParser) { + return (RegexErrorParser) ((RegexErrorParser)errorParser).clone(); + } else if (errorParser instanceof ErrorParserNamedWrapper) { + return (ErrorParserNamedWrapper) ((ErrorParserNamedWrapper)errorParser).clone(); + } + } catch (CloneNotSupportedException e) { + CCorePlugin.log(e); + } + return errorParser; + } + + +} |