| /******************************************************************************* |
| * Copyright (c) 2000, 2016 QNX Software Systems 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: |
| * QNX Software Systems - Initial API and implementation |
| * QNX Software Systems - Refactored to use platform implementation |
| * Marc Khouzam (Ericsson) - Added support for Tracepoints (bug 376116) |
| * Marc Khouzam (Ericsson) - Added support for Dynamic-Printf (bug 400628) |
| * Jonah Graham - Set REQUESTED_* fields when creating from dialog (bug 46026) |
| * Jonah Graham (Kichwa Coders) - Create "Add Line Breakpoint (C/C++)" action (Bug 464917) |
| *******************************************************************************/ |
| package org.eclipse.cdt.debug.internal.ui.breakpoints; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.cdt.debug.core.CDIDebugModel; |
| import org.eclipse.cdt.debug.core.model.ICBreakpoint; |
| import org.eclipse.cdt.debug.core.model.ICBreakpoint2; |
| import org.eclipse.cdt.debug.core.model.ICDynamicPrintf; |
| import org.eclipse.cdt.debug.core.model.ICLineBreakpoint2; |
| import org.eclipse.cdt.debug.core.model.ICTracepoint; |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IMarker; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.IWorkspaceRoot; |
| import org.eclipse.core.resources.IWorkspaceRunnable; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.ListenerList; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.debug.core.DebugPlugin; |
| import org.eclipse.jface.preference.IPersistentPreferenceStore; |
| import org.eclipse.jface.util.IPropertyChangeListener; |
| import org.eclipse.jface.util.PropertyChangeEvent; |
| |
| /** |
| * A preference store that presents the state of the properties of a C/C++ breakpoint, |
| * tracepoint or dynamic-printf. |
| */ |
| public class CBreakpointPreferenceStore implements IPersistentPreferenceStore { |
| |
| // This map is the current properties/values being maintained/manipulated |
| private HashMap<String, Object> fProperties = new HashMap<String, Object>(); |
| |
| // Original set of values. So we can see what has really changed on the save and |
| // perform appropriate change operations. We only really want to operate on changed |
| // values, to avoid generating churn. |
| private HashMap<String, Object> fOriginalValues = new HashMap<String, Object>(); |
| private boolean fIsDirty = false; |
| private boolean fIsCanceled = false; |
| private ListenerList fListeners; |
| private final CBreakpointContext fContext; |
| |
| public CBreakpointPreferenceStore() { |
| this (null, null); |
| } |
| |
| public CBreakpointPreferenceStore(CBreakpointContext context, Map<String, Object> attributes) { |
| fListeners = new ListenerList(org.eclipse.core.runtime.ListenerList.IDENTITY); |
| fContext = context; |
| |
| fOriginalValues.clear(); |
| fProperties.clear(); |
| if (context != null) { |
| IMarker marker = context.getBreakpoint().getMarker(); |
| if (marker != null) { |
| Map<String, Object> bpAttrs = Collections.emptyMap(); |
| try { |
| bpAttrs = marker.getAttributes(); |
| fOriginalValues.putAll(bpAttrs); |
| fProperties.putAll(bpAttrs); |
| } catch (CoreException e) { |
| DebugPlugin.log(e); |
| } |
| } |
| } |
| if (attributes != null) { |
| fProperties.putAll(attributes); |
| fIsDirty = true; |
| } |
| } |
| |
| public Map<String, Object> getAttributes() { |
| return fProperties; |
| } |
| |
| public void setCanceled(boolean canceled) { |
| fIsCanceled = canceled; |
| } |
| |
| public void save() throws IOException { |
| if (!fIsCanceled && fContext != null && fContext.getBreakpoint() != null) { |
| ICBreakpoint bp = fContext.getBreakpoint(); |
| if (bp.getMarker() != null && fIsDirty) { |
| saveToExistingMarker(bp, bp.getMarker()); |
| } else { |
| IResource resolved = getResource(fContext.getResource()); |
| if (resolved != null) { |
| saveToNewMarker(bp, resolved); |
| } else { |
| throw new IOException("Unable to create breakpoint: no resource specified."); //$NON-NLS-1$ |
| } |
| } |
| } |
| |
| } |
| |
| /** |
| * Get the resource to apply the marker against. This may not be the same |
| * resource the dialog was created for if the user has selected a different |
| * resource. |
| * <p> |
| * If the {@link ICBreakpoint#SOURCE_HANDLE} resolves to the same file on |
| * the filesystem as the preferred resource the preferred resource is used. |
| * |
| * @param preferred |
| * resource to use if it matches the SOURCE_HANDLE |
| * @return Resource to install marker on, or <code>null</code> for not |
| * available. |
| */ |
| private IResource getResource(IResource preferred) { |
| IResource resolved = null; |
| String source = getString(ICBreakpoint.SOURCE_HANDLE); |
| if (!"".equals(source)) { //$NON-NLS-1$ |
| IPath rawLocation = preferred.getRawLocation(); |
| if (rawLocation != null) { |
| File file = rawLocation.toFile(); |
| File sourceFile = new File(source); |
| if (file.getAbsoluteFile().equals(sourceFile.getAbsoluteFile())) { |
| resolved = preferred; |
| } |
| } |
| |
| if (resolved == null) { |
| IPath path = Path.fromOSString(source); |
| IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); |
| IFile file = root.getFileForLocation(path); |
| if (file == null) { |
| resolved = root; |
| } else { |
| resolved = file; |
| } |
| } |
| } |
| if (resolved == null) { |
| resolved = preferred; |
| } |
| return resolved; |
| } |
| |
| private void saveToExistingMarker(final ICBreakpoint breakpoint, final IMarker marker) throws IOException { |
| final List<String> changedProperties = new ArrayList<String>( 5 ); |
| Set<String> valueNames = fProperties.keySet(); |
| for ( String name : valueNames ) { |
| if ( fProperties.containsKey( name ) ) { |
| Object originalObject = fOriginalValues.get( name ); |
| Object currentObject = fProperties.get( name ); |
| if ( originalObject == null ) { |
| changedProperties.add( name ); |
| } |
| else if ( ! originalObject.equals( currentObject ) ) { |
| changedProperties.add( name ); |
| } |
| } |
| } |
| if ( ! changedProperties.isEmpty() ) { |
| IWorkspaceRunnable wr = new IWorkspaceRunnable() { |
| public void run( IProgressMonitor monitor ) throws CoreException { |
| Iterator<String> changed = changedProperties.iterator(); |
| while( changed.hasNext() ) { |
| String property = changed.next(); |
| if ( property.equals( ICBreakpoint.ENABLED ) ) { |
| breakpoint.setEnabled( getBoolean( ICBreakpoint.ENABLED ) ); |
| } |
| else if ( property.equals( ICBreakpoint.IGNORE_COUNT ) ) { |
| breakpoint.setIgnoreCount( getInt( ICBreakpoint.IGNORE_COUNT ) ); |
| } |
| else if ( breakpoint instanceof ICTracepoint && property.equals( ICTracepoint.PASS_COUNT ) ) { |
| ((ICTracepoint)breakpoint).setPassCount( getInt( ICTracepoint.PASS_COUNT ) ); |
| } |
| else if ( breakpoint instanceof ICDynamicPrintf && property.equals( ICDynamicPrintf.PRINTF_STRING ) ) { |
| ((ICDynamicPrintf)breakpoint).setPrintfString( getString( ICDynamicPrintf.PRINTF_STRING ) ); |
| } |
| else if ( property.equals( ICBreakpoint.CONDITION ) ) { |
| breakpoint.setCondition( getString( ICBreakpoint.CONDITION ) ); |
| } |
| else if ( property.equals( IMarker.LINE_NUMBER ) ) { |
| if (breakpoint instanceof ICLineBreakpoint2) { |
| // refresh message and line number |
| // Note there are no API methods to set the line number of a Line Breakpoint, so we |
| // replicate what is done in CDIDebugModel.setLineBreakpointAttributes() |
| // to set the line number fields properly and then refresh the message if possible |
| ((ICLineBreakpoint2)breakpoint).setRequestedLine(getInt(IMarker.LINE_NUMBER)); |
| breakpoint.getMarker().setAttribute(IMarker.LINE_NUMBER, getInt(IMarker.LINE_NUMBER)); |
| ((ICBreakpoint2)breakpoint).refreshMessage(); |
| } else { |
| // already workspace runnable, setting markers are safe |
| breakpoint.getMarker().setAttribute(IMarker.LINE_NUMBER, getInt(IMarker.LINE_NUMBER)); |
| breakpoint.getMarker().setAttribute(ICLineBreakpoint2.REQUESTED_LINE, getInt(IMarker.LINE_NUMBER)); |
| } |
| } else { |
| // this allow set attributes contributed by other plugins |
| Object value = fProperties.get(property); |
| if ( value != null ) { |
| marker.setAttribute(property, value); |
| if (breakpoint instanceof ICBreakpoint2) { |
| // To be safe, refresh the breakpoint message as the property |
| // change might affect it. |
| ((ICBreakpoint2)breakpoint).refreshMessage(); |
| } |
| } |
| } |
| } |
| } |
| }; |
| try { |
| ResourcesPlugin.getWorkspace().run( wr, null ); |
| } |
| catch( CoreException ce ) { |
| throw new IOException("Cannot save properties to breakpoint.", ce); //$NON-NLS-1$ |
| } |
| } |
| } |
| |
| private void saveToNewMarker(final ICBreakpoint breakpoint, final IResource resource) throws IOException { |
| try { |
| // On initial creation of BP, make sure that requested values of breakpoint |
| // match the current values (i.e. make sure it starts as a not-relocated breakpoint) |
| // See CDIDebugModel.setLineBreakpointAttributes |
| if (fProperties.containsKey(ICLineBreakpoint2.REQUESTED_SOURCE_HANDLE)) { |
| fProperties.put(ICLineBreakpoint2.REQUESTED_SOURCE_HANDLE, fProperties.get(ICBreakpoint.SOURCE_HANDLE)); |
| } |
| if (fProperties.containsKey(ICLineBreakpoint2.REQUESTED_LINE)) { |
| fProperties.put(ICLineBreakpoint2.REQUESTED_LINE, fProperties.get(IMarker.LINE_NUMBER)); |
| } |
| if (fProperties.containsKey(ICLineBreakpoint2.REQUESTED_CHAR_START)) { |
| fProperties.put(ICLineBreakpoint2.REQUESTED_CHAR_START, fProperties.get(IMarker.CHAR_START)); |
| } |
| if (fProperties.containsKey(ICLineBreakpoint2.REQUESTED_CHAR_END)) { |
| fProperties.put(ICLineBreakpoint2.REQUESTED_CHAR_END, fProperties.get(IMarker.CHAR_END)); |
| } |
| |
| CDIDebugModel.createBreakpointMarker(breakpoint, resource, fProperties, true); |
| } |
| catch( CoreException ce ) { |
| throw new IOException("Cannot save properties to new breakpoint.", ce); //$NON-NLS-1$ |
| } |
| } |
| |
| /////////////////////////////////////////////////////////////////////// |
| // IPreferenceStore |
| |
| public boolean needsSaving() { |
| return fIsDirty && !fIsCanceled; |
| } |
| |
| public boolean contains(String name) { |
| return fProperties.containsKey(name); |
| } |
| |
| public void addPropertyChangeListener(IPropertyChangeListener listener) { |
| fListeners.add(listener); |
| } |
| |
| public void removePropertyChangeListener(IPropertyChangeListener listener) { |
| fListeners.remove(listener); |
| } |
| |
| public void firePropertyChangeEvent(String name, |
| Object oldValue, |
| Object newValue) |
| { |
| Object[] listeners = fListeners.getListeners(); |
| // Do we need to fire an event. |
| if (listeners.length > 0 && (oldValue == null || !oldValue.equals(newValue))) { |
| PropertyChangeEvent pe = new PropertyChangeEvent(this, name, oldValue, newValue); |
| for (int i = 0; i < listeners.length; ++i) { |
| IPropertyChangeListener l = (IPropertyChangeListener) listeners[i]; |
| l.propertyChange(pe); |
| } |
| } |
| } |
| |
| public boolean getBoolean(String name) { |
| boolean retVal = false; |
| Object o = fProperties.get(name); |
| if (o instanceof Boolean) { |
| retVal = ((Boolean)o).booleanValue(); |
| } |
| return retVal; |
| } |
| |
| public int getInt(String name) { |
| int retVal = 0; |
| Object o = fProperties.get(name); |
| if (o instanceof Integer) { |
| retVal = ((Integer)o).intValue(); |
| } |
| return retVal; |
| } |
| |
| public String getString(String name) { |
| String retVal = ""; //$NON-NLS-1$ |
| Object o = fProperties.get(name); |
| if (o instanceof String) { |
| retVal = (String)o; |
| } |
| return retVal; |
| } |
| |
| public double getDouble(String name) { return 0; } |
| public float getFloat(String name) { return 0; } |
| public long getLong(String name) { return 0; } |
| |
| public boolean isDefault(String name) { return false; } |
| |
| public boolean getDefaultBoolean(String name) { return false; } |
| public double getDefaultDouble(String name) { return 0; } |
| public float getDefaultFloat(String name) { return 0; } |
| public int getDefaultInt(String name) { return 0; } |
| public long getDefaultLong(String name) { return 0; } |
| public String getDefaultString(String name) { return null; } |
| |
| public void putValue(String name, String value) { |
| Object oldValue = fProperties.get(name); |
| if ( oldValue == null || !oldValue.equals(value) ) { |
| fProperties.put(name, value); |
| setDirty(true); |
| } |
| } |
| |
| public void setDefault(String name, double value) {} |
| public void setDefault(String name, float value) {} |
| public void setDefault(String name, int value) {} |
| public void setDefault(String name, long value) {} |
| public void setDefault(String name, String defaultObject) {} |
| public void setDefault(String name, boolean value) {} |
| public void setToDefault(String name) {} |
| |
| public void setValue(String name, boolean value) { |
| boolean oldValue = getBoolean(name); |
| if (oldValue != value) { |
| fProperties.put( name, Boolean.valueOf(value) ); |
| setDirty(true); |
| firePropertyChangeEvent(name, Boolean.valueOf(oldValue), Boolean.valueOf(value) ); |
| } |
| } |
| |
| public void setValue(String name, int value) { |
| int oldValue = getInt(name); |
| if (oldValue != value) { |
| fProperties.put( name, Integer.valueOf(value) ); |
| setDirty(true); |
| firePropertyChangeEvent(name, Integer.valueOf(oldValue), Integer.valueOf(value) ); |
| } |
| } |
| |
| public void setValue(String name, String value) { |
| Object oldValue = fProperties.get(name); |
| if ( (oldValue == null && value != null) || |
| (oldValue != null && !oldValue.equals(value)) ) |
| { |
| fProperties.put(name, value); |
| setDirty(true); |
| firePropertyChangeEvent(name, oldValue, value); |
| } |
| } |
| |
| public void setValue(String name, float value) {} |
| public void setValue(String name, double value) {} |
| public void setValue(String name, long value) {} |
| |
| // IPreferenceStore |
| /////////////////////////////////////////////////////////////////////// |
| |
| private void setDirty(boolean isDirty) { |
| fIsDirty = isDirty; |
| } |
| } |