1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-04-23 06:32:10 +02:00

bug 109139: Generic Error parser

(RegexErrorParser)
This commit is contained in:
Andrew Gvozdev 2009-09-11 02:09:38 +00:00
parent 64a0f77faf
commit dbbdbd8f95
26 changed files with 4130 additions and 195 deletions

View file

@ -35,6 +35,11 @@ CommandTargetBuild.description=Invoke a make target build for the selected conta
CommandTargetCreate.name=Create Make Target
CommandTargetCreate.description=Create a new make build target for the selected container.
# Build Settings Preference page
PreferenceBuildSettings.name=Build Settings
ErrorParsersTab.name=Error Parsers
ErrorParsersTab.tooltip=Error Parsers scan build output and report errors in Problems view
PreferenceMakeProject.name=New Make Projects
PreferenceMake.name=Make
PreferenceMakefileEditor.name=Makefile Editor

View file

@ -175,6 +175,12 @@
</extension>
<extension
point="org.eclipse.ui.preferencePages">
<page
name="%PreferenceBuildSettings.name"
category="org.eclipse.cdt.ui.preferences.CPluginPreferencePage"
class="org.eclipse.cdt.make.internal.ui.preferences.BuildSettingsPreferencePage"
id="org.eclipse.cdt.make.ui.preferences.BuildSettings">
</page>
<page
name="%PreferenceMake.name"
category="org.eclipse.cdt.ui.preferences.CPluginPreferencePage"
@ -428,8 +434,8 @@
profileId="org.eclipse.cdt.make.core.GCCStandardMakePerFileProfile"/>
</extension>
<extension
point="org.eclipse.cdt.ui.cPropertyTab">
<extension
point="org.eclipse.cdt.ui.cPropertyTab">
<!-- exported from CORE -->
<tab
@ -448,7 +454,16 @@
helpId="cdt_u_prop_pns_sym"
parent="org.eclipse.cdt.make.internal.ui.properties.PathAndSymbolPage"
tooltip="%Symbols.tooltip"/>
</extension>
<tab
class="org.eclipse.cdt.ui.newui.ErrorParsTab"
helpId="cdt_u_prop_build_setting_errparser"
icon="icons/obj16/error_obj.gif"
name="%ErrorParsersTab.name"
parent="org.eclipse.cdt.make.internal.ui.preferences.BuildSettingsPreferencePage"
tooltip="%ErrorParsersTab.tooltip"
weight="020">
</tab>
</extension>
<extension
point="org.eclipse.ui.propertyPages">

View file

@ -0,0 +1,48 @@
/*******************************************************************************
* Copyright (c) 2009 Andrew Gvozdev 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 - Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.make.internal.ui.preferences;
import org.eclipse.cdt.core.settings.model.ICResourceDescription;
import org.eclipse.cdt.ui.newui.AbstractPrefPage;
import org.eclipse.cdt.ui.newui.ICPropertyTab;
/**
* Preference page for Build Settings.
*
*/
public class BuildSettingsPreferencePage extends AbstractPrefPage {
@Override
protected String getHeader() {
return MakefilePreferencesMessages.getString("BuildPreferencePage.description"); //$NON-NLS-1$
}
/*
* All affected settings are stored in preferences. Tabs are responsible for
* saving, after OK signal. No need to affect Project Description somehow.
*/
@Override
public boolean performOk() {
forEach(ICPropertyTab.OK, null);
return true;
}
@Override
public ICResourceDescription getResDesc() {
return null;
}
@Override
protected boolean isSingle() {
return false;
}
}

View file

@ -1,5 +1,5 @@
###############################################################################
# Copyright (c) 2000, 2006 IBM Corporation and others.
# Copyright (c) 2000, 2009 IBM Corporation 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
@ -9,6 +9,8 @@
# IBM Corporation - initial API and implementation
###############################################################################
BuildPreferencePage.description=These settings are global to the entire workspace. They are overridden by project-specific settings.
MakefileEditorPreferencePage.description=Makefile Editor settings:
MakefileEditorPreferencePage.invalid_input_print_margin= Invalid print margin column specified
MakefileEditorPreferencePage.empty_input_print_margin= No print margin column specified

View file

@ -26,6 +26,7 @@ public class ErrorParserTests {
suite.addTest(ErrorParserManagerTest.suite());
suite.addTest(ErrorParserFileMatchingTest.suite());
suite.addTest(ErrorParserEfsFileMatchingTest.suite());
suite.addTest(RegexErrorParserTests.suite());
return suite;
}

View file

