/******************************************************************************* * Copyright (c) 2014, 2015 Ericsson 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: * Marc Khouzam (Ericsson) - Initial API and implementation *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.internal.ui.breakpoints; import org.eclipse.cdt.debug.core.CDIDebugModel; import org.eclipse.cdt.debug.core.model.ICAddressBreakpoint; import org.eclipse.cdt.debug.core.model.ICBreakpoint; import org.eclipse.cdt.debug.core.model.ICDynamicPrintf; import org.eclipse.cdt.debug.core.model.ICFunctionBreakpoint; import org.eclipse.cdt.debug.core.model.ICLineBreakpoint; import org.eclipse.cdt.debug.internal.ui.breakpoints.BreakpointsMessages; import org.eclipse.cdt.debug.internal.ui.breakpoints.CBreakpointContext; import org.eclipse.cdt.debug.internal.ui.breakpoints.CBreakpointPreferenceStore; import org.eclipse.cdt.debug.ui.breakpoints.ICBreakpointContext; import org.eclipse.cdt.debug.ui.preferences.ReadOnlyFieldEditor; import org.eclipse.cdt.dsf.gdb.breakpoints.GDBDynamicPrintfUtils; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.model.IBreakpoint; import org.eclipse.debug.core.model.IDebugElement; import org.eclipse.debug.core.model.IDebugModelProvider; import org.eclipse.debug.ui.DebugUITools; import org.eclipse.debug.ui.contexts.IDebugContextProvider; import org.eclipse.jface.preference.BooleanFieldEditor; import org.eclipse.jface.preference.FieldEditor; import org.eclipse.jface.preference.FieldEditorPreferencePage; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.preference.IntegerFieldEditor; import org.eclipse.jface.preference.StringFieldEditor; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.IWorkbenchPropertyPage; import org.eclipse.ui.model.IWorkbenchAdapter; /** * The preference page used to present the properties of a GDB dynamic printf as preferences. */ public class GDBDynamicPrintfPropertyPage extends FieldEditorPreferencePage implements IWorkbenchPropertyPage { private class DynamicPrintfIntegerFieldEditor extends IntegerFieldEditor { public DynamicPrintfIntegerFieldEditor(String name, String labelText, Composite parent) { super(name, labelText, parent); setErrorMessage(Messages.PropertyPage_integer_negative); } /** * @see IntegerFieldEditor#checkState() */ @Override protected boolean checkState() { Text control = getTextControl(); if (!control.isEnabled()) { clearErrorMessage(); return true; } return super.checkState(); } /** * Only store if the text control is enabled * * @see FieldEditor#doStore() */ @Override protected void doStore() { Text text = getTextControl(); if (text.isEnabled()) { super.doStore(); } } /** * Clears the error message from the message line if the error message is the error message from this field editor. */ @Override protected void clearErrorMessage() { if (getPage() != null) { String message = getPage().getErrorMessage(); if ( message != null ) { if (getErrorMessage().equals(message)) { super.clearErrorMessage(); } } else { super.clearErrorMessage(); } } } } class DynamicPrintfStringFieldEditor extends StringFieldEditor { public DynamicPrintfStringFieldEditor(String name, String labelText, Composite parent) { super(name, labelText, parent); } /** * @see StringFieldEditor#checkState() */ @Override protected boolean checkState() { Text control = getTextControl(); if (!control.isEnabled()) { clearErrorMessage(); return true; } return super.checkState(); } @Override protected void doStore() { Text text = getTextControl(); if (text.isEnabled()) { super.doStore(); } } @Override protected void doLoad() { String value = getPreferenceStore().getString(getPreferenceName()); setStringValue(value); } /** * Clears the error message from the message line if the error message is the error message from this field editor. */ @Override protected void clearErrorMessage() { if (getPage() != null) { String message = getPage().getErrorMessage(); if ( message != null ) { if (getErrorMessage().equals(message)) { super.clearErrorMessage(); } } else { super.clearErrorMessage(); } } } } private class LabelFieldEditor extends ReadOnlyFieldEditor { private String fValue; public LabelFieldEditor(Composite parent, String title, String value) { super(title, title, parent); fValue = value; } @Override protected void doLoad() { if (textField != null) { textField.setText(fValue); } } @Override protected void doLoadDefault() { // nothing } } private BooleanFieldEditor fEnabled; private DynamicPrintfStringFieldEditor fCondition; private Text fIgnoreCountTextControl; private DynamicPrintfIntegerFieldEditor fLineEditor; private DynamicPrintfIntegerFieldEditor fIgnoreCount; /** * Indicates if the page currently aims to create * a breakpoint that already exits. */ private boolean fDuplicateBreakpoint; private DynamicPrintfStringFieldEditor fPrintString; private IAdaptable fElement; /** * The preference store used to interface between the dynamic printf and the * dynamic printf preference page. This preference store is initialized only * when the preference store cannot be retrieved from the preference * dialog's element. * @see #getPreferenceStore() */ private CBreakpointPreferenceStore fDynamicPrintfPreferenceStore; public GDBDynamicPrintfPropertyPage() { super(GRID); noDefaultAndApplyButton(); } @Override protected void createFieldEditors() { ICDynamicPrintf dprintf = getDprintf(); createMainLabel(dprintf); createTypeSpecificLabelFieldEditors(dprintf); createEnabledField(getFieldEditorParent()); createConditionEditor(getFieldEditorParent()); createIgnoreCountEditor(getFieldEditorParent()); createPrintStringEditor(getFieldEditorParent()); } private void createMainLabel(ICDynamicPrintf dprintf) { addField(createLabelEditor(getFieldEditorParent(), Messages.PropertyPage_Class, getDynamicPrintfMainLabel(dprintf))); } private void createTypeSpecificLabelFieldEditors(ICDynamicPrintf dprintf) { if (dprintf instanceof ICFunctionBreakpoint) { createFunctionEditor(getFieldEditorParent()); } else if (dprintf instanceof ICAddressBreakpoint) { String address = getPreferenceStore().getString(ICLineBreakpoint.ADDRESS); if (address == null || address.trim().length() == 0) { address = Messages.PropertyPage_NotAvailable; } addField(createLabelEditor(getFieldEditorParent(), Messages.PropertyPage_Address, address)); } else { // LineDprintf String fileName = getPreferenceStore().getString(ICBreakpoint.SOURCE_HANDLE); if (fileName != null) { addField(createLabelEditor(getFieldEditorParent(), Messages.PropertyPage_File, fileName)); } int lNumber = getPreferenceStore().getInt(IMarker.LINE_NUMBER); if (lNumber > 0) { createLineNumberEditor(getFieldEditorParent()); } } } private String getDynamicPrintfMainLabel(ICDynamicPrintf dprintf) { IWorkbenchAdapter labelProvider = getElement().getAdapter(IWorkbenchAdapter.class); if (labelProvider != null) { return labelProvider.getLabel(getElement()); } // default main label is the label of marker type for the dynamic printf return CDIDebugModel.calculateMarkerType(dprintf); } protected void createFunctionEditor(Composite parent) { ICDynamicPrintf dprintf = getDprintf(); if (dprintf == null || dprintf.getMarker() == null) { DynamicPrintfStringFieldEditor expressionEditor = new DynamicPrintfStringFieldEditor( ICLineBreakpoint.FUNCTION, Messages.PropertyPage_FunctionName, parent); expressionEditor.setErrorMessage(Messages.PropertyPage_function_value_errorMessage); expressionEditor.setEmptyStringAllowed(false); addField(expressionEditor); } else { String function = getPreferenceStore().getString(ICLineBreakpoint.FUNCTION); if (function == null) { function = Messages.PropertyPage_NotAvailable; } addField(createLabelEditor(getFieldEditorParent(), Messages.PropertyPage_FunctionName, function)); } } protected void createLineNumberEditor(Composite parent) { String title = Messages.PropertyPage_LineNumber; fLineEditor = new DynamicPrintfIntegerFieldEditor(IMarker.LINE_NUMBER, title, parent); fLineEditor.setValidRange(1, Integer.MAX_VALUE); fLineEditor.setErrorMessage(Messages.PropertyPage_lineNumber_errorMessage); addField(fLineEditor); } protected void createEnabledField(Composite parent) { fEnabled = new BooleanFieldEditor(ICBreakpoint.ENABLED, Messages.PropertyPage_Enabled, parent); addField(fEnabled); } protected void createConditionEditor(Composite parent) { fCondition = new DynamicPrintfStringFieldEditor(ICBreakpoint.CONDITION, Messages.PropertyPage_Condition, parent); fCondition.setEmptyStringAllowed(true); fCondition.setErrorMessage(Messages.PropertyPage_InvalidCondition); addField(fCondition); } protected void createIgnoreCountEditor(Composite parent) { fIgnoreCount = new DynamicPrintfIntegerFieldEditor(ICBreakpoint.IGNORE_COUNT, Messages.PropertyPage_IgnoreCount, parent); fIgnoreCount.setValidRange(0, Integer.MAX_VALUE); fIgnoreCountTextControl = fIgnoreCount.getTextControl(parent); fIgnoreCountTextControl.setEnabled( getPreferenceStore().getInt(ICBreakpoint.IGNORE_COUNT) >= 0 ); addField(fIgnoreCount); } protected void createPrintStringEditor(Composite parent) { fPrintString = new DynamicPrintfStringFieldEditor(ICDynamicPrintf.PRINTF_STRING, Messages.DynamicPrintfPropertyPage_PrintString, parent) { @Override protected boolean doCheckState() { GDBDynamicPrintfUtils.GDBDynamicPrintfString parsedStr = new GDBDynamicPrintfUtils.GDBDynamicPrintfString(getTextControl().getText()); boolean valid = parsedStr.isValid(); if (!valid) { setErrorMessage(parsedStr.getErrorMessage()); } return valid; } }; addField(fPrintString); } @Override public boolean isValid() { // Don't allow to create a duplicate breakpoint return super.isValid() && !fDuplicateBreakpoint; } @Override public void propertyChange(PropertyChangeEvent event) { super.propertyChange(event); ICBreakpoint currentBp = getDprintf(); if (!(currentBp instanceof ICFunctionBreakpoint) && !(currentBp instanceof ICAddressBreakpoint)) { // Check for duplication of line dprintf if (event.getProperty().equals(FieldEditor.VALUE)) { if (super.isValid()) { // For every change, if all the fields are valid // we then check if we are dealing with a duplicate // breakpoint. boolean oldValue = fDuplicateBreakpoint; fDuplicateBreakpoint = isDuplicateBreakpoint(); if (oldValue != fDuplicateBreakpoint) { if (fDuplicateBreakpoint) { setErrorMessage(BreakpointsMessages.getString("CBreakpointPropertyPage.breakpoint_already_exists_errorMessage")); //$NON-NLS-1$ } else { setErrorMessage(null); } // update container state if (getContainer() != null) { getContainer().updateButtons(); } // update page state updateApplyButton(); } } } } } private boolean isDuplicateBreakpoint() { String source = getPreferenceStore().getString(ICBreakpoint.SOURCE_HANDLE); int line = fLineEditor.getIntValue(); // Look for any breakpoint (base bp class) that has the same source file and line number as what // is currently being inputed. Careful not to compare with the current dprintf // in the case of modifying the properties of an existing dprintf; in // that case we of course have this particular dprintf at this file and line. ICBreakpoint currentBp = getDprintf(); IBreakpoint[] breakpoints = DebugPlugin.getDefault().getBreakpointManager().getBreakpoints(); for (IBreakpoint bp : breakpoints) { if (!bp.equals(currentBp) && bp instanceof ICBreakpoint) { IMarker marker = bp.getMarker(); if (marker != null) { String markerFile = marker.getAttribute(ICBreakpoint.SOURCE_HANDLE, ""); //$NON-NLS-1$ int markerLine = marker.getAttribute(IMarker.LINE_NUMBER, -1); if (source.equals(markerFile) && line == markerLine) { // Woops, we already have another breakpoint at this file:line return true; } } } } return false; } protected FieldEditor createLabelEditor(Composite parent, String title, String value) { return new LabelFieldEditor(parent, title, value); } protected ICDynamicPrintf getDprintf() { IAdaptable element = getElement(); if (element instanceof ICDynamicPrintf) { return (ICDynamicPrintf)element; } if (element instanceof ICBreakpointContext) { ICBreakpoint breakpoint =((ICBreakpointContext)element).getBreakpoint(); if (breakpoint instanceof ICDynamicPrintf) { return (ICDynamicPrintf)breakpoint; } assert false : "Should always have a dprintf"; //$NON-NLS-1$ } return element.getAdapter(ICDynamicPrintf.class); } protected Object getDebugContext() { IDebugContextProvider provider = getElement().getAdapter(IDebugContextProvider.class); if (provider != null) { ISelection selection = provider.getActiveContext(); if (selection instanceof IStructuredSelection) { return ((IStructuredSelection) selection).getFirstElement(); } return null; } return DebugUITools.getDebugContext(); } protected IResource getResource() { IAdaptable element = getElement(); if (element instanceof ICDynamicPrintf) { IMarker marker = ((ICDynamicPrintf)element).getMarker(); if (marker != null) { return marker.getResource(); } } else if (element instanceof ICBreakpointContext) { return ((ICBreakpointContext)element).getResource(); } return null; } @Override public IPreferenceStore getPreferenceStore() { IAdaptable element = getElement(); if (element instanceof ICBreakpointContext) { return ((ICBreakpointContext)element).getPreferenceStore(); } if (fDynamicPrintfPreferenceStore == null) { CBreakpointContext bpContext = element instanceof CBreakpointContext ? (CBreakpointContext)element : null; fDynamicPrintfPreferenceStore = new CBreakpointPreferenceStore(bpContext, null); } return fDynamicPrintfPreferenceStore; } @Override public boolean performCancel() { IPreferenceStore store = getPreferenceStore(); if (store instanceof CBreakpointPreferenceStore) { ((CBreakpointPreferenceStore)store).setCanceled(true); } return super.performCancel(); } @Override public boolean performOk() { IPreferenceStore store = getPreferenceStore(); if (store instanceof CBreakpointPreferenceStore) { ((CBreakpointPreferenceStore)store).setCanceled(false); } return super.performOk(); } /* (non-Javadoc) * @see org.eclipse.ui.IWorkbenchPropertyPage#getElement() */ @Override public IAdaptable getElement() { return fElement; } /* (non-Javadoc) * @see org.eclipse.ui.IWorkbenchPropertyPage#setElement(org.eclipse.core.runtime.IAdaptable) */ @Override public void setElement(IAdaptable element) { fElement = element; } protected String[] getDebugModelIds() { String[] debugModelIds = null; Object debugContext = getDebugContext(); IDebugModelProvider debugModelProvider = (IDebugModelProvider) DebugPlugin.getAdapter(debugContext, IDebugModelProvider.class); if (debugModelProvider != null) { debugModelIds = debugModelProvider.getModelIdentifiers(); } else if (debugContext instanceof IDebugElement) { debugModelIds = new String[] { ((IDebugElement)debugContext).getModelIdentifier() }; } return debugModelIds; } }