/******************************************************************************* * Copyright (c) 2005, 2015 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 * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * Sebastian Schmidt - bug 384460 *******************************************************************************/ package org.eclipse.debug.ui.actions; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; import java.io.StringReader; import java.io.UnsupportedEncodingException; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.SubMonitor; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.model.IBreakpoint; import org.eclipse.debug.core.model.IBreakpointImportParticipant; import org.eclipse.debug.internal.core.BreakpointManager; import org.eclipse.debug.internal.ui.IInternalDebugUIConstants; import org.eclipse.debug.internal.ui.importexport.breakpoints.IImportExportConstants; import org.eclipse.debug.internal.ui.importexport.breakpoints.ImportExportMessages; import org.eclipse.debug.ui.IDebugUIConstants; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.ui.IMemento; import org.eclipse.ui.IWorkingSet; import org.eclipse.ui.IWorkingSetManager; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.XMLMemento; import com.ibm.icu.text.MessageFormat; /** * Imports breakpoints from a file or string buffer into the workspace. * <p> * This class may be instantiated. * <p> * @since 3.2 * @noextend This class is not intended to be subclassed by clients. */ public class ImportBreakpointsOperation implements IRunnableWithProgress { private boolean fOverwriteAll = false; private String fFileName = null; private boolean fCreateWorkingSets = false; private ArrayList<IBreakpoint> fAdded = new ArrayList<IBreakpoint>(); private String fCurrentWorkingSetProperty = null; private BreakpointManager fManager = (BreakpointManager) DebugPlugin.getDefault().getBreakpointManager(); /** * When a buffer is specified, a file is not used. */ private StringBuffer fBuffer = null; private boolean fImportBreakpoints = true; /** * Constructs an operation to import breakpoints. * * @param fileName the file to read breakpoints from - the file should have been * created from an export operation * @param overwrite whether imported breakpoints will overwrite existing equivalent breakpoints * @param createWorkingSets whether breakpoint working sets should be created. Breakpoints * are exported with information about the breakpoint working sets they belong to. Those * working sets can be optionally re-created on import if they do not already exist in the * workspace. */ public ImportBreakpointsOperation(String fileName, boolean overwrite, boolean createWorkingSets) { this(fileName, overwrite, createWorkingSets, true); } /** * Constructs an operation to import breakpoints. * * @param fileName the file to read breakpoints from - the file should have been created from an * export operation * @param overwrite whether imported breakpoints will overwrite existing equivalent breakpoints * @param createWorkingSets whether breakpoint working sets should be created. Breakpoints are * exported with information about the breakpoint working sets they belong to. Those * working sets can be optionally re-created on import if they do not already exist * in the workspace. * @param importBreakpoints whether breakpoints should be imported and registered * @since 3.9 */ public ImportBreakpointsOperation(String fileName, boolean overwrite, boolean createWorkingSets, boolean importBreakpoints) { fFileName = fileName; fOverwriteAll = overwrite; fCreateWorkingSets = createWorkingSets; fImportBreakpoints = importBreakpoints; } /** * Constructs an operation to import breakpoints from a string buffer. The buffer * must contain a memento created an {@link ExportBreakpointsOperation}. * * @param buffer the string buffer to read breakpoints from - the file should have been * created from an export operation * @param overwrite whether imported breakpoints will overwrite existing equivalent breakpoints * @param createWorkingSets whether breakpoint working sets should be created. Breakpoints * are exported with information about the breakpoint working sets they belong to. Those * working sets can be optionally re-created on import if they do not already exist in the * workspace. * @since 3.5 */ public ImportBreakpointsOperation(StringBuffer buffer, boolean overwrite, boolean createWorkingSets) { this(buffer, overwrite, createWorkingSets, true); } /** * Constructs an operation to import breakpoints from a string buffer. The buffer must contain a * memento created an {@link ExportBreakpointsOperation}. * * @param buffer the string buffer to read breakpoints from - the file should have been created * from an export operation * @param overwrite whether imported breakpoints will overwrite existing equivalent breakpoints * @param createWorkingSets whether breakpoint working sets should be created. Breakpoints are * exported with information about the breakpoint working sets they belong to. Those * working sets can be optionally re-created on import if they do not already exist * in the workspace. * @param importBreakpoints whether breakpoints should be imported and registered * @since 3.9 */ public ImportBreakpointsOperation(StringBuffer buffer, boolean overwrite, boolean createWorkingSets, boolean importBreakpoints) { fBuffer = buffer; fOverwriteAll = overwrite; fCreateWorkingSets = createWorkingSets; fImportBreakpoints = importBreakpoints; } /* (non-Javadoc) * @see org.eclipse.core.resources.IWorkspaceRunnable#run(org.eclipse.core.runtime.IProgressMonitor) */ @Override public void run(final IProgressMonitor monitor) throws InvocationTargetException { SubMonitor localmonitor = SubMonitor.convert(monitor, ImportExportMessages.ImportOperation_0, 1); try { XMLMemento memento = null; if (fBuffer == null) { try (Reader reader = new InputStreamReader(new FileInputStream(fFileName), "UTF-8")) { //$NON-NLS-1$ memento = XMLMemento.createReadRoot(reader); } catch (FileNotFoundException e) { throw new InvocationTargetException(e, MessageFormat.format("Breakpoint import file not found: {0}", new Object[] { //$NON-NLS-1$ fFileName })); } catch (UnsupportedEncodingException e) { throw new InvocationTargetException(e, MessageFormat.format("The import file was written in non-UTF-8 encoding.", new Object[] { //$NON-NLS-1$ fFileName })); } catch (IOException e) { throw new InvocationTargetException(e); } } else { try (Reader reader = new StringReader(fBuffer.toString())) { memento = XMLMemento.createReadRoot(reader); } catch (IOException e) { throw new InvocationTargetException(e); } } IMemento[] nodes = memento.getChildren(IImportExportConstants.IE_NODE_BREAKPOINT); IWorkspaceRoot workspace = ResourcesPlugin.getWorkspace().getRoot(); localmonitor.setWorkRemaining(nodes.length); Map<String, Object> attributes = null; IBreakpointImportParticipant[] participants = null; for(int i = 0; i < nodes.length; i++) { if(localmonitor.isCanceled()) { return; } attributes = collectBreakpointProperties(nodes[i]); if(attributes == null) { continue; } IResource resource; if(fImportBreakpoints) { resource = workspace.findMember((String) attributes.get(IImportExportConstants.IE_NODE_PATH)); } else { resource = workspace; } // filter resource breakpoints that do not exist in this workspace if(resource != null) { try { participants = fManager.getImportParticipants((String) attributes.get(IImportExportConstants.IE_NODE_TYPE)); } catch(CoreException ce) {} IMarker marker = findExistingMarker(attributes, participants); if(marker == null) { marker = resource.createMarker((String) attributes.get(IImportExportConstants.IE_NODE_TYPE)); restoreBreakpoint(marker, attributes, participants); } else { if(fOverwriteAll) { if(!fImportBreakpoints) { marker = resource.createMarker((String) attributes.get(IImportExportConstants.IE_NODE_TYPE)); } else { marker.setAttributes(null); } restoreBreakpoint(marker, attributes, participants); } } } fCurrentWorkingSetProperty = null; localmonitor.worked(1); } if(fAdded.size() > 0 && fImportBreakpoints) { fManager.addBreakpoints(fAdded.toArray(new IBreakpoint[fAdded.size()])); } } catch(CoreException ce) { throw new InvocationTargetException(ce, MessageFormat.format("There was a problem importing breakpoints from: {0}", new Object[] { fFileName })); //$NON-NLS-1$ } finally { localmonitor.done(); } } /** * Returns a marker backing an existing breakpoint based on the given set of breakpoint attributes * @param attributes the map of attributes to compare for marker equality * @param participants the list of participants to ask if a breakpoint matches the given map of attributes * @return the marker for an existing breakpoint or <code>null</code> if one could not be located * @since 3.5 */ protected IMarker findExistingMarker(Map<String, Object> attributes, IBreakpointImportParticipant[] participants) { IBreakpoint[] bps = fManager.getBreakpoints(); for(int i = 0; i < bps.length; i++) { for(int j = 0; j < participants.length; j++) { try { if(participants[j].matches(attributes, bps[i])) { return bps[i].getMarker(); } } catch(CoreException ce) {} } } return null; } /** * Collects all of the properties for a breakpoint from the memento describing it. * The values in the map will be one of: * <ul> * <li>{@link String}</li> * <li>{@link Integer}</li> * <li>{@link Boolean}</li> * </ul> * @param memento the memento to read breakpoint attributes from * @return a new map of all of the breakpoint attributes from the given memento. * @since 3.5 */ protected Map<String, Object> collectBreakpointProperties(IMemento memento) { HashMap<String, Object> map = new HashMap<String, Object>(); //collect attributes from the 'breakpoint' node map.put(IImportExportConstants.IE_BP_ENABLED, memento.getBoolean(IImportExportConstants.IE_BP_ENABLED)); map.put(IImportExportConstants.IE_BP_PERSISTANT, memento.getBoolean(IImportExportConstants.IE_BP_PERSISTANT)); map.put(IImportExportConstants.IE_BP_REGISTERED, memento.getBoolean(IImportExportConstants.IE_BP_REGISTERED)); //collect attributes from the 'marker' node IMemento child = memento.getChild(IImportExportConstants.IE_NODE_MARKER); map.put(IImportExportConstants.IE_NODE_TYPE, child.getString(IImportExportConstants.IE_NODE_TYPE)); map.put(IMarker.LINE_NUMBER, child.getInteger(IMarker.LINE_NUMBER)); //copy all the marker attributes to the map IMemento[] children = child.getChildren(IImportExportConstants.IE_NODE_ATTRIB); for(int i = 0; i < children.length; i++) { readAttribute(children[i], map); } //collect attributes from the 'resource' node child = memento.getChild(IImportExportConstants.IE_NODE_RESOURCE); map.put(IImportExportConstants.IE_NODE_PATH, child.getString(IImportExportConstants.IE_NODE_PATH)); return map; } /** * Collects the 'name' and 'value' key / attribute from the given memento and places it in the specified map * @param memento the memento to read a name / value attribute from * @param map the map to add the read attribute to */ private void readAttribute(IMemento memento, Map<String, Object> map) { String name = memento.getString(IImportExportConstants.IE_NODE_NAME), value = memento.getString(IImportExportConstants.IE_NODE_VALUE); if (value != null && name != null) { if (name.equals(IInternalDebugUIConstants.WORKING_SET_NAME)) { fCurrentWorkingSetProperty = value; } Object val = value; try { val = Integer.valueOf(value); } catch (NumberFormatException e) { if (value.equalsIgnoreCase("false") || value.equalsIgnoreCase("true")) { //$NON-NLS-1$ //$NON-NLS-2$ val = Boolean.valueOf(value); } } if(val != null) { map.put(name, val); } } } /** * restores all of the attributes back into the given marker, recreates the breakpoint in the * breakpoint manager, and optionally recreates any working set(s) the breakpoint belongs to. * @param marker the marker to create the new breakpoint on * @param attributes the attributes to set in the new breakpoint * @param participants the list of participants used to verify the restored breakpoint * @since 3.5 */ protected void restoreBreakpoint(IMarker marker, final Map<String, Object> attributes, IBreakpointImportParticipant[] participants) { for (Entry<String, Object> entry : attributes.entrySet()) { try { marker.setAttribute(entry.getKey(), entry.getValue()); } catch (CoreException ce) { } } IBreakpoint breakpoint = null; try { // create the breakpoint breakpoint = fManager.createBreakpoint(marker); breakpoint.setEnabled(((Boolean)attributes.get(IImportExportConstants.IE_BP_ENABLED)).booleanValue()); breakpoint.setPersisted(((Boolean)attributes.get(IImportExportConstants.IE_BP_PERSISTANT)).booleanValue()); breakpoint.setRegistered(((Boolean)attributes.get(IImportExportConstants.IE_BP_REGISTERED)).booleanValue()); fAdded.add(breakpoint); if (fImportBreakpoints && fCreateWorkingSets && fCurrentWorkingSetProperty != null) { String[] names = fCurrentWorkingSetProperty.split("\\" + IImportExportConstants.DELIMITER); //$NON-NLS-1$ updateWorkingSets(names, breakpoint); } if(participants != null) { for(int i = 0; i < participants.length; i++) { participants[i].verify(breakpoint); } } } catch(CoreException ce) { //Something bad happened while trying to restore the breakpoint, remove it from the cached list and delete the marker //to ensure the manager does not hold bogus breakpoints if(breakpoint != null) { try { fAdded.remove(breakpoint); marker.delete(); } catch (CoreException e) {} } } } /** * Updates the working sets the given breakpoint belongs to * @param wsnames the array of working set names * @param breakpoint the breakpoint to add to the working sets * @since 3.5 */ private void updateWorkingSets(String[] wsnames, IBreakpoint breakpoint) { IWorkingSetManager mgr = PlatformUI.getWorkbench().getWorkingSetManager(); ArrayList<IWorkingSet> sets = new ArrayList<IWorkingSet>(); collectContainingWorkingsets(breakpoint, sets); for (int i = 0; i < wsnames.length; i++) { if("".equals(wsnames[i])) { //$NON-NLS-1$ continue; } IWorkingSet set = mgr.getWorkingSet(wsnames[i]); if(set == null) { //create working set set = mgr.createWorkingSet(wsnames[i], new IAdaptable[] {}); set.setId(IDebugUIConstants.BREAKPOINT_WORKINGSET_ID); mgr.addWorkingSet(set); } if(!sets.contains(set)) { IAdaptable[] elements = set.getElements(); IAdaptable[] newElements = new IAdaptable[elements.length + 1]; newElements[newElements.length - 1] = breakpoint; System.arraycopy(elements, 0, newElements, 0, elements.length); set.setElements(newElements); } sets.remove(set); } ArrayList<IAdaptable> items = null; for (IWorkingSet set : sets) { items = new ArrayList<IAdaptable>(Arrays.asList(set.getElements())); if(items.remove(breakpoint)) { set.setElements(items.toArray(new IAdaptable[items.size()])); } } } /** * Collects all of the breakpoint working sets that contain the given {@link IBreakpoint} * in the given list * * @param breakpoint the breakpoint to collect working set containers from * @param collector the list to collect containing working sets in * @since 3.5 */ private void collectContainingWorkingsets(IBreakpoint breakpoint, List<IWorkingSet> collector) { IWorkingSetManager mgr = PlatformUI.getWorkbench().getWorkingSetManager(); IWorkingSet[] sets = mgr.getWorkingSets(); for (int i = 0; i < sets.length; i++) { if(IDebugUIConstants.BREAKPOINT_WORKINGSET_ID.equals(sets[i].getId()) && containsBreakpoint(sets[i], breakpoint)) { collector.add(sets[i]); } } } /** * Method to ensure markers and breakpoints are not both added to the working set * @param set the set to check * @param breakpoint the breakpoint to check for existence * @return true if it is present false otherwise */ private boolean containsBreakpoint(IWorkingSet set, IBreakpoint breakpoint) { IAdaptable[] elements = set.getElements(); for (int i = 0; i < elements.length; i++) { if (elements[i].equals(breakpoint)) { return true; } } return false; } /** * Returns the breakpoints that were imported by this operation, possibly * an empty list. * * @return breakpoints imported by this operation * @since 3.5 */ public IBreakpoint[] getImportedBreakpoints() { return fAdded.toArray(new IBreakpoint[fAdded.size()]); } }