@ -0,0 +1,689 @@
/*******************************************************************************
* Copyright (c) 2009 Andrew Gvozdev 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 - Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.core.internal.errorparsers.tests;
import java.util.ArrayList;
import junit.framework.TestCase;
import junit.framework.TestSuite;
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.ProblemMarkerInfo;
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.errorparsers.ErrorParserExtensionManager;
import org.eclipse.cdt.internal.errorparsers.GCCErrorParser;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
/**
* Test cases testing RegexErrorParser functionality
*/
public class RegexErrorParserTests extends TestCase {
// These should match id and name of extension point defined in plugin.xml
private static final String REGEX_ERRORPARSER_ID = "org.eclipse.cdt.core.tests.RegexErrorParserId";
private static final String REGEX_ERRORPARSER_NAME = "Test Plugin RegexErrorParser";
private static final String GCC_ERRORPARSER_ID = "org.eclipse.cdt.core.GCCErrorParser";
private static final String TEST_PROJECT_NAME = "RegexErrorParserTests";
private IProject fProject = null;
private ArrayList<ProblemMarkerInfo> errorList;
private final IMarkerGenerator markerGenerator = new IMarkerGenerator() {
// deprecated
public void addMarker(IResource file, int lineNumber, String errorDesc, int severity, String errorVar) {}
public void addMarker(ProblemMarkerInfo problemMarkerInfo) {
errorList.add(problemMarkerInfo);
}
};
/**
* Dummy error parser
*/
public static class DummyErrorParser implements IErrorParser {
/**
* Constructor
*/
public DummyErrorParser() {
}
public boolean processLine(String line, ErrorParserManager eoParser) {
return false;
}
}
/**
* Constructor.
* @param name - name of the test.
*/
public RegexErrorParserTests(String name) {
super(name);
}
@Override
protected void setUp() throws Exception {
fProject = ResourceHelper.createCDTProject(TEST_PROJECT_NAME);
assertNotNull(fProject);
errorList = new ArrayList<ProblemMarkerInfo>();
}
@Override
protected void tearDown() throws Exception {
ResourceHelper.cleanUp();
fProject = null;
ErrorParserManager.setUserDefinedErrorParsers(null);
}
/**
* @return - new TestSuite.
*/
public static TestSuite suite() {
return new TestSuite(RegexErrorParserTests.class);
}
/**
* main function of the class.
*
* @param args - arguments
*/
public static void main(String[] args) {
junit.textui.TestRunner.run(suite());
}
/**
* Check if error pattern can be added/deleted.
*
* @throws Exception...
*/
public void testRegexErrorParserAddDeletePattern() throws Exception {
RegexErrorParser regexErrorParser = new RegexErrorParser();
regexErrorParser.addPattern(new RegexErrorPattern("pattern 1",
null, null, null, null, RegexErrorPattern.SEVERITY_SKIP, true));
regexErrorParser.addPattern(new RegexErrorPattern("delete me",
null, null, null, null, RegexErrorPattern.SEVERITY_SKIP, true));
regexErrorParser.addPattern(new RegexErrorPattern("pattern 3",
null, null, null, null, RegexErrorPattern.SEVERITY_SKIP, true));
// adding patterns
RegexErrorPattern[] patternsBefore = regexErrorParser.getPatterns();
assertEquals(3, patternsBefore.length);
assertEquals("delete me", patternsBefore[1].getPattern());
RegexErrorPattern next = patternsBefore[2];
// delete pattern test
regexErrorParser.removePattern(patternsBefore[1]);
RegexErrorPattern[] patternsAfter = regexErrorParser.getPatterns();
assertEquals(2, patternsAfter.length);
assertEquals(next, patternsAfter[1]);
}
/**
* Make sure the order of patterns is preserved.
*
* @throws Exception...
*/
public void testRegexErrorParserPatternOrder() throws Exception {
final int ERR=IMarkerGenerator.SEVERITY_ERROR_RESOURCE;
RegexErrorParser regexErrorParser = new RegexErrorParser();
RegexErrorPattern removable = new RegexErrorPattern("CCC", null, null, null, null, ERR, true);
regexErrorParser.addPattern(new RegexErrorPattern("AAA", null, null, null, null, ERR, true));
regexErrorParser.addPattern(new RegexErrorPattern("BBB", null, null, null, null, ERR, true));
regexErrorParser.addPattern(removable);
regexErrorParser.addPattern(new RegexErrorPattern("DDD", null, null, null, null, ERR, true));
regexErrorParser.addPattern(new RegexErrorPattern("ZZZ", null, null, null, null, ERR, true));
{
RegexErrorPattern[] patterns = regexErrorParser.getPatterns();
assertEquals("AAA", patterns[0].getPattern());
assertEquals("BBB", patterns[1].getPattern());
assertEquals("CCC", patterns[2].getPattern());
assertEquals("DDD", patterns[3].getPattern());
assertEquals("ZZZ", patterns[4].getPattern());
}
regexErrorParser.removePattern(removable);
{
RegexErrorPattern[] patterns = regexErrorParser.getPatterns();
assertEquals("AAA", patterns[0].getPattern());
assertEquals("BBB", patterns[1].getPattern());
assertEquals("DDD", patterns[2].getPattern());
assertEquals("ZZZ", patterns[3].getPattern());
}
}
/**
* Check how RegexErrorParser parses output.
*
* @throws Exception...
*/
public void testRegexErrorParserParseOutput() throws Exception {
RegexErrorParser regexErrorParser = new RegexErrorParser();
regexErrorParser.addPattern(new RegexErrorPattern("(.*)#(.*)#(.*)#(.*)",
"$1", "$2", "$3 $4", "var=$4", IMarkerGenerator.SEVERITY_ERROR_RESOURCE, true));
regexErrorParser.addPattern(new RegexErrorPattern("(.*)!(skip me)!(.*)!(.*)",
null, null, null, null, RegexErrorPattern.SEVERITY_SKIP, true));
regexErrorParser.addPattern(new RegexErrorPattern("(.*)!(Description)!(.*)!(.*)",
"$4", "$3", "$2", "$1", IMarkerGenerator.SEVERITY_WARNING, /*eat-line*/ false));
// broken pattern
regexErrorParser.addPattern(new RegexErrorPattern("(.*)!(.*)",
"$6", "$7", "$8", "$9", IMarkerGenerator.SEVERITY_WARNING, true));
regexErrorParser.addPattern(new RegexErrorPattern("(.*)!(.*)!(.*)!(.*)",
null, null, null, null, IMarkerGenerator.SEVERITY_INFO, true));
String fileName = "RegexErrorParser.c";
ResourceHelper.createFile(fProject, fileName);
ErrorParserManager epManager = new ErrorParserManager(fProject, markerGenerator, new String[0]);
boolean result;
ProblemMarkerInfo problemMarkerInfo;
// Regular pattern
regexErrorParser.processLine(fileName+"#10#Description#Variable", epManager);
// This should get ignored
regexErrorParser.processLine("Variable!skip me!10!"+fileName, epManager);
// Eat-line=false + qualifying next pattern (nulls), i.e. generates 2 problems
regexErrorParser.processLine("Variable!Description!10!"+fileName, epManager);
errorList.clear();
epManager.reportProblems();
assertEquals(3, errorList.size());
// Regular
problemMarkerInfo = errorList.get(0);
assertEquals(IMarkerGenerator.SEVERITY_ERROR_RESOURCE, problemMarkerInfo.severity);
assertEquals("L/"+TEST_PROJECT_NAME+"/"+fileName, problemMarkerInfo.file.toString());
assertEquals(fileName, problemMarkerInfo.file.getName());
assertEquals(10, problemMarkerInfo.lineNumber);
assertEquals("Description Variable",problemMarkerInfo.description);
assertEquals("var=Variable",problemMarkerInfo.variableName);
// Eat-line
problemMarkerInfo = errorList.get(1);
assertEquals(IMarkerGenerator.SEVERITY_WARNING, problemMarkerInfo.severity);
assertEquals("L/"+TEST_PROJECT_NAME+"/"+fileName, problemMarkerInfo.file.toString());
assertEquals(fileName, problemMarkerInfo.file.getName());
assertEquals(10, problemMarkerInfo.lineNumber);
assertEquals("Description",problemMarkerInfo.description);
assertEquals("Variable",problemMarkerInfo.variableName);
// Nulls
problemMarkerInfo = errorList.get(2);
assertEquals(IMarkerGenerator.SEVERITY_INFO, problemMarkerInfo.severity);
assertEquals("P/"+TEST_PROJECT_NAME, problemMarkerInfo.file.toString());
assertEquals(0, problemMarkerInfo.lineNumber);
assertEquals("",problemMarkerInfo.description);
assertEquals("",problemMarkerInfo.variableName);
// clone & equals
RegexErrorParser cloned = (RegexErrorParser)regexErrorParser.clone();
assertTrue(cloned!=regexErrorParser);
assertEquals(regexErrorParser, cloned);
assertTrue(cloned.getPatterns()!=regexErrorParser.getPatterns());
assertEquals(cloned.getPatterns().length, regexErrorParser.getPatterns().length);
for (int i=0; i<regexErrorParser.getPatterns().length; i++) {
// Checking deep copy
assertTrue(cloned.getPatterns()[i]!=regexErrorParser.getPatterns()[i]);
assertEquals(cloned.getPatterns()[i],regexErrorParser.getPatterns()[i]);
}
}
/**
* Checks if compatibility with CCorePlugin methods from CDT 6.0 was not violated.
*
* @throws Exception...
*/
public void testCompatibility() throws Exception {
final CCorePlugin cCorePlugin = CCorePlugin.getDefault();
// CCorePlugin.getAllErrorParsersIDs()
String all = ErrorParserManager.toDelimitedString(cCorePlugin.getAllErrorParsersIDs());
assertTrue(all.contains(GCC_ERRORPARSER_ID));
// CCorePlugin.getErrorParser(id)
IErrorParser[] gccErrorParserArray = cCorePlugin.getErrorParser(GCC_ERRORPARSER_ID);
assertNotNull(gccErrorParserArray);
assertEquals(1, gccErrorParserArray.length);
assertTrue(gccErrorParserArray[0] instanceof GCCErrorParser);
}
/**
* Check that regular error parser extension defined in plugin.xml is accessible.
*
* @throws Exception...
*/
public void testExtension() throws Exception {
// ErrorParserManager.getErrorParser
{
IErrorParserNamed errorParser = ErrorParserManager.getErrorParserCopy(REGEX_ERRORPARSER_ID);
assertNotNull(errorParser);
assertEquals(REGEX_ERRORPARSER_NAME, errorParser.getName());
assertTrue(errorParser instanceof RegexErrorParser);
RegexErrorParser regexErrorParser = (RegexErrorParser)errorParser;
assertEquals(REGEX_ERRORPARSER_ID, regexErrorParser.getId());
assertEquals(REGEX_ERRORPARSER_NAME, regexErrorParser.getName());
RegexErrorPattern[] patterns = regexErrorParser.getPatterns();
assertEquals(1, patterns.length);
RegexErrorPattern pattern = patterns[0];
assertEquals(IMarker.SEVERITY_ERROR, pattern.getSeverity());
assertEquals(true, pattern.isEatProcessedLine());
assertEquals("(.*):(.*):regex (.*)", pattern.getPattern());
assertEquals("$1", pattern.getFileExpression());
assertEquals("$2", pattern.getLineExpression());
assertEquals("$3", pattern.getDescriptionExpression());
assertEquals("", pattern.getVarNameExpression());
}
// ErrorParserManager.getErrorParsers
{
IErrorParser errorParser = ErrorParserManager.getErrorParserCopy(REGEX_ERRORPARSER_ID);
assertTrue(errorParser instanceof RegexErrorParser);
RegexErrorParser regexErrorParser = (RegexErrorParser)errorParser;
assertEquals(REGEX_ERRORPARSER_ID, regexErrorParser.getId());
assertEquals(REGEX_ERRORPARSER_NAME, regexErrorParser.getName());
}
}
/**
* Make sure extensions contributed through extension point are sorted by name.
*
* @throws Exception...
*/
public void testExtensionsSorting() throws Exception {
{
String[] ids = ErrorParserManager.getErrorParserExtensionIds();
String lastName="";
// error parsers created from extensions are to be sorted by names
for (String id : ids) {
String name = ErrorParserManager.getErrorParserCopy(id).getName();
assertTrue(lastName.compareTo(name)<=0);
lastName = name;
}
}
}
/**
* Test setting/retrieval of error parsers and their IDs.
*
* @throws Exception...
*/
public void testAvailableErrorParsers() throws Exception {
final String TESTING_ID = "org.eclipse.cdt.core.test.errorparser";
final String TESTING_NAME = "An error parser";
final String[] availableParserIds = ErrorParserManager.getErrorParserAvailableIds();
assertNotNull(availableParserIds);
assertTrue(availableParserIds.length>0);
final String firstId = ErrorParserManager.getErrorParserAvailableIds()[0];
final IErrorParserNamed firstErrorParser = ErrorParserManager.getErrorParserCopy(firstId);
assertNotNull(firstErrorParser);
assertEquals(firstId, firstErrorParser.getId());
final String firstName = firstErrorParser.getName();
// Preconditions
{
String all = ErrorParserManager.toDelimitedString(ErrorParserManager.getErrorParserAvailableIds());
assertEquals(false, all.contains(TESTING_ID));
assertEquals(true, all.contains(firstId));
assertNull(ErrorParserManager.getErrorParserCopy(TESTING_ID));
IErrorParserNamed retrieved2 = ErrorParserManager.getErrorParserCopy(firstId);
assertNotNull(retrieved2);
assertEquals(firstErrorParser, retrieved2);
}
// set available parsers
{
IErrorParser dummy1 = new DummyErrorParser();
IErrorParser dummy2 = new DummyErrorParser();
ErrorParserManager.setUserDefinedErrorParsers(new IErrorParserNamed[] {
// add brand new one
new ErrorParserNamedWrapper(TESTING_ID, TESTING_NAME, dummy1),
// override extension with another one
new ErrorParserNamedWrapper(firstId, firstName, dummy2),
});
String all = ErrorParserManager.toDelimitedString(ErrorParserManager.getErrorParserAvailableIds());
assertEquals(true, all.contains(TESTING_ID));
assertEquals(true, all.contains(firstId));
IErrorParserNamed retrieved1 = ErrorParserManager.getErrorParserCopy(TESTING_ID);
assertNotNull(retrieved1);
assertEquals(TESTING_NAME, retrieved1.getName());
assertTrue(retrieved1 instanceof ErrorParserNamedWrapper);
assertEquals(dummy1, ((ErrorParserNamedWrapper)retrieved1).getErrorParser());
IErrorParserNamed retrieved2 = ErrorParserManager.getErrorParserCopy(firstId);
assertNotNull(retrieved2);
assertEquals(firstName, retrieved2.getName());
assertTrue(retrieved2 instanceof ErrorParserNamedWrapper);
assertEquals(dummy2, ((ErrorParserNamedWrapper)retrieved2).getErrorParser());
}
// reset available parsers
{
ErrorParserManager.setUserDefinedErrorParsers(null);
String all = ErrorParserManager.toDelimitedString(ErrorParserManager.getErrorParserAvailableIds());
assertEquals(false, all.contains(TESTING_ID));
assertEquals(true, all.contains(firstId));
assertNull(ErrorParserManager.getErrorParserCopy(TESTING_ID));
IErrorParserNamed retrieved2 = ErrorParserManager.getErrorParserCopy(firstId);
assertNotNull(retrieved2);
assertEquals(firstErrorParser, retrieved2);
}
}
/**
* Test setting/retrieval of user defined error parsers.
*
* @throws Exception...
*/
public void testUserDefinedErrorParsers() throws Exception {
final String TESTING_ID = "org.eclipse.cdt.core.test.errorparser";
final String TESTING_NAME = "An error parser";
// reset parsers
{
ErrorParserManager.setUserDefinedErrorParsers(null);
String all = ErrorParserManager.toDelimitedString(ErrorParserManager.getErrorParserAvailableIds());
String extensions = ErrorParserManager.toDelimitedString(ErrorParserManager.getErrorParserExtensionIds());
assertEquals(all, extensions);
}
{
ErrorParserManager.setUserDefinedErrorParsers(new IErrorParserNamed[] {
new ErrorParserNamedWrapper(TESTING_ID, TESTING_NAME, new DummyErrorParser()),
});
String all = ErrorParserManager.toDelimitedString(ErrorParserManager.getErrorParserAvailableIds());
String extensions = ErrorParserManager.toDelimitedString(ErrorParserManager.getErrorParserExtensionIds());
assertFalse(all.equals(extensions));
}
}
/**
* Test setting/retrieval of default error parser IDs preferences.
*
* @throws Exception...
*/
public void testDefaultErrorParserIds() throws Exception {
final String[] availableParserIds = ErrorParserManager.getErrorParserAvailableIds();
assertNotNull(availableParserIds);
final String[] initialDefaultErrorParserIds = ErrorParserManager.getDefaultErrorParserIds();
// preconditions
{
String[] defaultErrorParserIds = ErrorParserManager.getDefaultErrorParserIds();
assertNotNull(defaultErrorParserIds);
assertEquals(ErrorParserManager.toDelimitedString(availableParserIds),
ErrorParserManager.toDelimitedString(defaultErrorParserIds));
}
// setDefaultErrorParserIds
{
String[] newDefaultErrorParserIds = {
"org.eclipse.cdt.core.test.errorparser0",
"org.eclipse.cdt.core.test.errorparser1",
"org.eclipse.cdt.core.test.errorparser2",
};
ErrorParserManager.setDefaultErrorParserIds(newDefaultErrorParserIds);
String[] defaultErrorParserIds = ErrorParserManager.getDefaultErrorParserIds();
assertNotNull(defaultErrorParserIds);
assertEquals(ErrorParserManager.toDelimitedString(newDefaultErrorParserIds),
ErrorParserManager.toDelimitedString(defaultErrorParserIds));
}
// reset
{
ErrorParserManager.setDefaultErrorParserIds(null);
String[] defaultErrorParserIds = ErrorParserManager.getDefaultErrorParserIds();
assertNotNull(defaultErrorParserIds);
assertEquals(ErrorParserManager.toDelimitedString(availableParserIds),
ErrorParserManager.toDelimitedString(defaultErrorParserIds));
}
}
/**
* Test serialization of user defined error parsers.
*
* @throws Exception...
*/
public void testSerializeErrorParser() throws Exception {
final String TESTING_ID = "org.eclipse.cdt.core.test.errorparser";
final String TESTING_NAME = "An error parser";
{
// Create error parser
IErrorParser errorParser = new GCCErrorParser();
// Add to available parsers
ErrorParserExtensionManager.setUserDefinedErrorParsersInternal(new IErrorParserNamed[] {new ErrorParserNamedWrapper(TESTING_ID, TESTING_NAME, errorParser)});
assertNotNull(ErrorParserManager.getErrorParserCopy(TESTING_ID));
assertEquals(TESTING_NAME, ErrorParserManager.getErrorParserCopy(TESTING_ID).getName());
// Serialize in persistent storage
ErrorParserExtensionManager.serializeUserDefinedErrorParsers();
}
{
// Remove from available parsers
ErrorParserExtensionManager.setUserDefinedErrorParsersInternal(null);
assertNull(ErrorParserManager.getErrorParserCopy(TESTING_ID));
}
{
// Re-load from persistent storage and check it out
ErrorParserExtensionManager.loadUserDefinedErrorParsers();
IErrorParserNamed errorParser = ErrorParserManager.getErrorParserCopy(TESTING_ID);
assertNotNull(errorParser);
assertEquals(TESTING_NAME, errorParser.getName());
assertTrue(errorParser instanceof ErrorParserNamedWrapper);
assertTrue(((ErrorParserNamedWrapper)errorParser).getErrorParser() instanceof GCCErrorParser);
}
{
// Remove from available parsers as clean-up
ErrorParserExtensionManager.setUserDefinedErrorParsersInternal(null);
assertNull(ErrorParserManager.getErrorParserCopy(TESTING_ID));
}
}
/**
* Test serialization of user defined RegexErrorParser.
*
* @throws Exception...
*/
public void testSerializeRegexErrorParser() throws Exception {
final String TESTING_ID = "org.eclipse.cdt.core.test.regexerrorparser";
final String TESTING_NAME = "Regex Error Parser";
final String ALL_IDS = ErrorParserManager.toDelimitedString(ErrorParserManager.getErrorParserAvailableIds());
{
// Create error parser with the same id as in eclipse registry
RegexErrorParser regexErrorParser = new RegexErrorParser(TESTING_ID, TESTING_NAME);
regexErrorParser.addPattern(new RegexErrorPattern("Pattern-Y",
"line-Y", "file-Y", "description-Y", null, IMarkerGenerator.SEVERITY_WARNING, false));
// Add to available parsers
ErrorParserExtensionManager.setUserDefinedErrorParsersInternal(new IErrorParserNamed[] {regexErrorParser});
assertNotNull(ErrorParserManager.getErrorParserCopy(TESTING_ID));
// And serialize in persistent storage
ErrorParserExtensionManager.serializeUserDefinedErrorParsers();
}
{
// Remove from available parsers
ErrorParserExtensionManager.setUserDefinedErrorParsersInternal(null);
assertNull(ErrorParserManager.getErrorParserCopy(TESTING_ID));
}
{
// Re-load from persistent storage and check it out
ErrorParserExtensionManager.loadUserDefinedErrorParsers();
String all = ErrorParserManager.toDelimitedString(ErrorParserManager.getErrorParserAvailableIds());
assertTrue(all.contains(TESTING_ID));
IErrorParser errorParser = ErrorParserManager.getErrorParserCopy(TESTING_ID);
assertNotNull(errorParser);
assertTrue(errorParser instanceof RegexErrorParser);
RegexErrorParser regexErrorParser = (RegexErrorParser)errorParser;
assertEquals(TESTING_ID, regexErrorParser.getId());
assertEquals(TESTING_NAME, regexErrorParser.getName());
RegexErrorPattern[] errorPatterns = regexErrorParser.getPatterns();
assertEquals(1, errorPatterns.length);
assertEquals("Pattern-Y", errorPatterns[0].getPattern());
}
{
// Remove from available parsers and serialize
ErrorParserExtensionManager.setUserDefinedErrorParsersInternal(null);
ErrorParserExtensionManager.serializeUserDefinedErrorParsers();
// Re-load from persistent storage and check it out
ErrorParserExtensionManager.loadUserDefinedErrorParsers();
String all = ErrorParserManager.toDelimitedString(ErrorParserManager.getErrorParserAvailableIds());
assertEquals(ALL_IDS, all);
}
}
/**
* Make sure special characters are serialized properly.
*
* @throws Exception...
*/
public void testSerializeRegexErrorParserSpecialCharacters() throws Exception {
final String TESTING_ID = "org.eclipse.cdt.core.test.regexerrorparser";
final String TESTING_NAME = "<>\"'\\& Error Parser";
final String TESTING_REGEX = "Pattern-<>\"'\\&";
final String ALL_IDS = ErrorParserManager.toDelimitedString(ErrorParserManager.getErrorParserAvailableIds());
{
// Create error parser with the same id as in eclipse registry
RegexErrorParser regexErrorParser = new RegexErrorParser(TESTING_ID, TESTING_NAME);
regexErrorParser.addPattern(new RegexErrorPattern(TESTING_REGEX,
"line-<>\"'\\&", "file-<>\"'\\&", "description-<>\"'\\&", null, IMarkerGenerator.SEVERITY_WARNING, false));
// Add to available parsers
ErrorParserExtensionManager.setUserDefinedErrorParsersInternal(new IErrorParserNamed[] {regexErrorParser});
assertNotNull(ErrorParserManager.getErrorParserCopy(TESTING_ID));
// And serialize in persistent storage
ErrorParserExtensionManager.serializeUserDefinedErrorParsers();
}
{
// Re-load from persistent storage and check it out
ErrorParserExtensionManager.loadUserDefinedErrorParsers();
String all = ErrorParserManager.toDelimitedString(ErrorParserManager.getErrorParserAvailableIds());
assertTrue(all.contains(TESTING_ID));
IErrorParser errorParser = ErrorParserManager.getErrorParserCopy(TESTING_ID);
assertNotNull(errorParser);
assertTrue(errorParser instanceof RegexErrorParser);
RegexErrorParser regexErrorParser = (RegexErrorParser)errorParser;
assertEquals(TESTING_ID, regexErrorParser.getId());
assertEquals(TESTING_NAME, regexErrorParser.getName());
RegexErrorPattern[] errorPatterns = regexErrorParser.getPatterns();
assertEquals(1, errorPatterns.length);
assertEquals(TESTING_REGEX, errorPatterns[0].getPattern());
}
}
/**
* Check that default parser IDs are stored properly.
*
* @throws Exception...
*/
public void testSerializeDefaultErrorParserIds() throws Exception {
final String[] testingDefaultErrorParserIds = {
"org.eclipse.cdt.core.test.errorparser0",
"org.eclipse.cdt.core.test.errorparser1",
"org.eclipse.cdt.core.test.errorparser2",
};
final String TESTING_IDS = ErrorParserManager.toDelimitedString(testingDefaultErrorParserIds);
final String DEFAULT_IDS = ErrorParserManager.toDelimitedString(ErrorParserManager.getDefaultErrorParserIds());
{
// setDefaultErrorParserIds
ErrorParserExtensionManager.setDefaultErrorParserIdsInternal(testingDefaultErrorParserIds);
String[] defaultErrorParserIds = ErrorParserManager.getDefaultErrorParserIds();
assertNotNull(defaultErrorParserIds);
assertEquals(TESTING_IDS, ErrorParserManager.toDelimitedString(defaultErrorParserIds));
// serialize them
ErrorParserExtensionManager.serializeDefaultErrorParserIds();
}
{
// Remove from internal list
ErrorParserExtensionManager.setDefaultErrorParserIdsInternal(null);
assertEquals(DEFAULT_IDS, ErrorParserManager.toDelimitedString(ErrorParserManager.getDefaultErrorParserIds()));
}
{
// Re-load from persistent storage and check it out
ErrorParserExtensionManager.loadDefaultErrorParserIds();
String[] defaultErrorParserIds = ErrorParserManager.getDefaultErrorParserIds();
assertNotNull(defaultErrorParserIds);
assertEquals(TESTING_IDS, ErrorParserManager.toDelimitedString(defaultErrorParserIds));
}
{
// Reset IDs and serialize
ErrorParserExtensionManager.setDefaultErrorParserIdsInternal(null);
ErrorParserExtensionManager.serializeDefaultErrorParserIds();
// Check that default IDs are loaded
ErrorParserExtensionManager.loadDefaultErrorParserIds();
String[] defaultErrorParserIds = ErrorParserManager.getDefaultErrorParserIds();
assertNotNull(defaultErrorParserIds);
assertEquals(DEFAULT_IDS, ErrorParserManager.toDelimitedString(defaultErrorParserIds));
}
}
/**
* Test retrieval of error parser, clone() and equals().
*
* @throws Exception...
*/
public void testGetErrorParserCopy() throws Exception {
{
IErrorParserNamed clone1 = ErrorParserManager.getErrorParserCopy(REGEX_ERRORPARSER_ID);
IErrorParserNamed clone2 = ErrorParserManager.getErrorParserCopy(REGEX_ERRORPARSER_ID);
assertEquals(clone1, clone2);
assertNotSame(clone1, clone2);
}
{
IErrorParserNamed clone1 = ErrorParserManager.getErrorParserCopy(GCC_ERRORPARSER_ID);
IErrorParserNamed clone2 = ErrorParserManager.getErrorParserCopy(GCC_ERRORPARSER_ID);
assertEquals(clone1, clone2);
assertNotSame(clone1, clone2);
assertTrue(clone1 instanceof ErrorParserNamedWrapper);
assertTrue(clone2 instanceof ErrorParserNamedWrapper);
IErrorParser gccClone1 = ((ErrorParserNamedWrapper)clone1).getErrorParser();
IErrorParser gccClone2 = ((ErrorParserNamedWrapper)clone2).getErrorParser();
assertNotSame(clone1, clone2);
}
}
}

