/*******************************************************************************
* Copyright (c) 2007, 2010 Intel 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
* http://www.eclipse.org/legal/epl-v10.html
*
* 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.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
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.layout.PixelConverter;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.CheckboxTableViewer;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.IDecoration;
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.osgi.util.NLS;
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.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.ui.PlatformUI;
import org.osgi.service.prefs.BackingStoreException;
import com.ibm.icu.text.MessageFormat;
import org.eclipse.cdt.core.CCorePlugin;
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.CDTSharedImages;
import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.cdt.ui.dialogs.ICOptionPage;
import org.eclipse.cdt.ui.dialogs.IInputStatusValidator;
import org.eclipse.cdt.ui.dialogs.InputStatusDialog;
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.StatusInfo;
import org.eclipse.cdt.internal.ui.newui.Messages;
/**
* 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 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 RESET_STR = Messages.ErrorParsTab_Reset;
private static final String OOPS = "OOPS"; //$NON-NLS-1$
private Table fTable;
private CheckboxTableViewer fTableViewer;
private ICConfigurationDescription fCfgDesc;
private static Map<String, IErrorParserNamed> fExtensionErrorParsers = null;
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);
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();
}});
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) {}
});
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 NLS.bind(Messages.ErrorParsTab_error_NonAccessibleID, id);
}
return OOPS;
}
@Override
public Image getImage(Object element) {
final String TEST_PLUGIN_ID = "org.eclipse.cdt.core.tests"; //$NON-NLS-1$
final String DEPRECATED = CCorePlugin.getResourceString("CCorePlugin.Deprecated"); //$NON-NLS-1$
if (element instanceof String) {
String id = (String) element;
String[] extIds = ErrorParserManager.getErrorParserExtensionIds();
if (Arrays.asList(extIds).contains(id)) {
String imageKey = CDTSharedImages.IMG_OBJS_EXTENSION;
if (id.startsWith(TEST_PLUGIN_ID))
imageKey = CDTSharedImages.IMG_OBJS_CDT_TESTING;
String[] overlayKeys = new String[5];
IErrorParserNamed errorParser = fAvailableErrorParsers.get(id);
IErrorParserNamed errorParserExt = fExtensionErrorParsers.get(id);
if (!errorParser.equals(errorParserExt)) {
overlayKeys[IDecoration.TOP_RIGHT] = CDTSharedImages.IMG_OVR_SETTING;
} else if (errorParser.getName().contains(DEPRECATED)) {
overlayKeys[IDecoration.TOP_RIGHT] = CDTSharedImages.IMG_OVR_INACTIVE;
}
return CDTSharedImages.getImageOverlaid(imageKey, overlayKeys);
}
return CDTSharedImages.getImage(CDTSharedImages.IMG_OBJS_USER);
}
return null;
}
});
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());
}
private void initMapParsers() {
if (fExtensionErrorParsers==null) {
fExtensionErrorParsers = new LinkedHashMap<String, IErrorParserNamed>();
String[] idsExt = ErrorParserManager.getErrorParserExtensionIds();
for (String idExt : idsExt) {
IErrorParserNamed errorParserExt = ErrorParserManager.getErrorParserExtensionCopy(idExt);
fExtensionErrorParsers.put(idExt, errorParserExt);
}
}
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(final 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());
optionsPage.addListener(new Listener() {
public void handleEvent(Event event) {
fTableViewer.refresh(id);
updateButtons();
}
});
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 BUTTON_ADD:
addErrorParser();
break;
case BUTTON_EDIT:
editErrorParser();
break;
case BUTTON_DELETE:
deleteErrorParser();
break;
case BUTTON_MOVEUP:
moveItem(true);
break;
case BUTTON_MOVEDOWN:
moveItem(false);
break;
default:
break;
}
updateButtons();
}
// Move item up / down
private void moveItem(boolean up) {
int n = fTable.getSelectionIndex();
if (n < 0 || (up && n == 0) || (!up && n+1 == fTable.getItemCount()))
return;
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();
}
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(Messages.ErrorParsTab_error_NonEmptyName);
} else if (newText.indexOf(ErrorParserManager.ERROR_PARSER_DELIMITER)>=0) {
String message = MessageFormat.format( Messages.ErrorParsTab_error_IllegalCharacter,
new Object[] { ErrorParserManager.ERROR_PARSER_DELIMITER });
status.setError(message);
} else if (fAvailableErrorParsers.containsKey(makeId(newText))) {
status.setError(Messages.ErrorParsTab_error_NonUniqueID);
}
return status;
}
};
InputStatusDialog addDialog = new InputStatusDialog(usercomp.getShell(),
Messages.ErrorParsTab_title_Add,
Messages.ErrorParsTab_label_EnterName,
Messages.ErrorParsTab_label_DefaultRegexErrorParserName,
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();
}
}
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(Messages.ErrorParsTab_error_NonEmptyName);
} else if (newText.indexOf(ErrorParserManager.ERROR_PARSER_DELIMITER)>=0) {
String message = MessageFormat.format( Messages.ErrorParsTab_error_IllegalCharacter,
new Object[] { ErrorParserManager.ERROR_PARSER_DELIMITER });
status.setError(message);
}
return status;
}
};
InputStatusDialog addDialog = new InputStatusDialog(usercomp.getShell(),
Messages.ErrorParsTab_title_Edit,
Messages.ErrorParsTab_label_EnterName,
errorParser.getName(),
inputValidator);
addDialog.setHelpAvailable(false);
if (addDialog.open() == Window.OK) {
errorParser.setName(addDialog.getValue());
fTableViewer.refresh(id);
}
}
private void deleteErrorParser() {
int n = fTable.getSelectionIndex();
if (n < 0)
return;
String id = (String)fTableViewer.getElementAt(n);
if (fExtensionErrorParsers.containsKey(id)) {
// Reset
fAvailableErrorParsers.put(id, ErrorParserManager.getErrorParserExtensionCopy(id));
fTableViewer.refresh(id);
initializeOptionsPage(id);
displaySelectedOptionPage();
} else {
// Delete
fTableViewer.remove(id);
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;
}
/**
* Check if error parser with this id shown to user differs from error parser defined as extension.
*/
private boolean isModified(String id) {
IErrorParserNamed errorParser = fAvailableErrorParsers.get(id);
IErrorParserNamed errorParserExt = fExtensionErrorParsers.get(id);
return ! errorParser.equals(errorParserExt);
}
/* (non-Javadoc)
* @see org.eclipse.cdt.ui.newui.AbstractCPropertyTab#updateButtons()
*/
@Override
public void updateButtons() {
int pos = fTable.getSelectionIndex();
int count = fTable.getItemCount();
int last = count - 1;
boolean selected = pos >= 0 && pos <= last;
String id = (String)fTableViewer.getElementAt(pos);
boolean isExtensionId = isExtensionId(id);
boolean canDelete = !isExtensionId && isErrorParsersEditable();
boolean canReset = isExtensionId && isErrorParsersEditable() && isModified(id);
buttonSetText(BUTTON_DELETE, isExtensionId ? RESET_STR : DEL_STR);
buttonSetEnabled(BUTTON_ADD, isErrorParsersEditable());
buttonSetEnabled(BUTTON_EDIT, isErrorParsersEditable() && selected);
buttonSetEnabled(BUTTON_DELETE, (canDelete || canReset) && selected);
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) {
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();
}
}
/* (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 {
List<IErrorParserNamed> errorParsersList = new ArrayList<IErrorParserNamed>(fTable.getItemCount());
for (TableItem item : fTable.getItems()) {
if (item.getData() instanceof String) {
String id = (String) item.getData();
if (isModified(id)) {
errorParsersList.add(fAvailableErrorParsers.get(id));
}
}
}
Object[] checkedElements = fTableViewer.getCheckedElements();
String[] checkedErrorParserIds = new String[checkedElements.length];
System.arraycopy(checkedElements, 0, checkedErrorParserIds, 0, checkedElements.length);
ErrorParserManager.setDefaultErrorParserIds(checkedErrorParserIds);
ErrorParserManager.setUserDefinedErrorParsers(errorParsersList.toArray(new IErrorParserNamed[errorParsersList.size()]));
} catch (BackingStoreException e) {
CUIPlugin.log(Messages.ErrorParsTab_error_OnApplyingSettings, e);
} catch (CoreException e) {
CUIPlugin.log(Messages.ErrorParsTab_error_OnApplyingSettings, e);
}
}
initMapParsers();
}
}
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 (isErrorParsersEditable()) {
// Must be Build Settings Preference Page
if (MessageDialog.openQuestion(usercomp.getShell(),
Messages.ErrorParsTab_title_ConfirmReset,
Messages.ErrorParsTab_message_ConfirmReset)) {
try {
ErrorParserManager.setUserDefinedErrorParsers(null);
ErrorParserManager.setDefaultErrorParserIds(null);
} catch (BackingStoreException e) {
CUIPlugin.log(Messages.ErrorParsTab_error_OnRestoring, e);
} catch (CoreException e) {
CUIPlugin.log(Messages.ErrorParsTab_error_OnRestoring, e);
}
}
} 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(Messages.ErrorParsTab_error_OnApplyingSettings, e);
}
}
}
}
}