/******************************************************************************* * Copyright (c) 2007-2009 Wind River Systems, 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: * Ted R Williams (Wind River Systems, Inc.) - initial implementation *******************************************************************************/ package org.eclipse.cdt.debug.ui.memory.search; import java.lang.reflect.Method; import java.math.BigInteger; import java.util.Properties; import java.util.StringTokenizer; import java.util.Vector; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.model.IMemoryBlockExtension; import org.eclipse.debug.core.model.MemoryByte; import org.eclipse.debug.ui.memory.IMemoryRendering; import org.eclipse.debug.ui.memory.IMemoryRenderingContainer; import org.eclipse.debug.ui.memory.IMemoryRenderingSite; import org.eclipse.debug.ui.memory.IRepositionableMemoryRendering; import org.eclipse.jface.action.IAction; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.search.ui.ISearchQuery; import org.eclipse.search.ui.ISearchResult; import org.eclipse.search.ui.NewSearchUI; import org.eclipse.swt.SWT; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.layout.FormAttachment; import org.eclipse.swt.layout.FormData; import org.eclipse.swt.layout.FormLayout; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Group; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.dialogs.SelectionDialog; public class FindReplaceDialog extends SelectionDialog { private IMemoryBlockExtension fMemoryBlock; final static int preFetchSize = 20 * 1024; private Text fFindText; private Text fReplaceText; private Combo fStartText; private Combo fEndText; private Button fFindButton; private Button fFindAllButton; private Button fReplaceButton; private Button fReplaceFindButton; private Button fReplaceAllButton; private IMemoryRenderingSite fMemoryView; private Button fFormatAsciiButton; private Button fFormatHexButton; private Button fFormatOctalButton; private Button fFormatBinaryButton; private Button fFormatDecimalButton; private Button fFormatByteSequenceButton; private Button fCaseInSensitiveCheckbox; private Button fWrapCheckbox; private Button fForwardButton; private Properties fProperties; protected final static String SEARCH_FIND = "SEARCH_FIND"; //$NON-NLS-1$ protected final static String SEARCH_REPLACE = "SEARCH_REPLACE"; //$NON-NLS-1$ protected final static String SEARCH_START = "SEARCH_START"; //$NON-NLS-1$ protected final static String SEARCH_END = "SEARCH_END"; //$NON-NLS-1$ protected final static String SEARCH_LAST_START = "SEARCH_LAST_START"; //$NON-NLS-1$ protected final static String SEARCH_LAST_END = "SEARCH_LAST_END"; //$NON-NLS-1$ protected final static String SEARCH_FORMAT = "SEARCH_FORMAT"; //$NON-NLS-1$ protected final static String SEARCH_FORMAT_ASCII = "SEARCH_FORMAT_ASCII"; //$NON-NLS-1$ protected final static String SEARCH_FORMAT_HEX = "SEARCH_FORMAT_HEX"; //$NON-NLS-1$ protected final static String SEARCH_FORMAT_OCTAL = "SEARCH_FORMAT_OCTAL"; //$NON-NLS-1$ protected final static String SEARCH_FORMAT_BINARY = "SEARCH_FORMAT_BINARY"; //$NON-NLS-1$ protected final static String SEARCH_FORMAT_DECIMAL = "SEARCH_FORMAT_DECIMAL"; //$NON-NLS-1$ protected final static String SEARCH_FORMAT_BYTESEQUENCE = "SEARCH_FORMAT_BYTESEQUENCE"; //$NON-NLS-1$ protected final static String SEARCH_FORMAT_CASEINSENSTIVE = "SEARCH_FORMAT_CASEINSENSTIVE"; //$NON-NLS-1$ protected final static String SEARCH_FORMAT_FORWARD = "SEARCH_FORMAT_FORWARD"; //$NON-NLS-1$ protected final static String SEARCH_FORMAT_WRAP = "SEARCH_FORMAT_WRAP"; //$NON-NLS-1$ protected final static String SEARCH_ENABLE_FIND_NEXT = "SEARCH_ENABLE_FIND_NEXT"; //$NON-NLS-1$ //the width of text fields of Find and Replace, increase it to 400 to fix the tvt defect 356901 protected final static int FIND_REPLACE_TEXT_WIDTH = 400; private IAction fFindAction = null; public FindReplaceDialog(Shell parent, IMemoryBlockExtension memoryBlock, IMemoryRenderingSite memoryView, Properties properties, IAction findAction) { super(parent); super.setTitle(Messages.getString("FindReplaceDialog.Title")); //$NON-NLS-1$ setShellStyle(getShellStyle() | SWT.RESIZE); fMemoryBlock = memoryBlock; fMemoryView = memoryView; fProperties = properties; this.setBlockOnOpen(false); fFindAction = findAction; } private BigInteger getUserStart() { String start = fStartText.getText(); if(start.toUpperCase().startsWith("0X")) //$NON-NLS-1$ start = start.substring(2); return new BigInteger(start, 16); } private BigInteger getUserEnd() { String end = fEndText.getText(); if(end.toUpperCase().startsWith("0X")) //$NON-NLS-1$ end = end.substring(2); return new BigInteger(end, 16); } private boolean getIsDirectionForward() { return fForwardButton.getSelection(); } private SearchPhrase getSearchPhrase() { SearchPhrase phrase = null; if(fFormatAsciiButton.getSelection()) { phrase = new AsciiSearchPhrase(fFindText.getText(), fCaseInSensitiveCheckbox.getSelection()); } else if(fFormatHexButton.getSelection()) { phrase = new BigIntegerSearchPhrase(new BigInteger(fFindText.getText().toUpperCase().startsWith("0X") //$NON-NLS-1$ ? fFindText.getText().substring(2) : fFindText.getText(), 16), 16); } else if(fFormatOctalButton.getSelection()) { phrase = new BigIntegerSearchPhrase(new BigInteger(fFindText.getText().startsWith("0") //$NON-NLS-1$ ? fFindText.getText().substring(1) : fFindText.getText(), 8), 8); } else if(fFormatBinaryButton.getSelection()) { phrase = new BigIntegerSearchPhrase(new BigInteger(fFindText.getText().toUpperCase().startsWith("0B") //$NON-NLS-1$ ? fFindText.getText().substring(2) : fFindText.getText(), 2), 2); } else if(fFormatDecimalButton.getSelection()) { phrase = new BigIntegerSearchPhrase(new BigInteger(fFindText.getText(), 10), 10); } else if(fFormatByteSequenceButton.getSelection()) { phrase = new ByteSequenceSearchPhrase(fFindText.getText()); } return phrase; } protected byte[] parseByteSequence(String s) { Vector<Byte> sequence = new Vector<Byte>(); StringTokenizer st = new StringTokenizer(s, " "); //$NON-NLS-1$ while(st.hasMoreElements()) { String element = ((String) st.nextElement()).trim(); if(element.length() > 0) { BigInteger value; if(element.toUpperCase().startsWith("0X")) //$NON-NLS-1$ value = new BigInteger(element.substring(2), 16); else if(element.toUpperCase().startsWith("0B")) //$NON-NLS-1$ value = new BigInteger(element.substring(2), 2); else if(element.toUpperCase().startsWith("0")) //$NON-NLS-1$ value = new BigInteger(element.substring(1), 8); else value = new BigInteger(element, 10); Byte b = new Byte(value.byteValue()); if(value.compareTo(BigInteger.valueOf(255)) > 0) return null; sequence.addElement(b); } } Byte seq[] = sequence.toArray(new Byte[sequence.size()]); byte[] bytes = new byte[seq.length]; for(int i = 0; i < seq.length; i++) bytes[i] = seq[i].byteValue(); return bytes; } private byte[] getReplaceData() { if(fFormatAsciiButton.getSelection()) return fReplaceText.getText().getBytes(); else if(fFormatHexButton.getSelection()) return removeZeroPrefixByte(new BigInteger(fReplaceText.getText().toUpperCase().startsWith("0X") ? fReplaceText.getText().substring(2) : fReplaceText.getText(), 16).toByteArray()); //$NON-NLS-1$ else if(fFormatOctalButton.getSelection()) return removeZeroPrefixByte(new BigInteger(fReplaceText.getText().startsWith("0") ? fReplaceText.getText().substring(1) : fReplaceText.getText(), 8).toByteArray()); //$NON-NLS-1$ else if(fFormatBinaryButton.getSelection()) return removeZeroPrefixByte(new BigInteger(fReplaceText.getText().toUpperCase().startsWith("0B") ? fReplaceText.getText().substring(2) : fReplaceText.getText(), 2).toByteArray()); //$NON-NLS-1$ else if(fFormatDecimalButton.getSelection()) return removeZeroPrefixByte(new BigInteger(fReplaceText.getText(), 10).toByteArray()); else if(fFormatByteSequenceButton.getSelection()) return parseByteSequence(fReplaceText.getText()); return new byte[0]; } /* (non-Javadoc) * @see org.eclipse.jface.dialogs.Dialog#createButtonsForButtonBar(org.eclipse.swt.widgets.Composite) */ protected void createButtonsForButtonBar(Composite parent) { fFindButton = createButton(parent, 10, Messages.getString("FindReplaceDialog.ButtonFind"), true); //$NON-NLS-1$ fFindButton.addSelectionListener(new SelectionListener() { public void widgetDefaultSelected(SelectionEvent e) { } public void widgetSelected(SelectionEvent e) { performFind(getUserStart(), getUserEnd(), getSearchPhrase(), getIsDirectionForward(), null, false, false); cancelPressed(); } }); fFindAllButton = createButton(parent, 10, Messages.getString("FindReplaceDialog.ButtonFindAll"), true); //$NON-NLS-1$ fFindAllButton.addSelectionListener(new SelectionListener() { public void widgetDefaultSelected(SelectionEvent e) { } public void widgetSelected(SelectionEvent e) { performFind(getUserStart(), getUserEnd(), getSearchPhrase(), getIsDirectionForward(), null, true, false); cancelPressed(); } }); fReplaceFindButton = createButton(parent, 11, Messages.getString("FindReplaceDialog.ButtonReplaceFind"), false); //$NON-NLS-1$ fReplaceFindButton.addSelectionListener(new SelectionListener() { public void widgetDefaultSelected(SelectionEvent e) { } public void widgetSelected(SelectionEvent e) { performFind(getUserStart(), getUserEnd(), getSearchPhrase(), getIsDirectionForward(), getReplaceData(), false, true); cancelPressed(); } }); fReplaceButton = createButton(parent, 12, Messages.getString("FindReplaceDialog.ButtonReplace"), false); //$NON-NLS-1$ fReplaceButton.addSelectionListener(new SelectionListener() { public void widgetDefaultSelected(SelectionEvent e) { } public void widgetSelected(SelectionEvent e) { performFind(getUserStart(), getUserEnd(), getSearchPhrase(), getIsDirectionForward(), getReplaceData(), false, false); cancelPressed(); } }); fReplaceAllButton = createButton(parent, 13, Messages.getString("FindReplaceDialog.ButtonReplaceAll"), false); //$NON-NLS-1$ fReplaceAllButton.addSelectionListener(new SelectionListener() { public void widgetDefaultSelected(SelectionEvent e) { } public void widgetSelected(SelectionEvent e) { performFind(getUserStart(), getUserEnd(), getSearchPhrase(), getIsDirectionForward(), getReplaceData(), true, false); cancelPressed(); } }); createButton(parent, IDialogConstants.CANCEL_ID, Messages.getString("FindReplaceDialog.Close"), false); //$NON-NLS-1$ ((GridLayout) parent.getLayout()).numColumns = 2; validate(); } /* (non-Javadoc) * @see org.eclipse.ui.dialogs.SelectionDialog#getResult() */ public Object[] getResult() { Object[] results = super.getResult(); if (results != null) { return results; } return new Object[0]; } /* (non-Javadoc) * @see org.eclipse.jface.dialogs.Dialog#cancelPressed() */ protected void cancelPressed() { fProperties.setProperty(SEARCH_FIND, fFindText.getText()); fProperties.setProperty(SEARCH_REPLACE, fReplaceText.getText()); fProperties.setProperty(SEARCH_START, fStartText.getText()); fProperties.setProperty(SEARCH_END, fEndText.getText()); if(fFormatAsciiButton.getSelection()) fProperties.setProperty(SEARCH_FORMAT, SEARCH_FORMAT_ASCII); else if(fFormatBinaryButton.getSelection()) fProperties.setProperty(SEARCH_FORMAT, SEARCH_FORMAT_BINARY); else if(fFormatByteSequenceButton.getSelection()) fProperties.setProperty(SEARCH_FORMAT, SEARCH_FORMAT_BYTESEQUENCE); else if(fFormatDecimalButton.getSelection()) fProperties.setProperty(SEARCH_FORMAT, SEARCH_FORMAT_DECIMAL); else if(fFormatHexButton.getSelection()) fProperties.setProperty(SEARCH_FORMAT, SEARCH_FORMAT_HEX); else if(fFormatOctalButton.getSelection()) fProperties.setProperty(SEARCH_FORMAT, SEARCH_FORMAT_OCTAL); fProperties.setProperty(SEARCH_FORMAT_FORWARD, Boolean.toString(fForwardButton.getSelection())); fProperties.setProperty(SEARCH_FORMAT_CASEINSENSTIVE, Boolean.toString(fCaseInSensitiveCheckbox.getSelection())); fProperties.setProperty(SEARCH_FORMAT_WRAP, Boolean.toString(fWrapCheckbox.getSelection())); fProperties.setProperty(SEARCH_ENABLE_FIND_NEXT, Boolean.FALSE.toString()); setResult(null); super.cancelPressed(); } /* (non-Javadoc) * @see org.eclipse.jface.dialogs.Dialog#okPressed() */ protected void okPressed() { setSelectionResult(new Object[]{ fProperties }); super.okPressed(); } public BigInteger getEndAddress() { String text = fEndText.getText(); boolean hex = text.startsWith("0x"); //$NON-NLS-1$ BigInteger endAddress = new BigInteger(hex ? text.substring(2) : text, hex ? 16 : 10); return endAddress; } public BigInteger getStartAddress() { String text = fStartText.getText(); boolean hex = text.startsWith("0x"); //$NON-NLS-1$ BigInteger startAddress = new BigInteger(hex ? text.substring(2) : text, hex ? 16 : 10); return startAddress; } private void validate() { boolean valid = false; boolean replaceValid = false; try { BigInteger endAddress = getEndAddress(); BigInteger startAddress = getStartAddress(); /* * The end-address must be larger that the start-address. */ if ( startAddress.compareTo(endAddress) == -1 ) { /* * Validate the search phrase. */ if(getSearchPhrase() != null && getSearchPhrase().getByteLength() > 0) { valid = true; } /* * Validate the replacement phrase. */ if(getReplaceData() != null && getReplaceData().length > 0) { replaceValid = true; } } } catch(Throwable ex) { // do nothing } fFindButton.setEnabled(valid); fFindAllButton.setEnabled(valid); fReplaceButton.setEnabled(replaceValid); fReplaceFindButton.setEnabled(replaceValid); fReplaceAllButton.setEnabled(replaceValid); } private String pad(int characterCount, String value) { StringBuffer sb = new StringBuffer(value); for(int i = 0; i < characterCount - value.length(); i++) sb.insert(0, "0"); //$NON-NLS-1$ return sb.toString(); } private String[] removeNullElements(String strings[]) { Vector<String> nonNullStrings = new Vector<String>(); for(String string : strings) if(string != null) nonNullStrings.addElement(string); return (String[]) nonNullStrings.toArray(new String[0]); } private String getMemoryBlockBaseAddress() { BigInteger base = null; try { base = fMemoryBlock.getBigBaseAddress(); } catch(DebugException de) { // do nothing } if(base == null) base = BigInteger.ZERO; return "0x" + pad(getAddressSize() * 2, base.toString(16).toUpperCase()); //$NON-NLS-1$ } private String getViewportStart() { ISelection selection = fMemoryView.getMemoryRenderingContainers()[0].getMemoryRenderingSite().getSite().getSelectionProvider().getSelection(); if(selection instanceof StructuredSelection) { if(((StructuredSelection) selection).getFirstElement() instanceof IRepositionableMemoryRendering) { ((IRepositionableMemoryRendering) ((StructuredSelection) selection).getFirstElement()).getSelectedAddress(); } } return null; } private String getStart() { BigInteger start = null; try { start = fMemoryBlock.getMemoryBlockStartAddress(); } catch(DebugException de) { // do nothing } if(start == null) start = BigInteger.ZERO; return "0x" + pad(getAddressSize() * 2, start.toString(16).toUpperCase()); //$NON-NLS-1$ } private String getEnd() { BigInteger end = null; try { end = fMemoryBlock.getMemoryBlockEndAddress(); } catch(DebugException de) { // do nothing } if(end == null) { end = BigInteger.ZERO; for(int i = getAddressSize(); i > 0; i--) { end = end.shiftLeft(8); end = end.or(BigInteger.valueOf(255)); } } return "0x" + pad(getAddressSize() * 2, end.toString(16).toUpperCase()); //$NON-NLS-1$ } private int getAddressSize() { int addressSize; try { addressSize = fMemoryBlock.getAddressSize(); } catch(DebugException de) { addressSize = 4; // default to 32bit? } return addressSize; } /* (non-Javadoc) * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite) */ protected Control createDialogArea(Composite parent) { PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, MemorySearchPlugin.getUniqueIdentifier() + ".FindReplaceDialog_context"); //$NON-NLS-1$ Composite composite = new Composite(parent, SWT.NONE); FormLayout formLayout = new FormLayout(); formLayout.spacing = 5; formLayout.marginWidth = formLayout.marginHeight = 9; composite.setLayout(formLayout); // find Label findLabel = new Label(composite, SWT.NONE); Label replaceLabel = new Label(composite, SWT.NONE); fReplaceText = new Text(composite, SWT.BORDER); findLabel.setText(Messages.getString("FindReplaceDialog.LabelFind")); //$NON-NLS-1$ fFindText = new Text(composite, SWT.BORDER); FormData data = new FormData(); data.left = new FormAttachment(fReplaceText, 0, SWT.LEFT); data.width = FIND_REPLACE_TEXT_WIDTH; fFindText.setLayoutData(data); fFindText.setText(fProperties.getProperty(SEARCH_FIND, "")); //$NON-NLS-1$ data = new FormData(); data.top = new FormAttachment(fFindText, 0, SWT.CENTER); findLabel.setLayoutData(data); // replace replaceLabel.setText(Messages.getString("FindReplaceDialog.LabelReplaceWith")); //$NON-NLS-1$ data = new FormData(); data.top = new FormAttachment(fFindText); replaceLabel.setLayoutData(data); data = new FormData(); data.top = new FormAttachment(replaceLabel, 0, SWT.CENTER); data.left = new FormAttachment(replaceLabel); data.width = FIND_REPLACE_TEXT_WIDTH; fReplaceText.setLayoutData(data); fReplaceText.setText(fProperties.getProperty(SEARCH_REPLACE, "")); //$NON-NLS-1$ // group direction Group directionGroup = new Group(composite, SWT.NONE); Group formatGroup = new Group(composite, SWT.NONE); Group rangeGroup = new Group(composite, SWT.NONE); directionGroup.setText(Messages.getString("FindReplaceDialog.LabelDirection")); //$NON-NLS-1$ GridLayout layout = new GridLayout(); layout.numColumns = 1; directionGroup.setLayout(layout); fForwardButton = new Button(directionGroup, SWT.RADIO); fForwardButton.setText(Messages.getString("FindReplaceDialog.ButtonForward")); //$NON-NLS-1$ final Button backwardButton = new Button(directionGroup, SWT.RADIO); backwardButton.setText(Messages.getString("FindReplaceDialog.ButtonBackward")); //$NON-NLS-1$ final boolean isForward = Boolean.parseBoolean(fProperties.getProperty(SEARCH_FORMAT_FORWARD, Boolean.TRUE.toString())); fForwardButton.setSelection(isForward); backwardButton.setSelection(!isForward); data = new FormData(); data.top = new FormAttachment(fReplaceText); data.right = new FormAttachment(formatGroup, 0, SWT.RIGHT); data.left = new FormAttachment(formatGroup, 0, SWT.LEFT); data.bottom = new FormAttachment(rangeGroup, 0, SWT.BOTTOM); directionGroup.setLayoutData(data); // group range rangeGroup.setText(Messages.getString("FindReplaceDialog.LabelRange")); //$NON-NLS-1$ layout = new GridLayout(); layout.numColumns = 2; layout.makeColumnsEqualWidth = false; rangeGroup.setLayout(layout); // group range - start address Label startLabel = new Label(rangeGroup, SWT.NONE); startLabel.setText(Messages.getString("FindReplaceDialog.LabelStartAddress")); //$NON-NLS-1$ fStartText = new Combo(rangeGroup, SWT.BORDER); GridData gridData = new GridData(); gridData.widthHint = 200; gridData.grabExcessHorizontalSpace = true; fStartText.setLayoutData(gridData); // group range - end address Label endLabel = new Label(rangeGroup, SWT.NONE); endLabel.setText(Messages.getString("FindReplaceDialog.LabelEndAddress")); //$NON-NLS-1$ fEndText = new Combo(rangeGroup, SWT.BORDER); gridData = new GridData(); gridData.widthHint = 200; gridData.grabExcessHorizontalSpace = true; fEndText.setLayoutData(gridData); data = new FormData(); data.left = new FormAttachment(directionGroup); data.top = new FormAttachment(directionGroup, 0, SWT.TOP); data.right = new FormAttachment(fFindText, 0, SWT.RIGHT); rangeGroup.setLayoutData(data); fStartText.setItems(removeNullElements(new String[] { getViewportStart(), getStart(), getEnd(), getMemoryBlockBaseAddress() })); fEndText.setItems(removeNullElements(new String[] { getEnd(), getStart(), getMemoryBlockBaseAddress(), getViewportStart() })); if(fProperties.getProperty(SEARCH_START) != null) fStartText.add(fProperties.getProperty(SEARCH_START), 0); if(fProperties.getProperty(SEARCH_END) != null) fEndText.add(fProperties.getProperty(SEARCH_END), 0); fStartText.select(0); fEndText.select(0); // format group formatGroup.setText(Messages.getString("FindReplaceDialog.LabelFormat")); //$NON-NLS-1$ layout = new GridLayout(); layout.numColumns = 1; formatGroup.setLayout(layout); fFormatAsciiButton = new Button(formatGroup, SWT.RADIO); fFormatAsciiButton.setText(Messages.getString("FindReplaceDialog.ButtonASCII")); //$NON-NLS-1$ fFormatHexButton = new Button(formatGroup, SWT.RADIO); fFormatHexButton.setText(Messages.getString("FindReplaceDialog.ButtonHexadecimal")); //$NON-NLS-1$ fFormatOctalButton = new Button(formatGroup, SWT.RADIO); fFormatOctalButton.setText(Messages.getString("FindReplaceDialog.ButtonOctal")); //$NON-NLS-1$ fFormatBinaryButton = new Button(formatGroup, SWT.RADIO); fFormatBinaryButton.setText(Messages.getString("FindReplaceDialog.ButtonBinary")); //$NON-NLS-1$ fFormatDecimalButton = new Button(formatGroup, SWT.RADIO); fFormatDecimalButton.setText(Messages.getString("FindReplaceDialog.ButtonDecimal")); //$NON-NLS-1$ fFormatByteSequenceButton = new Button(formatGroup, SWT.RADIO); fFormatByteSequenceButton.setText(Messages.getString("FindReplaceDialog.ButtonByteSequence")); //$NON-NLS-1$ final String format = fProperties.getProperty(SEARCH_FORMAT, FindReplaceDialog.SEARCH_FORMAT_ASCII); fFormatAsciiButton.setSelection(format.equals(SEARCH_FORMAT_ASCII)); fFormatOctalButton.setSelection(format.equals(SEARCH_FORMAT_OCTAL)); fFormatBinaryButton.setSelection(format.equals(SEARCH_FORMAT_BINARY)); fFormatDecimalButton.setSelection(format.equals(SEARCH_FORMAT_DECIMAL)); fFormatHexButton.setSelection(format.equals(SEARCH_FORMAT_HEX)); fFormatByteSequenceButton.setSelection(format.equals(SEARCH_FORMAT_BYTESEQUENCE)); data = new FormData(); data.top = new FormAttachment(rangeGroup); formatGroup.setLayoutData(data); // options group Group optionsGroup = new Group(composite, SWT.NONE); optionsGroup.setText(Messages.getString("FindReplaceDialog.LabelOptions")); //$NON-NLS-1$ data = new FormData(); data.left = new FormAttachment(formatGroup); data.top = new FormAttachment(rangeGroup); data.bottom = new FormAttachment(formatGroup, 0, SWT.BOTTOM); data.right = new FormAttachment(rangeGroup, 0, SWT.RIGHT); optionsGroup.setLayoutData(data); layout = new GridLayout(); layout.numColumns = 1; optionsGroup.setLayout(layout); // wrap fWrapCheckbox = new Button(optionsGroup, SWT.CHECK); fWrapCheckbox.setText(Messages.getString("FindReplaceDialog.ButtonWrapSearch")); //$NON-NLS-1$ fWrapCheckbox.setEnabled(false); // TODO implement wrap fCaseInSensitiveCheckbox = new Button(optionsGroup, SWT.CHECK); fCaseInSensitiveCheckbox.setText(Messages.getString("FindReplaceDialog.ButtonCaseInsensitive")); //$NON-NLS-1$ fCaseInSensitiveCheckbox.setEnabled(format.equals(SEARCH_FORMAT_ASCII)); fCaseInSensitiveCheckbox.setSelection(Boolean.parseBoolean(fProperties.getProperty(SEARCH_FORMAT_CASEINSENSTIVE, Boolean.FALSE.toString()))); fFormatAsciiButton.addSelectionListener(new SelectionListener() { public void widgetDefaultSelected(SelectionEvent e) { } public void widgetSelected(SelectionEvent e) { fCaseInSensitiveCheckbox.setEnabled(true); } }); SelectionListener nonAsciiListener = new SelectionListener() { public void widgetDefaultSelected(SelectionEvent e) { } public void widgetSelected(SelectionEvent e) { fCaseInSensitiveCheckbox.setEnabled(false); validate(); } }; fFormatHexButton.addSelectionListener(nonAsciiListener); fFormatOctalButton.addSelectionListener(nonAsciiListener); fFormatBinaryButton.addSelectionListener(nonAsciiListener); fFormatDecimalButton.addSelectionListener(nonAsciiListener); fFormatByteSequenceButton.addSelectionListener(nonAsciiListener); fStartText.addModifyListener(new ModifyListener() { public void modifyText(ModifyEvent e) { boolean valid = true; try { getStartAddress(); } catch(Exception ex) { valid = false; } fStartText.setForeground(valid ? Display.getDefault().getSystemColor(SWT.COLOR_BLACK) : Display.getDefault().getSystemColor(SWT.COLOR_RED)); validate(); } }); fEndText.addModifyListener(new ModifyListener() { public void modifyText(ModifyEvent e) { try { getEndAddress(); fEndText.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK)); } catch(Exception ex) { fEndText.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_RED)); } validate(); } }); fFindText.addModifyListener(new ModifyListener() { public void modifyText(ModifyEvent e) { validate(); } }); fReplaceText.addModifyListener(new ModifyListener() { public void modifyText(ModifyEvent e) { validate(); } }); composite.setTabList(new Control[] { fFindText, fReplaceText, directionGroup, rangeGroup, formatGroup, optionsGroup, }); fFindText.setFocus(); return composite; } class FindReplaceMemoryCache { BigInteger memoryCacheStartAddress = BigInteger.ZERO; MemoryByte memoryCacheData[] = new MemoryByte[0]; } /** * Function : getSearchableBytes * * This function returns to the user an array of memory * @param start Address ( inclusive ) of the beginning byte of the memory region to be searched * @param end Address ( inclusive ) of the ending * @param forwardSearch direction of the search ( true == searching forward , false = searching backwards * @param address Address ( inclusive ) of the byte set being requested/returned * @param length Number of bytes of data to be returned * @param cache Cached memory byte data ( this routine fetches additional bytes of memory to try and reduce interaction with the debug engine ) * @return MemoryByte[] array which contains the requested bytes * @throws DebugException */ private MemoryByte[] getSearchableBytes(BigInteger start, BigInteger end, boolean forwardSearch, BigInteger address, int length, FindReplaceMemoryCache cache) throws DebugException { BigInteger endCacheAddress = cache.memoryCacheStartAddress.add(BigInteger.valueOf(cache.memoryCacheData.length)); /* * Determine if the requested data is already within the cache. */ if( ! ( ( address.compareTo(cache.memoryCacheStartAddress) >= 0 ) && ( address.add(BigInteger.valueOf(length)).compareTo(endCacheAddress) < 0 ) ) ) { BigInteger prefetchSize = BigInteger.valueOf(preFetchSize); BigInteger len = BigInteger.valueOf(length); BigInteger fetchAddress = address; BigInteger fetchSize; /* * Determine which way we are searching. Whichever way we are searching we need to make sure * we capture the minimum requested amount of data in the forward direction. */ if ( forwardSearch ) { /* * Legend : "#" == minimum requested data , "*" == additional data we want to prefetch/cache * * This is the best case where everything cleanly fits within the starting/ending ranges * to be searched. What we cannot do is to fetch data outside of these ranges. The user * has specified them, if they are in error that is OK, but we must respect the boundaries * they specified. * * +-- address * | * +--length--+--prefetch--+------------------------------------+ * |##########|************| | * |##########|************| | * |##########|************| | * +----------+------------+------------------------------------+ * | | * +-- start end --+ * * This is the worst case scenario. We cannot even get the requested minimum ( no matter * the desired prefetch ) before we run out of the specified range. * * +-- address * | * +----------------------------------------------------+--length--+--prefetch--+ * | |##########|************| * | |##########|************| * | |##########|************| * +----------------------------------------------------+-------+--+------------+ * | | * +-- start end --+ * * See if the desired size ( minimum length + desired prefetch ) fits in to the current range. * If so there is nothing to adjust. */ if ( prefetchSize.compareTo(len) >= 0 ) { fetchSize = prefetchSize; } else { fetchSize = len; } if ( address.add( fetchSize ).compareTo(end) > 0 ) { /* * It does not all fit. Get as much as we can ( end - current ) + 1. */ fetchSize = end.subtract(address).add(BigInteger.ONE); /* * If the amount of data we can get does not even meet the minimum request. In this case * we have to readjust how much we copy to match what we can actually read. If we do not * do this then we will run past the actual data fetched and generate an exception. */ if ( fetchSize.compareTo(len) < 0 ) { length = fetchSize.intValue(); } } /* * The fetch address just starts at the current requested location since we are searching in * the forward direction and thus prefetching in the forward direction. */ fetchAddress = address; } else { /* * Legend : "#" == minimum requested data , "*" == additional data we want to prefetch/cache * * This is the best case where everything cleanly fits within the starting/ending ranges * to be searched. What we cannot do is to fetch data outside of these ranges. The user * has specified them, if they are in error that is OK, but we must respect the boundaries * they specified. * * +-- address * | * +--prefetch--+--length--+------------------------------------+ * |************|##########| | * |************|##########| | * |************|##########| | * +------------+----------+------------------------------------+ * | | * +-- start end --+ * * This is the second worst case scenario. We cannot even get the requested minimum ( no matter * the desired prefetch ) before we run out of the specified range. * * +-- address * | * +--------------------------------------------+--prefetch--+--length--+ * | |************|##########| * | |************|##########| * | |************|##########| * +--------------------------------------------+------------+--+-------+ * | | * +-- start end --+ * * This is the worst case scenario. The minimum length moves us off the end of the high range * end and the prefetch before this minimum data request ( remember we are fetching backwards * since we are searching backwards ) runs us off the start of the data. * * +-- address * | * +---+-----------------------------------------------prefetch--+--length--+ * |*************************************************************|##########| * |*************************************************************|##########| * |*************************************************************|##########| * +---+---------------------------------------------------------+--+-------+ * | | * +-- start end --+ * * See if the desired size ( minimum length + desired prefetch ) fits in to the current range. * Without running off the end. */ if ( address.add(len).compareTo(end) > 0 ) { /* * We need to reduce the amount we can ask for to whats left. Also make sure to reduce the * amount to copy, otherwise we will overrun the buffer and generate an exception. */ len = end.subtract(address).add(BigInteger.ONE); length = len.intValue(); } /* * Now determine if the prefetch is going to run backwards past the "start" of where we are allowed * to access the memory. We will normalize the prefetch size so it takes in to account the amount of * data being gathered as part of the length requested portion. This should insure that in the end * we will request the prefetch amount of data unless there is not enough to service this request. */ if ( len.compareTo(prefetchSize) > 0 ) { prefetchSize = BigInteger.ZERO; } else { prefetchSize = prefetchSize.subtract(len); } if ( address.subtract(prefetchSize).compareTo(start) < 0) { /* * Just get what we can from the beginning up to the current required address. */ prefetchSize = address.subtract(start); fetchAddress = start; } else { /* * It fits so just start reading from the calculated position prior to the requested point. */ fetchAddress = address.subtract(prefetchSize); } fetchSize = len.add(prefetchSize); } /* * OK, we have determined where to start reading the data and how much. Just get the data * and store it in the cache. */ MemoryByte bytes[] = fMemoryBlock.getBytesFromAddress(fetchAddress, fetchSize.longValue()); cache.memoryCacheStartAddress = fetchAddress; cache.memoryCacheData = bytes; } /* * Either it was already cached or just has been, either way we have the data so copy what we can * back to the user buffer. */ MemoryByte bytes[] = new MemoryByte[length]; System.arraycopy(cache.memoryCacheData, address.subtract(cache.memoryCacheStartAddress).intValue(), bytes, 0, length); return bytes; } private BigInteger parseHexBigInteger(String s) { if(s.toUpperCase().startsWith("0X")) //$NON-NLS-1$ return new BigInteger(s.substring(2), 16); else return new BigInteger(s, 16); } protected void performFindNext() { try { BigInteger start = parseHexBigInteger(fProperties.getProperty(SEARCH_LAST_START)); BigInteger end = parseHexBigInteger(fProperties.getProperty(SEARCH_LAST_END)); boolean searchForward = Boolean.parseBoolean(fProperties.getProperty(SEARCH_FORMAT_FORWARD, Boolean.FALSE.toString())); boolean caseInSensitive = Boolean.parseBoolean(fProperties.getProperty(SEARCH_FORMAT_CASEINSENSTIVE, Boolean.FALSE.toString())); SearchPhrase phrase = null; String findText = fProperties.getProperty(SEARCH_FIND); if(fProperties.getProperty(SEARCH_FORMAT).equals(SEARCH_FORMAT_ASCII)) phrase = new AsciiSearchPhrase(findText, caseInSensitive); else if(fProperties.getProperty(SEARCH_FORMAT).equals(SEARCH_FORMAT_HEX)) phrase = new BigIntegerSearchPhrase(new BigInteger(findText.toUpperCase().startsWith("0X") ? findText.substring(2) : findText, 16), 16); //$NON-NLS-1$ else if(fProperties.getProperty(SEARCH_FORMAT).equals(SEARCH_FORMAT_OCTAL)) phrase = new BigIntegerSearchPhrase(new BigInteger(findText.startsWith("0") ? findText.substring(1) : findText, 8), 8); //$NON-NLS-1$ else if(fProperties.getProperty(SEARCH_FORMAT).equals(SEARCH_FORMAT_BINARY)) phrase = new BigIntegerSearchPhrase(new BigInteger(findText.toUpperCase().startsWith("0B") ? findText.substring(2) : findText, 2), 2); //$NON-NLS-1$ else if(fProperties.getProperty(SEARCH_FORMAT).equals(SEARCH_FORMAT_DECIMAL)) phrase = new BigIntegerSearchPhrase(new BigInteger(findText, 10), 10); else if(fProperties.getProperty(SEARCH_FORMAT).equals(SEARCH_FORMAT_BYTESEQUENCE)) phrase = new ByteSequenceSearchPhrase(findText); performFind(start, end, phrase, searchForward, null, false, false); } catch(Exception e) { MemorySearchPlugin.logError(Messages.getString("FindReplaceDialog.MemorySearchFailure"), e); //$NON-NLS-1$ } } private void performFind(final BigInteger start, final BigInteger end, final SearchPhrase searchPhrase, final boolean searchForward, final byte[] replaceData, final boolean all, final boolean replaceThenFind) { final ISearchQuery query = new IMemorySearchQuery() { private ISearchResult fSearchResult = null; public boolean canRerun() { return false; } public boolean canRunInBackground() { return true; } public String getLabel() { return Messages.getString("FindReplaceDialog.SearchingMemoryFor") + searchPhrase; //$NON-NLS-1$ } public ISearchResult getSearchResult() { if(fSearchResult == null) fSearchResult = new MemorySearchResult(this, Messages.getString("FindReplaceDialog.SearchingMemoryFor") + searchPhrase); //$NON-NLS-1$ return fSearchResult; } public IStatus run(IProgressMonitor monitor) throws OperationCanceledException { final BigInteger searchPhraseLength = BigInteger.valueOf(searchPhrase.getByteLength()); BigInteger range = end.subtract(start).add(BigInteger.ONE); BigInteger currentPosition = searchForward ? start : end.subtract(searchPhraseLength); if ( searchPhraseLength.compareTo(range) >= 0 ) { return Status.OK_STATUS; } boolean isReplace = replaceData != null; BigInteger jobs = range; BigInteger factor = BigInteger.ONE; if(jobs.compareTo(BigInteger.valueOf(0x07FFFFFF)) > 0) { factor = jobs.divide(BigInteger.valueOf(0x07FFFFFF)); jobs = jobs.divide(factor); } BigInteger jobCount = BigInteger.ZERO; BigInteger replaceCount = BigInteger.ZERO; FindReplaceMemoryCache cache = new FindReplaceMemoryCache(); monitor.beginTask(Messages.getString("FindReplaceDialog.SearchingMemoryFor") + searchPhrase, jobs.intValue()); //$NON-NLS-1$ boolean matched = false; while(((searchForward && currentPosition.compareTo(end.subtract(searchPhraseLength)) < 0) || (!searchForward && currentPosition.compareTo(start) > 0)) && !monitor.isCanceled()) { try { MemoryByte bytes[] = getSearchableBytes(start, end, searchForward, currentPosition, searchPhraseLength.intValue(), cache); matched = searchPhrase.isMatch(bytes); if(matched) { if(all && !isReplace) ((MemorySearchResult) getSearchResult()).addMatch(new MemoryMatch(currentPosition, searchPhraseLength)); if(isReplace) { try { fMemoryBlock.setValue(currentPosition.subtract(fMemoryBlock.getBigBaseAddress()), replaceData); } catch(DebugException de) { MemorySearchPlugin.logError(Messages.getString("FindReplaceDialog.MemoryReadFailed"), de); //$NON-NLS-1$ } replaceCount = replaceCount.add(BigInteger.ONE); } if(isReplace && replaceThenFind && replaceCount.compareTo(BigInteger.ONE) == 0) { isReplace = false; matched = false; } if(matched && !all) { final BigInteger finalCurrentPosition = currentPosition; final BigInteger finalStart = start ; final BigInteger finalEnd = end; Display.getDefault().asyncExec(new Runnable(){ public void run() { IMemoryRenderingContainer containers[] = getMemoryView().getMemoryRenderingContainers(); for(int i = 0; i < containers.length; i++) { IMemoryRendering rendering = containers[i].getActiveRendering(); if(rendering instanceof IRepositionableMemoryRendering) { try { ((IRepositionableMemoryRendering) rendering).goToAddress(finalCurrentPosition); } catch (DebugException e) { MemorySearchPlugin.logError(Messages.getString("FindReplaceDialog.RepositioningMemoryViewFailed"), e); //$NON-NLS-1$ } } if(rendering != null) { // Temporary, until platform accepts/adds new interface for setting the selection try { Method m = rendering.getClass().getMethod("setSelection", new Class[] { BigInteger.class, BigInteger.class } ); //$NON-NLS-1$ if(m != null) m.invoke(rendering, finalCurrentPosition, finalCurrentPosition.add(searchPhraseLength)); } catch (Exception e) { // do nothing } } } } }); fProperties.setProperty(SEARCH_ENABLE_FIND_NEXT, Boolean.TRUE.toString()); if ( searchForward ) { BigInteger newFinalStart = finalCurrentPosition.add(BigInteger.ONE); fProperties.setProperty(SEARCH_LAST_START, "0x" + newFinalStart.toString(16)); //$NON-NLS-1$ fProperties.setProperty(SEARCH_LAST_END, "0x" + finalEnd.toString(16)); //$NON-NLS-1$ } else { BigInteger newFinalEnd = finalCurrentPosition.subtract(BigInteger.ONE); fProperties.setProperty(SEARCH_LAST_START, "0x" + finalStart.toString(16)); //$NON-NLS-1$ fProperties.setProperty(SEARCH_LAST_END, "0x" + newFinalEnd.toString(16)); //$NON-NLS-1$ } if ( fFindAction != null ) { fFindAction.setEnabled(true); } return Status.OK_STATUS; } } matched = false; if(searchForward) currentPosition = currentPosition.add(BigInteger.ONE); else currentPosition = currentPosition.subtract(BigInteger.ONE); } catch(DebugException e) { MemorySearchPlugin.logError(Messages.getString("FindReplaceDialog.MemorySearchFailure"), e); //$NON-NLS-1$ return Status.CANCEL_STATUS; } jobCount = jobCount.add(BigInteger.ONE); if(jobCount.compareTo(factor) == 0) { jobCount = BigInteger.ZERO; monitor.worked(1); } } if(monitor.isCanceled()) return Status.CANCEL_STATUS; return Status.OK_STATUS; } public IMemoryRenderingSite getMemoryView() { return fMemoryView; } }; if(all && replaceData == null) { Display.getDefault().asyncExec(new Runnable() { public void run() { NewSearchUI.activateSearchResultView(); NewSearchUI.runQueryInBackground(query); } }); } else { Job job = new Job("Searching memory for " + searchPhrase){ //$NON-NLS-1$ public IStatus run(IProgressMonitor monitor) { return query.run(monitor); } }; job.schedule(); } } interface SearchPhrase { boolean isMatch(MemoryByte[] bytes); int getByteLength(); String toString(); } class AsciiSearchPhrase implements SearchPhrase { private String fPhrase; private boolean fIsCaseInsensitive; public AsciiSearchPhrase(String phrase, boolean isCaseInsensitive) { fPhrase = phrase; fIsCaseInsensitive = isCaseInsensitive; } public int getByteLength() { return fPhrase.length(); } public String toString() { return fPhrase; } public boolean isMatch(MemoryByte[] bytes) { byte[] targetBytes = new byte[bytes.length]; for(int i = 0; i < bytes.length; i++) targetBytes[i] = bytes[i].getValue(); String searchString = fPhrase; String targetString = new String(targetBytes); if(fIsCaseInsensitive) { searchString = searchString.toUpperCase(); targetString = targetString.toUpperCase(); } return searchString.equals(targetString); } } class ByteSequenceSearchPhrase implements SearchPhrase { private byte[] fBytes = null; public ByteSequenceSearchPhrase(String phrase) { fBytes = parseByteSequence(phrase); } public int getByteLength() { if ( fBytes != null ) { return fBytes.length; } else { return 0; } } public String toString() { if(fBytes == null) return ""; //$NON-NLS-1$ StringBuffer buf = new StringBuffer(); for(int i = 0; i < fBytes.length; i++) buf.append(BigInteger.valueOf(fBytes[i]).toString(16) + " "); //$NON-NLS-1$ return buf.toString(); } public boolean isMatch(MemoryByte[] bytes) { if ( fBytes == null ) return false; for(int i = 0; i < bytes.length; i++) if(bytes[i].getValue() != fBytes[i]) return false; return true; } } class BigIntegerSearchPhrase implements SearchPhrase { private BigInteger fPhrase; private int fRadix; public BigIntegerSearchPhrase(BigInteger phrase, int radix) { fPhrase = phrase; fRadix = radix; } public int getByteLength() { return removeZeroPrefixByte(fPhrase.toByteArray()).length; } public String toString() { return fPhrase.toString(fRadix); } public boolean isMatch(MemoryByte[] bytes) { byte[] targetBytes = new byte[bytes.length + 1]; targetBytes[0] = 0; for(int i = 0; i < bytes.length; i++) targetBytes[i + 1] = bytes[i].getValue(); // TODO endian? BigInteger targetBigInteger = new BigInteger(targetBytes); return fPhrase.equals(targetBigInteger); } } private byte[] removeZeroPrefixByte(byte[] bytes) { if(bytes[0] != 0 || bytes.length == 1) return bytes; byte[] processedBytes = new byte[bytes.length - 1]; System.arraycopy(bytes, 1, processedBytes, 0, processedBytes.length); return processedBytes; } interface IMemorySearchQuery extends ISearchQuery { public IMemoryRenderingSite getMemoryView(); }; }