View file

@ -159,4 +159,21 @@
</run>
</filesystem>
</extension>
<extension
id="RegexErrorParserId"
name="Test Plugin RegexErrorParser"
point="org.eclipse.cdt.core.ErrorParser">
<errorparser
id="org.eclipse.cdt.core.tests.RegexErrorParserId"
name="Test Plugin RegexErrorParser">
<pattern
description-expr="$3"
eat-processed-line="true"
file-expr="$1"
line-expr="$2"
regex="(.*):(.*):regex (.*)"
severity="Error">
</pattern>
</errorparser>
</extension>
</plugin>

View file

@ -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

View file

@ -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 -->
<!-- =================================================================================== -->

View file

@ -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 &lt;samp&gt;org.eclipse.cdt.core.IErrorParser&lt;/samp&gt; 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 &quot;pattern&quot; to configure RegexErrorParser.
</documentation>
</annotation>
<complexType>
<attribute name="severity" use="required">
<annotation>
<documentation>
Attribute &quot;severity&quot; specifies which severity should be used to display the marker in Problems View. There are 3 levels of severity, &quot;Error&quot;, &quot;Warning&quot; and &quot;Info&quot;. &quot;Ignore&quot; 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>
&quot;Replacement&quot; expression composed from capturing groups defined in regex to define the file.
</documentation>
</annotation>
</attribute>
<attribute name="line-expr" type="string">
<annotation>
<documentation>
&quot;Replacement&quot; 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>
&quot;Replacement&quot; expression composed from capturing groups defined in regex to define the description (i.e. &quot;$1: $2&quot;). It is possible to specify more than one capturing group in such expression.
</documentation>
</annotation>
</attribute>
<attribute name="variable-expr" type="string">
<annotation>
<documentation>
&quot;Replacement&quot; 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. &quot;No&quot; 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 {&lt;br/&gt;
<documentation>
Plug-ins that want to extend this extension point must implement &lt;samp&gt;org.eclipse.cdt.core.IErrorParser&lt;/samp&gt; interface.
&lt;br/&gt;
It is recommended to extend &lt;samp&gt;org.eclipse.cdt.core.errorparsers.AbstractErrorParser&lt;/samp&gt; for most cases.
For most cases it is sufficient to configure RegexErrorParser which is provided by default.
Another good choice is to extend &lt;samp&gt;org.eclipse.cdt.core.errorparsers.AbstractErrorParser&lt;/samp&gt; as done in the example.
&lt;br/&gt;
ErrorParsers dealing with multi-line messages should implement &lt;samp&gt;org.eclipse.cdt.core.IErrorParser2&lt;/samp&gt; interface.
</documentation>
@ -132,8 +221,7 @@ All rights reserved. This program and the accompanying materials&lt;br/&gt;
are made available under the terms of the Eclipse Public License v1.0&lt;br/&gt;
which accompanies this distribution, and is available at&lt;br/&gt;
http://www.eclipse.org/legal/epl-v10.html&lt;br/&gt;
</documentation>
</annotation>
</schema>
</schema>

View file

@ -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) {

View file

@ -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;
}
}

View file

@ -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();
}

View file

@ -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);
}
}

View file

@ -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;
}
}

View file

@ -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);
}
}

View file

@ -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;
}
}

View file

@ -0,0 +1,34 @@
/*******************************************************************************
* Copyright (c) 2009, 2009 Andrew Gvozdev 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 - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.internal.ui.dialogs;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
/**
* The IInputStatusValidator is the interface for IStatus validators.
*/
public interface IInputStatusValidator {
/**
* Validates the given string. Returns the status with an error/warning/info message to display if the new
* text fails validation.
*
* @param newText
* the text to check for validity
*
* @return {@link IStatus} object. For the purpose of validation severity and message are considered.
* <li/>{@link Status#OK_STATUS} or any {@link IStatus#OK} to indicate no error.
* <li/>{@link IStatus#ERROR} indicates an error.
* <li/>{@link IStatus#WARNING} indicates a warning.
* <li/>{@link IStatus#INFO} indicates an informational message.
*/
public IStatus isValid(String newText);
}

View file

@ -0,0 +1,208 @@
/*******************************************************************************
* Copyright (c) 2009, 2009 Andrew Gvozdev 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 - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.internal.ui.dialogs;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.StatusDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
/**
* An input dialog for soliciting an input string from the user.
* The string can be validated. In case of problem error/warning/info message
* is shown in status line and decorated with appropriate status icon.
* <p>
* This concrete dialog class can be instantiated as is, or further subclassed as required.
* </p>
*/
public class InputStatusDialog extends StatusDialog {
/**
* The title of the dialog.
*/
private String title;
/**
* The message to display, or <code>null</code> if none.
*/
private String message;
/**
* The input value; the empty string by default.
*/
private String value = "";//$NON-NLS-1$
/**
* The input validator, or <code>null</code> if none.
*/
private IInputStatusValidator validator;
/**
* Input text widget.
*/
private Text text;
/**
* Creates an input dialog with OK and Cancel buttons. Note that the dialog
* will have no visual representation (no widgets) until it is told to open.
* <p>
* Note that the <code>open</code> method blocks for input dialogs.
* </p>
*
* @param parentShell
* the parent shell, or <code>null</code> to create a top-level
* shell
* @param dialogTitle
* the dialog title, or <code>null</code> if none
* @param dialogMessage
* the dialog message, or <code>null</code> if none
* @param initialValue
* the initial input value, or <code>null</code> if none
* (equivalent to the empty string)
* @param validator
* an input validator, or <code>null</code> if none
* For a validator, following return statuses are recognized:
* <li/>{@link Status#OK_STATUS} or any {@link IStatus#OK} to indicate no error.
* <li/>{@link IStatus#ERROR} indicates an error.
* <li/>{@link IStatus#WARNING} indicates a warning.
* <li/>{@link IStatus#INFO} indicates an informational message
*/
public InputStatusDialog(Shell parentShell, String dialogTitle, String dialogMessage,
String initialValue, IInputStatusValidator validator) {
super(parentShell);
this.title = dialogTitle;
if (dialogMessage == null) {
this.message = ""; //$NON-NLS-1$
} else {
this.message = dialogMessage;
}
if (initialValue == null) {
this.value = ""; //$NON-NLS-1$
} else {
this.value = initialValue;
}
this.validator = validator;
}
/*
* (non-Javadoc) Method declared on Dialog.
*/
@Override
protected void buttonPressed(int buttonId) {
if (buttonId == IDialogConstants.OK_ID) {
value = text.getText();
} else {
value = null;
}
super.buttonPressed(buttonId);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.window.Window#configureShell(org.eclipse.swt.widgets.Shell)
*/
@Override
protected void configureShell(Shell shell) {
super.configureShell(shell);
if (title != null) {
shell.setText(title);
}
}
@Override
protected Control createDialogArea(Composite parent) {
Composite composite = (Composite) super.createDialogArea(parent);
Label label = new Label(composite, SWT.WRAP);
label.setText(message);
GridData data = new GridData(GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL
| GridData.HORIZONTAL_ALIGN_FILL | GridData.VERTICAL_ALIGN_CENTER);
data.widthHint = convertHorizontalDLUsToPixels(IDialogConstants.MINIMUM_MESSAGE_AREA_WIDTH);
label.setLayoutData(data);
label.setFont(parent.getFont());
text = new Text(composite, getInputTextStyle());
text.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL | GridData.HORIZONTAL_ALIGN_FILL));
text.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
validateInput();
}
});
text.setFocus();
if (value != null) {
text.setText(value);
text.selectAll();
}
applyDialogFont(composite);
return composite;
}
/**
* Returns the text area.
*
* @return the text area
*/
protected Text getText() {
return text;
}
/**
* Returns the validator.
*
* @return the validator
*/
protected IInputStatusValidator getValidator() {
return validator;
}
public String getValue() {
return value;
}
/**
* Validates the input.
* <p>
* The default implementation of this framework method delegates the request to the supplied input
* validator object; if it finds the input invalid, the error message is displayed in the dialog's message
* line. This hook method is called whenever the text changes in the input field.
* </p>
*/
protected void validateInput() {
IStatus status = Status.OK_STATUS;
if (validator != null) {
status = validator.isValid(text.getText());
}
updateStatus(status);
}
/**
* Returns the style bits that should be used for the input text field. Defaults to a single line entry.
* Subclasses may override.
*
* @return the integer style bits that should be used when creating the input text
*
*/
protected int getInputTextStyle() {
return SWT.SINGLE | SWT.BORDER;
}
}

View file

@ -57,6 +57,52 @@ public class DialogsMessages extends NLS {
public static String DocCommentOwnerBlock_SelectDocToolDescription;
public static String DocCommentOwnerCombo_None;
public static String DocCommentOwnerComposite_DocumentationToolGroupTitle;
/** @since 5.2 */
public static String RegexErrorParserOptionPage_ConsumeNo;
/** @since 5.2 */
public static String RegexErrorParserOptionPage_ConsumeYes;
/** @since 5.2 */
public static String RegexErrorParserOptionPage_DescriptionColumn;
/** @since 5.2 */
public static String RegexErrorParserOptionPage_EatColumn;
/** @since 5.2 */
public static String RegexErrorParserOptionPage_FileColumn;
/** @since 5.2 */
public static String RegexErrorParserOptionPage_LineColumn;
/** @since 5.2 */
public static String RegexErrorParserOptionPage_LinkToPreferencesMessage;
/** @since 5.2 */
public static String RegexErrorParserOptionPage_Pattern_Column;
/** @since 5.2 */
public static String RegexErrorParserOptionPage_SeverityColumn;
/** @since 5.2 */
public static String RegexErrorParserOptionPage_SeverityError;
/** @since 5.2 */
public static String RegexErrorParserOptionPage_SeverityIgnore;
/** @since 5.2 */
public static String RegexErrorParserOptionPage_SeverityInfo;
/** @since 5.2 */
public static String RegexErrorParserOptionPage_SeverityWarning;
/** @since 5.2 */
public static String RegexErrorParserOptionPage_Title;
/** @since 5.2 */
public static String RegexErrorParserOptionPage_TooltipConsume;
/** @since 5.2 */
public static String RegexErrorParserOptionPage_TooltipDescription;
/** @since 5.2 */
public static String RegexErrorParserOptionPage_TooltipFile;
/** @since 5.2 */
public static String RegexErrorParserOptionPage_TooltipLine;
/** @since 5.2 */
public static String RegexErrorParserOptionPage_TooltipPattern;
/** @since 5.2 */
public static String RegexErrorParserOptionPage_TooltipSeverity;
/** @since 5.2 */
public static String RegularExpression_EmptyPattern;
/** @since 5.2 */
public static String RegularExpression_Validate;
/** @since 5.2 */
public static String RegularExpression_Enter;
static {
// initialize resource bundle

View file

@ -41,3 +41,26 @@ IndexerStrategyBlock_immediateUpdate=Update index immediately after every file-s
IndexerStrategyBlock_buildConfigGroup=Build configuration for the indexer
IndexerStrategyBlock_activeBuildConfig=Use active build configuration
IndexerStrategyBlock_specificBuildConfig=Use the build configuration specified in the project's indexer settings
RegexErrorParserOptionPage_ConsumeNo=No
RegexErrorParserOptionPage_ConsumeYes=
RegexErrorParserOptionPage_DescriptionColumn=Description
RegexErrorParserOptionPage_EatColumn=Consume
RegexErrorParserOptionPage_FileColumn=File
RegexErrorParserOptionPage_LineColumn=Line
RegexErrorParserOptionPage_LinkToPreferencesMessage=These options are workspace-wide and can be changed in <a href="workspace">Workspace Settings</a>.
RegexErrorParserOptionPage_Pattern_Column=Pattern
RegexErrorParserOptionPage_SeverityColumn=Severity
RegexErrorParserOptionPage_SeverityError=Error
RegexErrorParserOptionPage_SeverityIgnore=Ignore
RegexErrorParserOptionPage_SeverityInfo=Info
RegexErrorParserOptionPage_SeverityWarning=Warning
RegexErrorParserOptionPage_Title=Error Parser Options
RegexErrorParserOptionPage_TooltipConsume=Use ''{0}'' to allow to re-process matching input line by other patterns
RegexErrorParserOptionPage_TooltipDescription=Description as replacement expression (i.e. $3)
RegexErrorParserOptionPage_TooltipFile=File as replacement expression (i.e. $1)
RegexErrorParserOptionPage_TooltipLine=Line as replacement expression (i.e. $2)
RegexErrorParserOptionPage_TooltipPattern=Java regular expression specifying capturing groups for 'File', 'Line' and 'Description'
RegexErrorParserOptionPage_TooltipSeverity=Severity of marker
RegularExpression_EmptyPattern=Empty pattern
RegularExpression_Validate=Validate Regular Expression
RegularExpression_Enter=Enter regular expression (use Ctrl+Space for content assist):

View file

@ -0,0 +1,752 @@
/*******************************************************************************
* Copyright (c) 2009, 2009 Andrew Gvozdev 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 - Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.ui.dialogs;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.ComboBoxCellEditor;
import org.eclipse.jface.viewers.EditingSupport;
import org.eclipse.jface.viewers.TableLayout;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.jface.viewers.TextCellEditor;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Link;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.PreferencesUtil;
import org.eclipse.ui.fieldassist.ContentAssistCommandAdapter;
import com.ibm.icu.text.MessageFormat;
import org.eclipse.cdt.core.ErrorParserManager;
import org.eclipse.cdt.core.IErrorParserNamed;
import org.eclipse.cdt.core.IMarkerGenerator;
import org.eclipse.cdt.core.errorparsers.RegexErrorParser;
import org.eclipse.cdt.core.errorparsers.RegexErrorPattern;
import org.eclipse.cdt.ui.newui.AbstractCPropertyTab;
import org.eclipse.cdt.internal.ui.dialogs.MessageLine;
import org.eclipse.cdt.internal.ui.util.PixelConverter;
import org.eclipse.cdt.internal.ui.util.SWTUtil;
import org.eclipse.cdt.internal.ui.util.TableLayoutComposite;
/**
* Options page for RegexErrorParser in Error Parsers Tab of properties/preferences.
*
* @noextend This class is not intended to be subclassed by clients.
* @since 5.2
*/
public final class RegexErrorParserOptionPage extends AbstractCOptionPage {
private static final String WORKSPACE_PREFERENCE_PAGE = "org.eclipse.cdt.make.ui.preferences.BuildSettings"; //$NON-NLS-1$
private static final int BUTTON_ADD = 0;
private static final int BUTTON_DELETE = 1;
private static final int BUTTON_MOVEUP = 2;
private static final int BUTTON_MOVEDOWN = 3;
private static final String[] BUTTONS = new String[] {
AbstractCPropertyTab.ADD_STR,
AbstractCPropertyTab.DEL_STR,
AbstractCPropertyTab.MOVEUP_STR,
AbstractCPropertyTab.MOVEDOWN_STR,
};
private static final String OOPS = "OOPS"; //$NON-NLS-1$
private Table fTable;
private TableViewer fTableViewer;
private Button[] fButtons = null;
private RegexErrorParser fErrorParser;
private boolean fEditable;
/**
* Provides generic implementation for overridden methods.
* One purpose is to make it easier for subclasses to operate with {@link RegexErrorPattern},
* another is to provide content assist.
*
*/
private abstract class RegexPatternEditingSupport extends EditingSupport {
private final TableViewer tableViewer;
/**
* Constructor.
*
* @param viewer - table viewer where to provide editing support.
* @param isFindStyle - if "find" or "replace" style for potential content assist,
* see {@link FindReplaceDocumentAdapterContentProposalProvider}.
*/
public RegexPatternEditingSupport(TableViewer viewer, boolean isFindStyle) {
super(viewer);
tableViewer = viewer;
}
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.EditingSupport#canEdit(java.lang.Object)
*/
@Override
protected boolean canEdit(Object element) {
return fEditable;
}
/**
* The intention of RegexPatternEditingSupport is to provide Regex content assist
* during in-table editing. However having problems with mouse selection and
* {@link ContentAssistCommandAdapter} using {@link FindReplaceDocumentAdapterContentProposalProvider}
* is removed for time being. See bug 288982 for more details.
*
* @see org.eclipse.jface.viewers.EditingSupport#getCellEditor(java.lang.Object)
*/
@Override
protected CellEditor getCellEditor(Object element) {
CellEditor editor = new TextCellEditor(tableViewer.getTable());
return editor;
}
/**
* Get value from {@link RegexErrorPattern}. This is column-specific value.
*
* @param regexErrorPattern - pattern to query.
* @return retrieved value
*/
abstract protected Object getFromPattern(RegexErrorPattern regexErrorPattern);
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.EditingSupport#getValue(java.lang.Object)
*/
@Override
protected Object getValue(Object element) {
if (element instanceof RegexErrorPattern) {
RegexErrorPattern regexErrorPattern = (RegexErrorPattern) element;
return getFromPattern(regexErrorPattern);
}
return OOPS;
}
/**
* Set value into one of the pattern's field. Which field - it's column-specific.
*
* @param regexErrorPattern - pattern to set the field
* @param value - value to set
*/
abstract protected void setToPattern(RegexErrorPattern regexErrorPattern, String value);
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.EditingSupport#setValue(java.lang.Object, java.lang.Object)
*/
@Override
protected void setValue(Object element, Object value) {
if (element instanceof RegexErrorPattern && (value instanceof String)) {
String stringValue = (String) value;
RegexErrorPattern errorPattern = (RegexErrorPattern) element;
setToPattern(errorPattern, stringValue);
tableViewer.update(element, null);
}
}
}
/**
* Constructor.
*
* @param errorparser - regex error parser for which to display options.
* @param editable - if error parser is editable and allowed to change its patterns
*/
public RegexErrorParserOptionPage(RegexErrorParser errorparser, boolean editable) {
fErrorParser = errorparser;
fEditable = editable;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets.Composite)
*/
@Override
public void createControl(Composite parent) {
Group group = new Group(parent, SWT.SHADOW_ETCHED_IN);
group.setText(DialogsMessages.RegexErrorParserOptionPage_Title);
GridLayout gridLayout = new GridLayout(2, true);
gridLayout.makeColumnsEqualWidth = false;
gridLayout.marginRight = -10;
gridLayout.marginLeft = -4;
group.setLayout(gridLayout);
group.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
Composite composite = new Composite(group, SWT.NONE);
GridLayout layout = new GridLayout();
layout.numColumns = 2;
layout.marginWidth = 1;
layout.marginHeight = 1;
layout.marginRight = 1;
composite.setLayout(layout);
composite.setLayoutData(new GridData(GridData.FILL_BOTH));
Dialog.applyDialogFont(composite);
if (!fEditable)
createLinkToPreferences(composite);
createPatternsTable(group, composite);
if (fEditable) {
createButtons(composite);
// TODO: remove the warning before 6.1
MessageLine errorImageLabel = new MessageLine(composite, SWT.NONE);
errorImageLabel.setImage(JFaceResources.getImage(Dialog.DLG_IMG_MESSAGE_WARNING));
errorImageLabel.setText("Error Parser Options user entries may be lost on CDT update as persistence will be finalized with CDT 6.1 final release.");
}
setControl(group);
group.update();
}
private void createLinkToPreferences(final Composite parent) {
// must not be editable as error parser gets desynchronized with ErrorParsTab
Assert.isTrue(!fEditable);
Link link = new Link(parent, SWT.NONE);
link.setText(DialogsMessages.RegexErrorParserOptionPage_LinkToPreferencesMessage);
link.addListener(SWT.Selection, new Listener() {
public void handleEvent(Event event) {
// Use event.text to tell which link was used
PreferencesUtil.createPreferenceDialogOn(parent.getShell(), WORKSPACE_PREFERENCE_PAGE, null, null).open();
IErrorParserNamed errorParser = ErrorParserManager.getErrorParserCopy(fErrorParser.getId());
if (errorParser instanceof RegexErrorParser)
fErrorParser = (RegexErrorParser) errorParser;
else
fErrorParser = null;
initializeTable();
}
});
GridData gridData = new GridData(SWT.FILL, SWT.BOTTOM, true, false);
gridData.horizontalSpan = 2;
link.setLayoutData(gridData);
}
private static RegexErrorPattern newDummyPattern() {
return new RegexErrorPattern(null, null, null, null, null, IMarker.SEVERITY_ERROR, true);
}
private static String severityToString(int severity) {
switch (severity) {
case IMarkerGenerator.SEVERITY_INFO:
return DialogsMessages.RegexErrorParserOptionPage_SeverityInfo;
case IMarkerGenerator.SEVERITY_WARNING:
return DialogsMessages.RegexErrorParserOptionPage_SeverityWarning;
case IMarkerGenerator.SEVERITY_ERROR_BUILD:
case IMarkerGenerator.SEVERITY_ERROR_RESOURCE:
return DialogsMessages.RegexErrorParserOptionPage_SeverityError;
}
return DialogsMessages.RegexErrorParserOptionPage_SeverityIgnore;
}
private void initializeTable() {
RegexErrorPattern[] errorParserPatterns = fErrorParser!=null
? errorParserPatterns = fErrorParser.getPatterns()
: new RegexErrorPattern[0];
int len = errorParserPatterns.length;
int newLen = len;
RegexErrorPattern[] tablePatterns = new RegexErrorPattern[newLen];
System.arraycopy(errorParserPatterns, 0, tablePatterns, 0, len);
fTableViewer.setInput(tablePatterns);
fTableViewer.refresh();
}
private void createPatternsTable(Group group, Composite parent) {
TableLayoutComposite tableLayouter = new TableLayoutComposite(parent, SWT.NONE);
tableLayouter.addColumnData(new ColumnWeightData(10, true)); // severity
tableLayouter.addColumnData(new ColumnWeightData(40, true)); // pattern
tableLayouter.addColumnData(new ColumnWeightData(10, true)); // file
tableLayouter.addColumnData(new ColumnWeightData(10, true)); // line
tableLayouter.addColumnData(new ColumnWeightData(15, true)); // description
tableLayouter.addColumnData(new ColumnWeightData(10, true)); // eat line
int style= SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER;
if (fEditable) {
style = style | SWT.FULL_SELECTION;
}
fTable = new Table(tableLayouter, style);
fTable.setHeaderVisible(true);
fTable.setLinesVisible(true);
fTable.setEnabled(fEditable);
GridData gridData = new GridData(GridData.FILL_BOTH);
gridData.heightHint = SWTUtil.getTableHeightHint(fTable, 10);
gridData.widthHint = new PixelConverter(group).convertWidthInCharsToPixels(100);
tableLayouter.setLayoutData(gridData);
fTable.setLayout(new TableLayout());
fTable.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
updateButtons();
}
});
fTableViewer = new TableViewer(fTable);
fTableViewer.setUseHashlookup(true);
fTableViewer.setContentProvider(new ArrayContentProvider());
createSeverityColumn();
createPatternColumn();
createFileColumn();
createLineColumn();
createDescriptionColumn();
createEatLineColumn();
initializeTable();
}
private void createSeverityColumn() {
TableViewerColumn columnViewer = new TableViewerColumn(fTableViewer, SWT.NONE);
columnViewer.getColumn().setText(DialogsMessages.RegexErrorParserOptionPage_SeverityColumn);
columnViewer.getColumn().setResizable(true);
columnViewer.getColumn().setToolTipText(DialogsMessages.RegexErrorParserOptionPage_TooltipSeverity);
columnViewer.setLabelProvider(new ColumnLabelProvider() {
final ISharedImages images = PlatformUI.getWorkbench().getSharedImages();
@Override
public Image getImage(Object element) {
if (element instanceof RegexErrorPattern) {
RegexErrorPattern regexErrorPattern = (RegexErrorPattern) element;
switch (regexErrorPattern.getSeverity()) {
case IMarkerGenerator.SEVERITY_INFO:
return images.getImage(ISharedImages.IMG_OBJS_INFO_TSK);
case IMarkerGenerator.SEVERITY_WARNING:
return images.getImage(ISharedImages.IMG_OBJS_WARN_TSK);
case IMarkerGenerator.SEVERITY_ERROR_BUILD:
case IMarkerGenerator.SEVERITY_ERROR_RESOURCE:
return images.getImage(ISharedImages.IMG_OBJS_ERROR_TSK);
case RegexErrorPattern.SEVERITY_SKIP:
return images.getImage(ISharedImages.IMG_ELCL_REMOVE_DISABLED);
}
}
return null;
}
@Override
public String getText(Object element) {
if (element instanceof RegexErrorPattern) {
RegexErrorPattern regex = (RegexErrorPattern) element;
return severityToString(regex.getSeverity());
}
return severityToString(RegexErrorPattern.SEVERITY_SKIP);
}
});
columnViewer.setEditingSupport(new EditingSupport(fTableViewer) {
final String[] severityComboBoxArray = new String[] {
severityToString(IMarkerGenerator.SEVERITY_ERROR_RESOURCE),
severityToString(IMarkerGenerator.SEVERITY_WARNING),
severityToString(IMarkerGenerator.SEVERITY_INFO),
severityToString(RegexErrorPattern.SEVERITY_SKIP),
};
private int severityToIndex(int severity) {
String strSeverity = severityToString(severity);
for (int i = 0; i < severityComboBoxArray.length; i++) {
if (strSeverity.equals(severityComboBoxArray[i]))
return i;
}
return 0;
}
private int indexToSeverity(int index) {
String strCombo = severityComboBoxArray[index];
for (int i = 0; i < severityComboBoxArray.length; i++) {
if (severityToString(i).equals(strCombo))
return i;
}
return RegexErrorPattern.SEVERITY_SKIP;
}
@Override
protected boolean canEdit(Object element) {
return (element instanceof RegexErrorPattern) && fEditable;
}
@Override
protected CellEditor getCellEditor(Object element) {
return new ComboBoxCellEditor(fTableViewer.getTable(), severityComboBoxArray, SWT.READ_ONLY);
}
@Override
protected Object getValue(Object element) {
if (element instanceof RegexErrorPattern)
return severityToIndex(((RegexErrorPattern) element).getSeverity());
return RegexErrorPattern.SEVERITY_SKIP;
}
@Override
protected void setValue(Object element, Object value) {
if (element instanceof RegexErrorPattern && (value instanceof Integer)) {
((RegexErrorPattern) element).setSeverity(indexToSeverity(((Integer) value).intValue()));
fTableViewer.update(element, null);
}
}
});
}
private void createPatternColumn() {
TableViewerColumn columnViewer = new TableViewerColumn(fTableViewer, SWT.NONE);
columnViewer.getColumn().setText(DialogsMessages.RegexErrorParserOptionPage_Pattern_Column);
columnViewer.getColumn().setResizable(true);
columnViewer.getColumn().setToolTipText(DialogsMessages.RegexErrorParserOptionPage_TooltipPattern);
columnViewer.setLabelProvider(new ColumnLabelProvider() {
@Override
public String getText(Object element) {
if (element instanceof RegexErrorPattern) {
RegexErrorPattern regex = (RegexErrorPattern) element;
String pattern = regex.getPattern();
return pattern;
}
return OOPS;
}
});
columnViewer.setEditingSupport(new RegexPatternEditingSupport(fTableViewer, true) {
@Override
protected Object getFromPattern(RegexErrorPattern regexErrorPattern) {
return regexErrorPattern.getPattern();
}
@Override
protected void setToPattern(RegexErrorPattern regexErrorPattern, String value) {
if (!fEditable)
return;
try{
regexErrorPattern.setPattern(value);
} catch (Exception e) {
// to avoid recursive edits. the dialog is needed to ensure valid pattern on losing focus.
// this looks ugly and likely incorrect
fEditable = false;
RegularExpressionStatusDialog dialog= new RegularExpressionStatusDialog(getShell(), value);
if (dialog.open() == Window.OK) {
regexErrorPattern.setPattern(dialog.getValue());
}
fEditable = true;
}
}
});
}
private void createFileColumn() {
TableViewerColumn columnViewer = new TableViewerColumn(fTableViewer, SWT.NONE);
columnViewer.getColumn().setText(DialogsMessages.RegexErrorParserOptionPage_FileColumn);
columnViewer.getColumn().setToolTipText(DialogsMessages.RegexErrorParserOptionPage_TooltipFile);
columnViewer.getColumn().setResizable(true);
columnViewer.setLabelProvider(new ColumnLabelProvider() {
@Override
public String getText(Object element) {
if (element instanceof RegexErrorPattern) {
RegexErrorPattern regex = (RegexErrorPattern) element;
return regex.getFileExpression();
}
return OOPS;
}
});
columnViewer.setEditingSupport(new RegexPatternEditingSupport(fTableViewer, false) {
@Override
protected Object getFromPattern(RegexErrorPattern regexErrorPattern) {
return regexErrorPattern.getFileExpression();
}
@Override
protected void setToPattern(RegexErrorPattern regexErrorPattern, String value) {
regexErrorPattern.setFileExpression(value);
}
});
}
private void createLineColumn() {
TableViewerColumn columnViewer = new TableViewerColumn(fTableViewer, SWT.NONE);
columnViewer.getColumn().setText(DialogsMessages.RegexErrorParserOptionPage_LineColumn);
columnViewer.getColumn().setResizable(true);
columnViewer.getColumn().setToolTipText(DialogsMessages.RegexErrorParserOptionPage_TooltipLine);
columnViewer.setLabelProvider(new ColumnLabelProvider() {
@Override
public String getText(Object element) {
if (element instanceof RegexErrorPattern) {
RegexErrorPattern regex = (RegexErrorPattern) element;
return regex.getLineExpression();
}
return OOPS;
}
});
columnViewer.setEditingSupport(new RegexPatternEditingSupport(fTableViewer, false) {
@Override
protected Object getFromPattern(RegexErrorPattern regexErrorPattern) {
return regexErrorPattern.getLineExpression();
}
@Override
protected void setToPattern(RegexErrorPattern regexErrorPattern, String value) {
regexErrorPattern.setLineExpression(value);
}
});
}
private void createDescriptionColumn() {
TableViewerColumn columnViewer = new TableViewerColumn(fTableViewer, SWT.NONE);
columnViewer.getColumn().setText(DialogsMessages.RegexErrorParserOptionPage_DescriptionColumn);
columnViewer.getColumn().setResizable(true);
columnViewer.getColumn().setToolTipText(DialogsMessages.RegexErrorParserOptionPage_TooltipDescription);
columnViewer.setLabelProvider(new ColumnLabelProvider() {
@Override
public String getText(Object element) {
if (element instanceof RegexErrorPattern) {
RegexErrorPattern regex = (RegexErrorPattern) element;
return regex.getDescriptionExpression();
}
return OOPS;
}
});
columnViewer.setEditingSupport(new RegexPatternEditingSupport(fTableViewer, false) {
@Override
protected Object getFromPattern(RegexErrorPattern regexErrorPattern) {
return regexErrorPattern.getDescriptionExpression();
}
@Override
protected void setToPattern(RegexErrorPattern regexErrorPattern, String value) {
regexErrorPattern.setDescriptionExpression(value);
}
});
}
private void createEatLineColumn() {
final String EAT_NO = DialogsMessages.RegexErrorParserOptionPage_ConsumeNo;
final String EAT_YES = DialogsMessages.RegexErrorParserOptionPage_ConsumeYes;
final String[] eatLineComboBoxArray = new String[] { EAT_YES, EAT_NO, };
final int EAT_YES_INDEX = 0;
final int EAT_NO_INDEX = 1;
TableViewerColumn columnViewer = new TableViewerColumn(fTableViewer, SWT.NONE);
columnViewer.getColumn().setText(DialogsMessages.RegexErrorParserOptionPage_EatColumn);
columnViewer.getColumn().setResizable(true);
String message = MessageFormat.format(DialogsMessages.RegexErrorParserOptionPage_TooltipConsume, new Object[] { EAT_NO });
columnViewer.getColumn().setToolTipText(message);
columnViewer.setLabelProvider(new ColumnLabelProvider() {
@Override
public String getText(Object element) {
if (element instanceof RegexErrorPattern) {
RegexErrorPattern regex = (RegexErrorPattern) element;
if (!regex.isEatProcessedLine())
return EAT_NO;
}
return EAT_YES;
}
});
columnViewer.setEditingSupport(new EditingSupport(fTableViewer) {
@Override
protected boolean canEdit(Object element) {
return (element instanceof RegexErrorPattern) && fEditable;
}
@Override
protected CellEditor getCellEditor(Object element) {
return new ComboBoxCellEditor(fTableViewer.getTable(), eatLineComboBoxArray, SWT.READ_ONLY);
}
@Override
protected Object getValue(Object element) {
if (element instanceof RegexErrorPattern)
if (!((RegexErrorPattern) element).isEatProcessedLine())
return EAT_NO_INDEX;
return EAT_YES_INDEX;
}
@Override
protected void setValue(Object element, Object value) {
if ((element instanceof RegexErrorPattern) && (value instanceof Integer)) {
((RegexErrorPattern) element).setEatProcessedLine((Integer) value != EAT_NO_INDEX);
fTableViewer.update(element, null);
}
}
});
}
private void createButtons(Composite parent) {
Composite composite = new Composite(parent, SWT.NONE);
composite.setLayoutData(new GridData(GridData.FILL_VERTICAL));
composite.setLayout(new GridLayout(1, false));
fButtons = new Button[BUTTONS.length];
for (int i = 0; i < BUTTONS.length; i++) {
fButtons[i] = new Button(composite, SWT.PUSH);
GridData gridData = new GridData(SWT.FILL, SWT.CENTER, false, false);
gridData.minimumWidth = 80;
if (BUTTONS[i] != null) {
fButtons[i].setText(BUTTONS[i]);
fButtons[i].setEnabled(false);
} else {
// no button, but placeholder
fButtons[i].setVisible(false);
fButtons[i].setEnabled(false);
gridData.heightHint = 10;
}
fButtons[i].setLayoutData(gridData);
fButtons[i].addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent event) {
for (int i=0; i<fButtons.length; i++) {
if (fButtons[i].equals(event.widget)) {
buttonPressed(i);
return;
}
}
}
});
}
updateButtons();
}
private void updateButtons() {
if (fButtons!=null) {
int pos = fTable.getSelectionIndex();
int count = fTable.getItemCount();
int last = count-1;
boolean selected = pos>=0 && pos<=last;
fButtons[BUTTON_ADD].setEnabled(true);
fButtons[BUTTON_DELETE].setEnabled(selected);
fButtons[BUTTON_MOVEUP].setEnabled(selected && pos != 0);
fButtons[BUTTON_MOVEDOWN].setEnabled(selected && pos != last);
}
}
private void buttonPressed (int button) {
switch (button) {
case BUTTON_ADD:
addErrorPattern();
break;
case BUTTON_DELETE:
deleteErrorPattern();
break;
case BUTTON_MOVEUP:
moveItem(true);
break;
case BUTTON_MOVEDOWN:
moveItem(false);
break;
default:
break;
}
updateButtons();
}
private void addErrorPattern() {
int pos = fTable.getSelectionIndex();
int last = fTable.getItemCount()-1;
if (pos<0 || pos>last)
pos = last;
int newPos = pos + 1;
fTableViewer.insert(newDummyPattern(), newPos);
fTable.setSelection(newPos);
}
private void deleteErrorPattern() {
int pos = fTable.getSelectionIndex();
int last = fTable.getItemCount()-1;
if (pos>=0 && pos<=last) {
fTableViewer.remove(fTableViewer.getElementAt(pos));
fTable.setSelection(pos);
}
}
private void moveItem(boolean up) {
int pos = fTable.getSelectionIndex();
int count = fTable.getItemCount();
int last = count-1;
boolean selected = pos>=0 && pos<=last;
if (!selected || (up && pos==0) || (!up && pos==last))
return;
Object item = fTableViewer.getElementAt(pos);
fTableViewer.remove(item);
int newPos = up ? pos-1 : pos+1;
fTableViewer.insert(item, newPos);
fTable.setSelection(newPos);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.cdt.ui.dialogs.ICOptionPage#performApply(org.eclipse.core.runtime.IProgressMonitor)
*/
@Override
public void performApply(IProgressMonitor monitor) throws CoreException {
if (fErrorParser!=null && fEditable) {
fErrorParser.clearPatterns();
for (TableItem tableItem : fTable.getItems()) {
Object item = tableItem.getData();
if (item instanceof RegexErrorPattern) {
fErrorParser.addPattern((RegexErrorPattern)item);
}
}
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.cdt.ui.dialogs.ICOptionPage#performDefaults()
*/
@Override
public void performDefaults() {
// ErrorParsTas.performDefaults() will do all the work
}
}

View file

@ -0,0 +1,106 @@
/*******************************************************************************
* Copyright (c) 2009,2009 Andrew Gvozdev 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 - Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.ui.dialogs;
import java.util.regex.Pattern;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jface.fieldassist.TextContentAdapter;
import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.fieldassist.ContentAssistCommandAdapter;
import org.eclipse.cdt.internal.ui.dialogs.IInputStatusValidator;
import org.eclipse.cdt.internal.ui.dialogs.InputStatusDialog;
import org.eclipse.cdt.internal.ui.dialogs.StatusInfo;
/**
* Input Dialog for validating regular expression syntax.
*
* @since 5.2
*/
public class RegularExpressionStatusDialog extends InputStatusDialog {
private static final IInputStatusValidator fValidator = new IInputStatusValidator() {
public IStatus isValid(String newText) {
StatusInfo status = new StatusInfo();
if (newText.length() == 0) {
status.setWarning(DialogsMessages.RegularExpression_EmptyPattern);
} else {
try {
Pattern.compile(newText);
} catch (Exception e) {
// only first line of PatternSyntaxException is really descriptive
status.setError(e.getMessage().split("[\n\r]", 2)[0]); //$NON-NLS-1$
}
}
return status;
}
};
/**
* Constructor
*
* @param shell - the parent shell, or <code>null</code> to create a top-level shell
* @param initialValue the initial input value, or <code>null</code> if none
* (equivalent to the empty string)
*/
public RegularExpressionStatusDialog(Shell shell, String initialValue) {
super(shell, DialogsMessages.RegularExpression_Validate,
DialogsMessages.RegularExpression_Enter,
initialValue, fValidator);
}
/**
* Constructor
*
* @param shell - the parent shell, or <code>null</code> to create a top-level shell
* @param dialogTitle - the dialog title, or <code>null</code> if none
* @param dialogMessage - the dialog message, or <code>null</code> if none
* @param initialValue the initial input value, or <code>null</code> if none
* (equivalent to the empty string)
*/
public RegularExpressionStatusDialog(Shell shell, String dialogTitle, String dialogMessage, String initialValue) {
super(shell, dialogTitle, dialogMessage, initialValue, fValidator);
}
/* (non-Javadoc)
* @see org.eclipse.cdt.internal.ui.dialogs.InputStatusDialog#createDialogArea(org.eclipse.swt.widgets.Composite)
*/
@Override
protected Control createDialogArea(Composite parent) {
Control control = super.createDialogArea(parent);
new ContentAssistCommandAdapter(
getText(),
new TextContentAdapter(),
new FindReplaceDocumentAdapterContentProposalProvider(true),
null,
null,
true);
setHelpAvailable(false);
return control;
}
/* (non-Javadoc)
* @see org.eclipse.jface.dialogs.StatusDialog#create()
*/
@Override
public void create() {
super.create();
if (getValue().length()>0)
validateInput();
}
}

View file

@ -339,7 +339,7 @@ implements
GridData gd;
parentComposite = new Composite(c, SWT.NONE);
parentComposite.setLayoutData(gd= new GridData(GridData.FILL_BOTH));
gd.widthHint= 200;
gd.widthHint= 800;
itabs.clear();
if (!isSingle()) {
parentComposite.setLayout(new FillLayout());

View file

@ -7,234 +7,568 @@
*
* Contributors:
* Intel Corporation - Initial API and implementation
* Andrew Gvozdev (Quoin Inc.) - Regular expression error parsers
*******************************************************************************/
package org.eclipse.cdt.ui.newui;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.runtime.IExtension;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.CheckboxTableViewer;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.ui.PlatformUI;
import org.osgi.service.prefs.BackingStoreException;
import org.eclipse.cdt.core.CCorePlugin;
import com.ibm.icu.text.MessageFormat;
import org.eclipse.cdt.core.ErrorParserManager;
import org.eclipse.cdt.core.IErrorParserNamed;
import org.eclipse.cdt.core.errorparsers.RegexErrorParser;
import org.eclipse.cdt.core.settings.model.ICConfigurationDescription;
import org.eclipse.cdt.core.settings.model.ICMultiConfigDescription;
import org.eclipse.cdt.core.settings.model.ICResourceDescription;
import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.cdt.ui.dialogs.ICOptionPage;
import org.eclipse.cdt.ui.dialogs.RegexErrorParserOptionPage;
import org.eclipse.cdt.utils.ui.controls.TabFolderLayout;
import org.eclipse.cdt.internal.ui.ICHelpContextIds;
import org.eclipse.cdt.internal.ui.dialogs.IInputStatusValidator;
import org.eclipse.cdt.internal.ui.dialogs.InputStatusDialog;
import org.eclipse.cdt.internal.ui.dialogs.StatusInfo;
import org.eclipse.cdt.internal.ui.util.PixelConverter;
/**
* This class represents Error Parser Tab in Project Properties or workspace Preferences
*
* @noextend This class is not intended to be subclassed by clients.
*/
public class ErrorParsTab extends AbstractCPropertyTab {
private HashMap<String, String> mapParsers = new HashMap<String, String>();
private Table table;
private CheckboxTableViewer tv;
private ICConfigurationDescription cfgd;
private static final int DEFAULT_HEIGHT = 130;
private static final int BUTTON_ADD = 0;
private static final int BUTTON_EDIT = 1;
private static final int BUTTON_DELETE = 2;
// there is a separator instead of button = 3
private static final int BUTTON_MOVEUP = 4;
private static final int BUTTON_MOVEDOWN = 5;
private static final String[] BUTTONS = new String[] {
ADD_STR,
EDIT_STR,
DEL_STR,
null,
MOVEUP_STR,
MOVEDOWN_STR,
};
private static final String OOPS = "OOPS"; //$NON-NLS-1$
private Table fTable;
private CheckboxTableViewer fTableViewer;
private ICConfigurationDescription fCfgDesc;
private final Map<String, IErrorParserNamed> fAvailableErrorParsers = new LinkedHashMap<String, IErrorParserNamed>();
private final Map<String, ICOptionPage> fOptionsPageMap = new HashMap<String, ICOptionPage>();
private ICOptionPage fCurrentOptionsPage = null;
private Composite fCompositeForOptionsPage;
/* (non-Javadoc)
* @see org.eclipse.cdt.ui.newui.AbstractCPropertyTab#createControls(org.eclipse.swt.widgets.Composite)
*/
@Override
public void createControls(Composite parent) {
super.createControls(parent);
usercomp.setLayout(new FillLayout());
table = new Table(usercomp, SWT.BORDER | SWT.CHECK | SWT.SINGLE);
table.addSelectionListener(new SelectionAdapter() {
PlatformUI.getWorkbench().getHelpSystem().setHelp(usercomp, ICHelpContextIds.ERROR_PARSERS_PAGE);
usercomp.setLayout(new GridLayout(1, false));
// SashForm
SashForm sashForm = new SashForm(usercomp, SWT.NONE);
sashForm.setBackground(sashForm.getDisplay().getSystemColor(SWT.COLOR_GRAY));
sashForm.setOrientation(SWT.VERTICAL);
sashForm.setLayoutData(new GridData(GridData.FILL_BOTH));
GridLayout layout = new GridLayout(2, false);
layout.marginHeight = 5;
sashForm.setLayout(layout);
// table
Composite compositeSashForm = new Composite(sashForm, SWT.NONE);
compositeSashForm.setLayout(new GridLayout(2, false));
fTable = new Table(compositeSashForm, SWT.BORDER | SWT.CHECK | SWT.SINGLE);
fTable.setLayoutData(new GridData(GridData.FILL_BOTH));
fTable.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
displaySelectedOptionPage();
updateButtons();
}});
tv = new CheckboxTableViewer(table);
tv.setContentProvider(new IStructuredContentProvider() {
fTableViewer = new CheckboxTableViewer(fTable);
fTableViewer.setContentProvider(new IStructuredContentProvider() {
public Object[] getElements(Object inputElement) {
return (Object[])inputElement;
}
public void dispose() {}
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {}
});
tv.addCheckStateListener(new ICheckStateListener() {
public void checkStateChanged(CheckStateChangedEvent e) {
saveChecked();
fTableViewer.setLabelProvider(new LabelProvider() {
@Override
public String getText(Object element) {
if (element instanceof String) {
String id = (String)element;
IErrorParserNamed errorParser = fAvailableErrorParsers.get(id);
if (errorParser!=null) {
String name = errorParser.getName();
if (name!=null && name.length()>0) {
return name;
}
}
return UIMessages.getFormattedString("ErrorParsTab.error.NonAccessibleID", id); //$NON-NLS-1$
}
return OOPS;
}
});
initButtons(new String[] {
MOVEUP_STR, MOVEDOWN_STR, null,
UIMessages.getString("ErrorParsTab.0"), //$NON-NLS-1$
UIMessages.getString("ErrorParsTab.1") //$NON-NLS-1$
});
fTableViewer.addCheckStateListener(new ICheckStateListener() {
public void checkStateChanged(CheckStateChangedEvent e) {
saveChecked();
}});
// Buttons
Composite compositeButtons = new Composite(compositeSashForm, SWT.NONE);
compositeButtons.setLayoutData(new GridData(GridData.END));
initButtons(compositeButtons, BUTTONS);
fCompositeForOptionsPage = new Composite(sashForm, SWT.NULL);
GridData gd = new GridData();
fCompositeForOptionsPage.setLayout(new TabFolderLayout());
PixelConverter converter = new PixelConverter(parent);
gd.heightHint = converter.convertHorizontalDLUsToPixels(DEFAULT_HEIGHT);
gd.horizontalAlignment = GridData.FILL;
gd.grabExcessHorizontalSpace = true;
gd.grabExcessVerticalSpace = true;
gd.horizontalSpan = 2;
fCompositeForOptionsPage.setLayoutData(gd);
sashForm.setWeights(new int[] {50, 50});
// init data
ICResourceDescription resDecs = getResDesc();
fCfgDesc = resDecs!=null ? resDecs.getConfiguration() : null;
initMapParsers();
updateData(getResDesc());
}
protected void initMapParsers() {
mapParsers.clear();
IExtensionPoint point = Platform.getExtensionRegistry().getExtensionPoint(
CCorePlugin.PLUGIN_ID,
CCorePlugin.ERROR_PARSER_SIMPLE_ID
);
if (point != null) {
IExtension[] exts = point.getExtensions();
for (IExtension ext : exts) {
if (ext.getConfigurationElements().length > 0) {
mapParsers.put(ext.getUniqueIdentifier(), ext.getLabel());
private void initMapParsers() {
fAvailableErrorParsers.clear();
fOptionsPageMap.clear();
for (String id : ErrorParserManager.getErrorParserAvailableIds()) {
IErrorParserNamed errorParser = ErrorParserManager.getErrorParserCopy(id);
fAvailableErrorParsers.put(id, errorParser);
initializeOptionsPage(id);
}
String ids[];
if (fCfgDesc!=null) {
ICConfigurationDescription srcCfgDesc = fCfgDesc.getConfiguration();
if (srcCfgDesc instanceof ICMultiConfigDescription) {
String[][] ss = ((ICMultiConfigDescription)srcCfgDesc).getErrorParserIDs();
ids = CDTPrefUtil.getStrListForDisplay(ss);
} else {
ids = srcCfgDesc.getBuildSetting().getErrorParserIDs();
}
Set<String> setIds = new LinkedHashSet<String>(Arrays.asList(ids));
setIds.addAll(fAvailableErrorParsers.keySet());
fTableViewer.setInput(setIds.toArray(new String[0]));
} else {
fTableViewer.setInput(fAvailableErrorParsers.keySet().toArray(new String[0]));
ids = ErrorParserManager.getDefaultErrorParserIds();
}
fTableViewer.setCheckedElements(ids);
displaySelectedOptionPage();
}
private void initializeOptionsPage(String id) {
IErrorParserNamed errorParser = fAvailableErrorParsers.get(id);
if (errorParser!=null) {
String name = errorParser.getName();
if (name!=null && name.length()>0) {
// RegexErrorParser has an Options page
if (errorParser instanceof RegexErrorParser) {
// allow to edit only for Build Settings Preference Page (where cfgd==null)
RegexErrorParserOptionPage optionsPage = new RegexErrorParserOptionPage((RegexErrorParser) errorParser, isErrorParsersEditable());
fOptionsPageMap.put(id, optionsPage);
optionsPage.setContainer(page);
optionsPage.createControl(fCompositeForOptionsPage);
optionsPage.setVisible(false);
fCompositeForOptionsPage.layout(true);
}
}
}
}
private void displaySelectedOptionPage() {
if (fCurrentOptionsPage != null)
fCurrentOptionsPage.setVisible(false);
int pos = fTable.getSelectionIndex();
if (pos<0)
return;
String parserId = (String)fTable.getItem(pos).getData();
ICOptionPage optionsPage = fOptionsPageMap.get(parserId);
if (optionsPage != null) {
optionsPage.setVisible(true);
}
fCurrentOptionsPage = optionsPage;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.ui.newui.AbstractCPropertyTab#buttonPressed(int)
*/
@Override
public void buttonPressed (int n) {
switch (n) {
case 0: // up
case BUTTON_ADD:
addErrorParser();
break;
case BUTTON_EDIT:
editErrorParser();
break;
case BUTTON_DELETE:
deleteErrorParser();
break;
case BUTTON_MOVEUP:
moveItem(true);
break;
case 1: // down
case BUTTON_MOVEDOWN:
moveItem(false);
break;
case 2: // do nothing - it's not a button
break;
case 3: // check all
tv.setAllChecked(true);
saveChecked();
break;
case 4: // uncheck all
tv.setAllChecked(false);
saveChecked();
break;
default:
break;
}
updateButtons();
}
// Move item up / down
private void moveItem(boolean up) {
int n = table.getSelectionIndex();
if (n < 0 ||
(up && n == 0) ||
(!up && n+1 == table.getItemCount()))
int n = fTable.getSelectionIndex();
if (n < 0 || (up && n == 0) || (!up && n+1 == fTable.getItemCount()))
return;
TableData d = (TableData)tv.getElementAt(n);
boolean checked = tv.getChecked(d);
tv.remove(d);
n = up ? n - 1 : n + 1;
tv.insert(d, n);
tv.setChecked(d, checked);
table.setSelection(n);
String id = (String)fTableViewer.getElementAt(n);
boolean checked = fTableViewer.getChecked(id);
fTableViewer.remove(id);
n = up ? n-1 : n+1;
fTableViewer.insert(id, n);
fTableViewer.setChecked(id, checked);
fTable.setSelection(n);
saveChecked();
}
class TableData {
String key;
String value;
public TableData (String _key, String _value) {
key = _key;
value = _value;
}
@Override
public String toString() { return value; }
}
@Override
public void updateData(ICResourceDescription _cfgd) {
cfgd = _cfgd.getConfiguration();
if (mapParsers == null) return;
String[] ss = null;
if (page.isMultiCfg()) {
String[][] ids = ((ICMultiConfigDescription)cfgd).getErrorParserIDs();
ss = CDTPrefUtil.getStrListForDisplay(ids);
} else {
ss = cfgd.getBuildSetting().getErrorParserIDs();
}
ArrayList<TableData> data = new ArrayList<TableData>(mapParsers.size());
ArrayList<TableData> checked = new ArrayList<TableData>(ss.length);
HashMap<String, String> cloneMap = new HashMap<String, String>(mapParsers);
// add checked elements
for (String element : ss) {
String s = cloneMap.get(element);
if (s != null) {
TableData d = new TableData(element,s);
data.add(d);
checked.add(d);
cloneMap.remove(element);
private String makeId(String name) {
return CUIPlugin.PLUGIN_ID+'.'+name;
}
private void addErrorParser() {
IInputStatusValidator inputValidator = new IInputStatusValidator() {
public IStatus isValid(String newText) {
StatusInfo status = new StatusInfo();
if (newText.trim().length() == 0) {
status.setError(UIMessages.getString("ErrorParsTab.error.NonEmptyName")); //$NON-NLS-1$
} else if (newText.indexOf(ErrorParserManager.ERROR_PARSER_DELIMITER)>=0) {
String message = MessageFormat.format( UIMessages.getString("ErrorParsTab.error.IllegalCharacter"), //$NON-NLS-1$
new Object[] { ErrorParserManager.ERROR_PARSER_DELIMITER });
status.setError(message);
} else if (fAvailableErrorParsers.containsKey(makeId(newText))) {
status.setError(UIMessages.getString("ErrorParsTab.error.NonUniqueID")); //$NON-NLS-1$
}
return status;
}
};
InputStatusDialog addDialog = new InputStatusDialog(usercomp.getShell(),
UIMessages.getString("ErrorParsTab.title.Add"), //$NON-NLS-1$
UIMessages.getString("ErrorParsTab.label.EnterName"), //$NON-NLS-1$
UIMessages.getString("ErrorParsTab.label.DefaultRegexErrorParserName"), //$NON-NLS-1$
inputValidator);
addDialog.setHelpAvailable(false);
if (addDialog.open() == Window.OK) {
String newName = addDialog.getValue();
String newId = makeId(newName);
IErrorParserNamed errorParser = new RegexErrorParser(newId, newName);
fAvailableErrorParsers.put(newId, errorParser);
fTableViewer.add(newId);
fTableViewer.setChecked(newId, true);
fTable.setSelection(fTable.getItemCount()-1);
initializeOptionsPage(newId);
displaySelectedOptionPage();
updateButtons();
}
// add remaining parsers (unchecked)
Iterator<String> it = cloneMap.keySet().iterator();
while (it.hasNext()) {
String s = it.next();
data.add(new TableData(s, cloneMap.get(s)));
}
private void editErrorParser() {
int n = fTable.getSelectionIndex();
Assert.isTrue(n>=0);
String id = (String)fTableViewer.getElementAt(n);
IErrorParserNamed errorParser = fAvailableErrorParsers.get(id);
IInputStatusValidator inputValidator = new IInputStatusValidator() {
public IStatus isValid(String newText) {
StatusInfo status = new StatusInfo();
if (newText.trim().length() == 0) {
status.setError(UIMessages.getString("ErrorParsTab.error.NonEmptyName")); //$NON-NLS-1$
} else if (newText.indexOf(ErrorParserManager.ERROR_PARSER_DELIMITER)>=0) {
String message = MessageFormat.format( UIMessages.getString("ErrorParsTab.error.IllegalCharacter"), //$NON-NLS-1$
new Object[] { ErrorParserManager.ERROR_PARSER_DELIMITER });
status.setError(message);
}
return status;
}
};
InputStatusDialog addDialog = new InputStatusDialog(usercomp.getShell(),
UIMessages.getString("ErrorParsTab.title.Edit"), //$NON-NLS-1$
UIMessages.getString("ErrorParsTab.label.EnterName"), //$NON-NLS-1$
errorParser.getName(),
inputValidator);
addDialog.setHelpAvailable(false);
if (addDialog.open() == Window.OK) {
errorParser.setName(addDialog.getValue());
fTableViewer.refresh(id);
}
tv.setInput(data.toArray());
tv.setCheckedElements(checked.toArray());
}
private void deleteErrorParser() {
int n = fTable.getSelectionIndex();
if (n < 0)
return;
fTableViewer.remove(fTableViewer.getElementAt(n));
int last = fTable.getItemCount() - 1;
if (n>last)
n = last;
if (n>=0)
fTable.setSelection(n);
saveChecked();
}
/* (non-Javadoc)
* @see org.eclipse.cdt.ui.newui.AbstractCPropertyTab#updateData(org.eclipse.cdt.core.settings.model.ICResourceDescription)
*/
@Override
public void updateData(ICResourceDescription resDecs) {
ICConfigurationDescription oldCfgDesc = fCfgDesc;
fCfgDesc = resDecs!=null ? resDecs.getConfiguration() : null;
if (oldCfgDesc!=fCfgDesc) {
initMapParsers();
}
displaySelectedOptionPage();
updateButtons();
}
private static boolean isExtensionId(String id) {
for (String extId : ErrorParserManager.getErrorParserExtensionIds()) {
if (extId.equals(id)) {
return true;
}
}
return false;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.ui.newui.AbstractCPropertyTab#updateButtons()
*/
@Override
public void updateButtons() {
int cnt = table.getItemCount();
int pos = table.getSelectionIndex();
buttonSetEnabled(0, pos > 0);
buttonSetEnabled(1, pos != -1 && pos < (cnt - 1));
buttonSetEnabled(3, cnt > 0);
buttonSetEnabled(4, cnt > 0);
int pos = fTable.getSelectionIndex();
int count = fTable.getItemCount();
int last = count - 1;
boolean selected = pos >= 0 && pos <= last;
String id = (String)fTableViewer.getElementAt(pos);
buttonSetEnabled(BUTTON_ADD, isErrorParsersEditable());
buttonSetEnabled(BUTTON_EDIT, isErrorParsersEditable() && selected);
buttonSetEnabled(BUTTON_DELETE, isErrorParsersEditable() && selected && !isExtensionId(id));
buttonSetEnabled(BUTTON_MOVEUP, selected && pos != 0);
buttonSetEnabled(BUTTON_MOVEDOWN, selected && pos != last);
}
/* (non-Javadoc)
* @see org.eclipse.cdt.ui.newui.AbstractCPropertyTab#performApply(org.eclipse.cdt.core.settings.model.ICResourceDescription, org.eclipse.cdt.core.settings.model.ICResourceDescription)
*/
@Override
protected void performApply(ICResourceDescription src, ICResourceDescription dst) {
ICConfigurationDescription sd = src.getConfiguration();
ICConfigurationDescription dd = dst.getConfiguration();
String[] s = null;
if (sd instanceof ICMultiConfigDescription) {
String[][] ss = ((ICMultiConfigDescription)sd).getErrorParserIDs();
s = CDTPrefUtil.getStrListForDisplay(ss);
} else {
s = sd.getBuildSetting().getErrorParserIDs();
performOK();
if (!page.isForPrefs()) {
ICConfigurationDescription sd = src.getConfiguration();
ICConfigurationDescription dd = dst.getConfiguration();
String[] s = null;
if (sd instanceof ICMultiConfigDescription) {
String[][] ss = ((ICMultiConfigDescription)sd).getErrorParserIDs();
s = CDTPrefUtil.getStrListForDisplay(ss);
} else {
s = sd.getBuildSetting().getErrorParserIDs();
}
if (dd instanceof ICMultiConfigDescription)
((ICMultiConfigDescription)dd).setErrorParserIDs(s);
else
dd.getBuildSetting().setErrorParserIDs(s);
initMapParsers();
}
if (dd instanceof ICMultiConfigDescription)
((ICMultiConfigDescription)dd).setErrorParserIDs(s);
else
dd.getBuildSetting().setErrorParserIDs(s);
}
/* (non-Javadoc)
* @see org.eclipse.cdt.ui.newui.AbstractCPropertyTab#performOK()
*/
@Override
protected void performOK() {
informPages(true);
if (page.isForPrefs()) {
if (fCfgDesc==null) {
// Build Settings page
try {
IErrorParserNamed[] errorParsers = new IErrorParserNamed[fTable.getItemCount()];
int i=0;
for (TableItem item : fTable.getItems()) {
if (item.getData() instanceof String) {
String id = (String) item.getData();
errorParsers[i] = fAvailableErrorParsers.get(id);
i++;
}
}
private void saveChecked() {
Object[] objs = tv.getCheckedElements();
ArrayList<String> lst = new ArrayList<String>();
if (objs != null) {
for (Object ob : objs)
lst.add(((TableData)ob).key);
Object[] checkedElements = fTableViewer.getCheckedElements();
String[] checkedErrorParserIds = new String[checkedElements.length];
System.arraycopy(checkedElements, 0, checkedErrorParserIds, 0, checkedElements.length);
ErrorParserManager.setUserDefinedErrorParsers(errorParsers);
ErrorParserManager.setDefaultErrorParserIds(checkedErrorParserIds);
} catch (BackingStoreException e) {
CUIPlugin.log(UIMessages.getString("ErrorParsTab.error.OnApplyingSettings"), e); //$NON-NLS-1$
} catch (CoreException e) {
CUIPlugin.log(UIMessages.getString("ErrorParsTab.error.OnApplyingSettings"), e); //$NON-NLS-1$
}
}
initMapParsers();
}
String[] s = lst.toArray(new String[lst.size()]);
if (cfgd instanceof ICMultiConfigDescription)
((ICMultiConfigDescription)cfgd).setErrorParserIDs(s);
else
cfgd.getBuildSetting().setErrorParserIDs(s);
}
// This page can be displayed for project only
private void saveChecked() {
if (fCfgDesc!=null) {
Object[] objs = fTableViewer.getCheckedElements();
String[] ids = new String[objs.length];
System.arraycopy(objs, 0, ids, 0, objs.length);
if (fCfgDesc instanceof ICMultiConfigDescription)
((ICMultiConfigDescription)fCfgDesc).setErrorParserIDs(ids);
else
fCfgDesc.getBuildSetting().setErrorParserIDs(ids);
}
}
/* (non-Javadoc)
* @see org.eclipse.cdt.ui.newui.AbstractCPropertyTab#canBeVisible()
*/
@Override
public boolean canBeVisible() {
return page.isForProject() || page.isForPrefs();
}
/**
* @return {@code true} if the error parsers are allowed to be editable,
* i.e. Add/Edit/Delete buttons are enabled and Options page edits enabled.
* This will evaluate to {@code true} for Preference Build Settings page but
* not for Preference New CDT Project Wizard/Makefile Project.
*/
private boolean isErrorParsersEditable() {
return fCfgDesc==null;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.ui.newui.AbstractCPropertyTab#performDefaults()
*/
@Override
protected void performDefaults() {
if (cfgd instanceof ICMultiConfigDescription)
((ICMultiConfigDescription)cfgd).setErrorParserIDs(null);
else
cfgd.getBuildSetting().setErrorParserIDs(null);
updateData(getResDesc());
}
if (isErrorParsersEditable()) {
// Must be Build Settings Preference Page
if (MessageDialog.openQuestion(usercomp.getShell(),
UIMessages.getString(UIMessages.getString("ErrorParsTab.title.ConfirmReset")), //$NON-NLS-1$
UIMessages.getString(UIMessages.getString("ErrorParsTab.message.ConfirmReset")))) { //$NON-NLS-1$
try {
ErrorParserManager.setUserDefinedErrorParsers(null);
ErrorParserManager.setDefaultErrorParserIds(null);
} catch (BackingStoreException e) {
CUIPlugin.log(UIMessages.getString("ErrorParsTab.error.OnRestoring"), e); //$NON-NLS-1$
} catch (CoreException e) {
CUIPlugin.log(UIMessages.getString("ErrorParsTab.error.OnRestoring"), e); //$NON-NLS-1$
}
}
} else {
if (fCfgDesc instanceof ICMultiConfigDescription)
((ICMultiConfigDescription) fCfgDesc).setErrorParserIDs(null);
else
fCfgDesc.getBuildSetting().setErrorParserIDs(null);
}
initMapParsers();
updateButtons();
}
private void informPages(boolean apply) {
Collection<ICOptionPage> pages = fOptionsPageMap.values();
for (ICOptionPage dynamicPage : pages) {
if (dynamicPage!=null && dynamicPage.isValid() && dynamicPage.getControl() != null) {
try {
if (apply)
dynamicPage.performApply(new NullProgressMonitor());
else
dynamicPage.performDefaults();
} catch (CoreException e) {
CUIPlugin.log(UIMessages.getString("ErrorParsTab.error.OnApplyingSettings"), e); //$NON-NLS-1$
}
}
}
}
}

View file

@ -443,8 +443,18 @@ ConfigDescriptionTab.0=Project Description
ConfigDescriptionTab.1=Configuration Description
ConfigDescriptionTab.2=Resource Description
BinaryParsTab.0=Binary parser:
ErrorParsTab.0=Check all
ErrorParsTab.1=Uncheck all
ErrorParsTab.error.NonEmptyName=Specify non empty name
ErrorParsTab.error.NonUniqueID=Error parser ID is not unique, specify different name
ErrorParsTab.error.OnApplyingSettings=Error applying Error Parser Tab settings
ErrorParsTab.error.OnRestoring=Error restoring default Error Parser Tab settings
ErrorParsTab.error.NonAccessibleID=[ Not accessible id={0} ]
ErrorParsTab.error.IllegalCharacter=Special character ''{0}'' is not allowed
ErrorParsTab.label.EnterName=Enter name of new error parser:
ErrorParsTab.label.DefaultRegexErrorParserName=Regex Error Parser
ErrorParsTab.message.ConfirmReset=Are you sure you want to delete all customized error parsers?
ErrorParsTab.title.Add=Add Regex Error Parser
ErrorParsTab.title.ConfirmReset=Confirm Resetting Error Parsers
ErrorParsTab.title.Edit=Edit Regex Error Parser name
StructureTreeTab.0=Level:
StructureTreeTab.1=Maximal tree nesting
StructureTreeTab.2=Long