Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/port80/eclipse/jdt/actions/RegexReplaceDialog.java


1   package com.port80.eclipse.jdt.actions;
2   
3   import java.util.ArrayList;
4   import java.util.List;
5   
6   import org.apache.oro.text.perl.Perl5Util;
7   import org.apache.oro.text.regex.PatternMatcherInput;
8   import org.eclipse.jface.dialogs.Dialog;
9   import org.eclipse.jface.text.BadLocationException;
10  import org.eclipse.jface.text.IDocument;
11  import org.eclipse.jface.text.Position;
12  import org.eclipse.jface.text.TextSelection;
13  import org.eclipse.jface.viewers.ISelection;
14  import org.eclipse.swt.SWT;
15  import org.eclipse.swt.events.DisposeEvent;
16  import org.eclipse.swt.events.DisposeListener;
17  import org.eclipse.swt.events.ModifyEvent;
18  import org.eclipse.swt.events.ModifyListener;
19  import org.eclipse.swt.events.SelectionAdapter;
20  import org.eclipse.swt.events.SelectionEvent;
21  import org.eclipse.swt.events.ShellAdapter;
22  import org.eclipse.swt.events.ShellEvent;
23  import org.eclipse.swt.graphics.Color;
24  import org.eclipse.swt.graphics.Font;
25  import org.eclipse.swt.graphics.FontMetrics;
26  import org.eclipse.swt.graphics.GC;
27  import org.eclipse.swt.graphics.Point;
28  import org.eclipse.swt.layout.GridData;
29  import org.eclipse.swt.widgets.Button;
30  import org.eclipse.swt.widgets.Combo;
31  import org.eclipse.swt.widgets.Composite;
32  import org.eclipse.swt.widgets.Control;
33  import org.eclipse.swt.widgets.Label;
34  import org.eclipse.swt.widgets.Shell;
35  import org.eclipse.ui.texteditor.ITextEditor;
36  
37  import com.port80.eclipse.jdt.JdtPlugin;
38  import com.port80.eclipse.jdt.ThemeManager;
39  import com.port80.eclipse.util.UIUtil;
40  import com.port80.util.struct.IntList;
41  
42  /**
43   * @author chrisl
44   *
45   * To change this generated comment edit the template variable "typecomment":
46   * Window>Preferences>Java>Templates.
47   * To enable and disable the creation of type comments go to
48   * Window>Preferences>Java>Code Generation.
49   */
50  public class RegexReplaceDialog extends Dialog {
51  
52    ////////////////////////////////////////////////////////////////////////
53  
54    private static final String NAME = "RegexReplaceDialog";
55    private static final boolean DEBUG = false;
56    private static final int MAX_PERSISTENT = 8;
57    private static final int WIDTH=32;
58  
59    ////////////////////////////////////////////////////////////////////////
60  
61    private static RegexReplaceDialog fDefault;
62    private static Point fWindowPosition;
63    private static List fPatterns = new ArrayList();
64    private static List fReplaces = new ArrayList();
65    private static Perl5Util fReEngine;
66  
67    private Shell fParent;
68    private ITextEditor fEditor;
69    private IDocument fDoc;
70    PatternMatcherInput fMatchInput;
71    // Start of search region.
72    int fStart;
73    // End of search region.  A 0-length Position is required so that it would be adjusted
74    // when on replacements.
75    Position fEnd;
76    // Current search starting point.
77    int fOffset;
78    // Incremental search anchor, allow incremental search to restart at anchor location
79    // on pattern changes.
80    int fAnchor;
81    int fLastReplaced;
82    IntList fHistory = new IntList();
83    PatternChanged fPatternChangedHandler = new PatternChanged();
84    boolean isWrapped;
85    boolean fReplaceAllRunning;
86    //
87    private Combo fPattern;
88    private Combo fReplace;
89    private Button fButtonReplaceAll;
90    private Button fButtonReplaceClose;
91    private Button fButtonRelaceFind;
92    private Button fButtonFind;
93    private Button fButtonPrev;
94    private Button fButtonNext;
95    private Button fSingleLine;
96    private Button fCaseSensitive;
97    private Button fButtonClose;
98    private Label fStatus;
99  
100   ////////////////////////////////////////////////////////////////////////
101 
102   public static RegexReplaceDialog getDefault(Shell shell, ITextEditor editor) {
103     if (fDefault == null || fDefault.getShell() == null || fDefault.getShell().isDisposed()) {
104       fDefault = new RegexReplaceDialog(shell, editor);
105     } else {
106       if (DEBUG)
107         System.err.println("getDefault()");
108       fDefault.fEditor = editor;
109       fWindowPosition = fDefault.getShell().getLocation();
110     }
111     return fDefault;
112   }
113 
114   /**
115    * Constructor for RegexReplaceDialog.
116    * @param parentShell
117    */
118   private RegexReplaceDialog(Shell parentShell, ITextEditor editor) {
119     super(parentShell);
120     fParent = parentShell;
121     fEditor = editor;
122     //
123     setShellStyle(SWT.DIALOG_TRIM);
124     setBlockOnOpen(false);
125   }
126 
127   ////////////////////////////////////////////////////////////////////////
128 
129   public void dispose() {
130     if (DEBUG)
131       System.err.println("dispose()");
132     if (fEnd != null) {
133       fDoc.removePosition(fEnd);
134       fEnd = null;
135     }
136     fReEngine = null;
137     fDoc = null;
138     //
139     fMatchInput = null;
140     fPattern = null;
141     fReplace = null;
142     fButtonReplaceAll = null;
143     fButtonReplaceClose = null;
144     fButtonRelaceFind = null;
145     fButtonFind = null;
146     fButtonPrev = null;
147     fButtonNext = null;
148     fSingleLine = null;
149     fCaseSensitive = null;
150     fButtonClose = null;
151   }
152 
153   void init(ITextEditor editor) {
154     if (DEBUG)
155       System.err.println("init");
156     fHistory.clear();
157     fButtonPrev.setEnabled(false);
158     isWrapped = false;
159     fEditor = editor;
160     if (fReEngine == null)
161       fReEngine = new Perl5Util();
162     //
163     fDoc = fEditor.getDocumentProvider().getDocument(fEditor.getEditorInput());
164     ISelection s = fEditor.getSelectionProvider().getSelection();
165     int doclength = fDoc.getLength();
166     fStart = 0;
167     if (fEnd != null)
168       fDoc.removePosition(fEnd);
169     else
170       fEnd = new Position(0, 0);
171     fEnd.offset = doclength;
172     fOffset = 0;
173     fAnchor = fOffset;
174     String pat = null;
175     if (s instanceof TextSelection) {
176       TextSelection selection = (TextSelection) s;
177       fStart = selection.getOffset();
178       fEnd.offset = fStart + selection.getLength();
179       fOffset = fStart;
180       fAnchor = fOffset;
181       int sline;
182       int eline;
183       try {
184         sline = fDoc.getLineOfOffset(fStart) + 1;
185         eline = fDoc.getLineOfOffset(fEnd.offset) + 1;
186       } catch (BadLocationException e) {
187         JdtPlugin.log(NAME + ".init()", e);
188         sline = eline = 0;
189       }
190       if (selection.getLength() > 0) {
191         if (sline != eline) {
192           fStatus.setText("Search region: line " + sline + " to " + eline);
193         } else {
194           try {
195             pat = fDoc.get(fStart, fEnd.offset - fStart);
196             savePattern(pat);
197           } catch (BadLocationException e) {
198             JdtPlugin.log(NAME + ".init()", e);
199           }
200           fStart = 0;
201           fEnd.offset = doclength;
202           fStatus.setText("Search region: whole buffer, starting at line " + sline);
203         }
204       } else {
205         fStart = 0;
206         fEnd.offset = doclength;
207         fStatus.setText("Search region: whole buffer, starting at line " + sline);
208       }
209     }
210     fLastReplaced = -1;
211     fMatchInput = new PatternMatcherInput(fDoc.get(), fStart, fEnd.offset - fStart);
212     fMatchInput.setCurrentOffset(fOffset);
213     try {
214       fDoc.addPosition(fEnd);
215     } catch (BadLocationException e) {
216       JdtPlugin.log(NAME + ".init(): fEnd.offset=" + fEnd.offset, e);
217     }
218     if (pat != null) {
219       fPattern.removeModifyListener(fPatternChangedHandler);
220       setEnableFind(false);
221       if (fPatterns.size() > 0) {
222         fPattern.setItems((String[]) fPatterns.toArray(new String[fPatterns.size()]));
223         fPattern.setText((String) fPatterns.get(0));
224         setEnableFind(fPattern.getText().length() > 0);
225       }
226       fPattern.addModifyListener(fPatternChangedHandler);
227     }
228     fEditor.selectAndReveal(fOffset, 0);
229   }
230 
231   ////////////////////////////////////////////////////////////////////////
232 
233   /**
234    * @see org.eclipse.jface.window.Window#constrainShellSize()
235    */
236   protected void constrainShellSize() {
237     Shell shell = getShell();
238     if (fWindowPosition != null) {
239       System.err.println("Last Window location=" + fWindowPosition);
240       shell.setLocation(fWindowPosition);
241     } else {
242       fWindowPosition = shell.getLocation();
243       shell.setLocation(fWindowPosition.x - 400, fWindowPosition.y + 400);
244     }
245     super.constrainShellSize();
246   }
247 
248   /**
249    * @see org.eclipse.jface.window.Window#createContents(Composite)
250    */
251   protected Control createContents(Composite parent) {
252     Composite top = UIUtil.createBox(parent, 1, 1, true, true, 1, 10, 10, 0, 10, null);
253     createDialogArea(top);
254     createButtonBar(top);
255     return top;
256   }
257 
258   protected Control createDialogArea(Composite parent) {
259     ThemeManager manager = JdtPlugin.getThemeManager();
260     Font boldfont = manager.getFont("Bold");
261     Color lightcolor = manager.getColor("LightBG");
262     //
263     String title = JdtPlugin.getString("RegexFindReplace.title");
264     String str_search = JdtPlugin.getString("RegexFindReplace.search") + ": ";
265     String str_replace = JdtPlugin.getString("RegexFindReplace.replace") + ": ";
266     String str_oneline = JdtPlugin.getString("RegexFindReplace.oneline");
267     String str_casesensitive = JdtPlugin.getString("RegexFindReplace.case.sensitive");
268     String str_replaceall = JdtPlugin.getString("RegexFindReplace.replace.all");
269     String str_replacefind = JdtPlugin.getString("RegexFindReplace.replace.find");
270     String str_replaceclose = JdtPlugin.getString("RegexFindReplace.replace.close");
271     String str_find = JdtPlugin.getString("RegexFindReplace.find");
272     //
273     getShell().setText(title);
274     Composite top = UIUtil.createBox(parent, 1, 1, true, true, 3, 0, 0, 5, 5, true, null);
275     UIUtil.createGridLabel(
276       top,
277       SWT.RIGHT,
278       str_search,
279       null,
280       null,
281       boldfont,
282       1,
283       1,
284       GridData.FILL,
285       GridData.CENTER,
286       false,
287       false);
288     fPattern = new Combo(top, SWT.FLAT);
289     fPattern.setLayoutData(UIUtil.fillCell(1, 2, true, false));
290     GC gc = new GC(fPattern);
291     gc.setFont(fPattern.getFont());
292     FontMetrics fm = gc.getFontMetrics();
293     ((GridData) fPattern.getLayoutData()).widthHint = fm.getAverageCharWidth() * WIDTH;
294     fPattern.setBackground(lightcolor);
295     UIUtil.createGridLabel(
296       top,
297       SWT.RIGHT,
298       str_replace,
299       null,
300       null,
301       boldfont,
302       1,
303       1,
304       GridData.FILL,
305       GridData.CENTER,
306       false,
307       false);
308     fReplace = new Combo(top, SWT.FLAT);
309     fReplace.setLayoutData(UIUtil.fillCell(1, 2, true, false));
310     ((GridData) fReplace.getLayoutData()).widthHint = fm.getAverageCharWidth() * WIDTH;
311     fReplace.setBackground(lightcolor);
312     //
313     //For GTK
314     Composite options = UIUtil.createBox(top, 1, 3, true, false, 8, 0, 0, 5, 5, true, null);
315     // For Motif
316     // Composite options = UIUtil.createBox(top, 1, 3, true, false, 2, 0, 0, 0, 0, true, null);
317     fSingleLine = UIUtil.createButton(options, SWT.CHECK, str_oneline, null, null);
318     fSingleLine.setLayoutData(UIUtil.alignCell(1, 4, GridData.END, GridData.CENTER, false, false));
319     fCaseSensitive = UIUtil.createButton(options, SWT.CHECK, str_casesensitive, null, null);
320     fCaseSensitive.setLayoutData(UIUtil.alignCell(1, 4, GridData.END, GridData.CENTER, false, false));
321     //Composite buttons = UIUtil.createBox(top, 1, 4, true, false, 2, 0, 0, 0, 0, true, null);
322     fStatus = new Label(options, SWT.LEFT);
323     fStatus.setLayoutData(UIUtil.fillCell(2, 8, false, true));
324     fStatus.setForeground(manager.getColor("Red"));
325     ((GridData) fStatus.getLayoutData()).heightHint = fm.getHeight() * 2;
326     gc.dispose();
327     //
328     fButtonReplaceAll =
329       UIUtil.createButton(options, SWT.NONE, str_replaceall, lightcolor, null, 1, 4, true, false);
330     fButtonReplaceAll.addSelectionListener(new SelectionAdapter() {
331       public void widgetSelected(SelectionEvent e) {
332         actionReplaceAll();
333       }
334     });
335     fButtonRelaceFind =
336       UIUtil.createButton(options, SWT.NONE, str_replacefind, lightcolor, null, 1, 4, true, false);
337     fButtonRelaceFind.addSelectionListener(new SelectionAdapter() {
338       public void widgetSelected(SelectionEvent e) {
339         actionReplaceFind();
340       }
341 
342     });
343     //
344     fButtonReplaceClose =
345       UIUtil.createButton(options, SWT.NONE, str_replaceclose, lightcolor, null, 1, 4, true, false);
346     fButtonReplaceClose.addSelectionListener(new SelectionAdapter() {
347       public void widgetSelected(SelectionEvent e) {
348         actionReplaceClose();
349       }
350     });
351     fButtonFind = UIUtil.createButton(options, SWT.NONE, str_find, lightcolor, null, 1, 3, true, false);
352     fButtonFind.addSelectionListener(new SelectionAdapter() {
353       public void widgetSelected(SelectionEvent e) {
354         actionFind();
355       }
356 
357     });
358     fButtonPrev = UIUtil.createButton(options, SWT.NONE, "", lightcolor, null, 1, 1, true, false);
359     fButtonPrev.setToolTipText("Previous in history");
360     fButtonPrev.setImage(manager.getImage("UpArrow"));
361     fButtonPrev.setEnabled(false);
362     fButtonPrev.addSelectionListener(new SelectionAdapter() {
363       public void widgetSelected(SelectionEvent e) {
364         actionHistoryPrev();
365       }
366     });
367     //
368     fHistory.clear();
369     // init(fEditor);
370     //
371     setEnableFind(false);
372     if (fPatterns.size() > 0) {
373       fPattern.setItems((String[]) fPatterns.toArray(new String[fPatterns.size()]));
374       fPattern.setText((String) fPatterns.get(0));
375       setEnableFind(fPattern.getText().length() > 0);
376     }
377     setEnableReplace(false);
378     if (fReplaces.size() > 0) {
379       fReplace.setItems((String[]) fReplaces.toArray(new String[fReplaces.size()]));
380       fReplace.setText((String) fReplaces.get(0));
381     }
382     getShell().addDisposeListener(new DisposeListener() {
383       public void widgetDisposed(DisposeEvent e) {
384         dispose();
385       }
386     });
387     fPattern.addModifyListener(fPatternChangedHandler);
388     getShell().setDefaultButton(fButtonFind);
389     fPattern.setFocus();
390     //
391     getShell().addShellListener(new ShellAdapter() {
392       public void shellActivated(ShellEvent e) {
393         super.shellActivated(e);
394         init(getEditor());
395       }
396       public void shellDeactivated(ShellEvent e) {
397         super.shellDeactivated(e);
398         setEnableReplace(false);
399         setEnablePrev(false);
400       }
401     });
402     return top;
403   }
404 
405   protected Control createButtonBar(Composite parent) {
406     Composite top = UIUtil.createBox(parent, 1, 1, 1, 0, 0, null);
407     fButtonClose = UIUtil.createButton(top, SWT.CENTER, "Close", null, null, 1, 1, true, false);
408     fButtonClose.addSelectionListener(new SelectionAdapter() {
409       public void widgetSelected(SelectionEvent e) {
410         actionClose();
411       }
412     });
413     return top;
414   }
415 
416   public boolean close() {
417     if (DEBUG)
418       System.err.println("close()");
419     fWindowPosition = getShell().getLocation();
420     savePattern(fPattern.getText());
421     saveReplace(fReplace.getText());
422     fReEngine = null;
423     return super.close();
424   }
425 
426   ////////////////////////////////////////////////////////////////////////
427 
428   ITextEditor getEditor() {
429     return fEditor;
430   }
431 
432   void setEnableFind(boolean enable) {
433     fButtonFind.setEnabled(enable);
434   }
435 
436   void setEnableReplace(boolean enable) {
437     fButtonReplaceAll.setEnabled(enable);
438     fButtonReplaceClose.setEnabled(enable);
439     fButtonRelaceFind.setEnabled(enable);
440   }
441 
442   void setEnablePrev(boolean b) {
443     fButtonPrev.setEnabled(b);
444   }
445 
446   ////////////////////////////////////////////////////////////////////////
447 
448   boolean find(String pat, String opt) {
449     if (isWrapped) {
450       fHistory.clear();
451       fButtonPrev.setEnabled(false);
452       isWrapped = false;
453     }
454     pat = Util.escapeRegex(pat);
455     fStatus.setText("");
456     try {
457       if (fReEngine.match("/" + pat + opt, fMatchInput)) {
458         //
459         fOffset = fReEngine.getMatch().beginOffset(0);
460         fAnchor = fOffset;
461         int len = fReEngine.getMatch().endOffset(0) - fOffset;
462         fEditor.selectAndReveal(fOffset, len);
463         setEnableReplace(true);
464         appendHistory(fOffset, len);
465         return true;
466       }
467     } catch (Exception e) {
468       fStatus.setText("ERROR: " + e.getMessage());
469       return false;
470     }
471     return wrap();
472   }
473 
474   /**
475    * A simplified version of find() for actionReplaceAll to avoid accessing UI, eg. fStatus.
476    * 
477    * @param pat
478    * @param opt
479    * @return boolean
480    */
481   boolean find1(String pat, String opt) {
482     pat = Util.escapeRegex(pat);
483     try {
484       if (fReEngine.match("/" + pat + opt, fMatchInput)) {
485         //
486         fOffset = fReEngine.getMatch().beginOffset(0);
487         fAnchor = fOffset;
488         return true;
489       }
490     } catch (Exception e) {
491       return false;
492     }
493     return false;
494   }
495 
496   boolean replace(String pat, String replace, String opt, StringBuffer buf) {
497     String input = fReEngine.getMatch().group(0);
498     int start = fOffset;
499     int len = input.length();
500     String ret;
501     boolean noadjust = pat.startsWith("^");
502     pat = fixPatternForReplace(pat);
503     replace = escapeReplaceString(replace);
504     ret = fReEngine.substitute("s/" + pat + "/" + replace + opt, input);
505     if (buf == null) {
506       try {
507         fDoc.replace(start, len, ret);
508       } catch (BadLocationException e) {
509         fEditor.selectAndReveal(fOffset, 0);
510         setEnableReplace(false);
511         return false;
512       }
513     } else
514       buf.replace(start, start + len, ret);
515     len = ret.length();
516     fOffset = start + len;
517     if (!noadjust)
518       fOffset = adjustOffset(pat, buf);
519     fAnchor = fOffset;
520     return true;
521   }
522 
523   private void appendHistory(int offset, int len) {
524     int size = fHistory.size();
525     if (size < 2 || fHistory.get(size - 2) != offset) {
526       fHistory.add(offset);
527       fHistory.add(len);
528     } else if (fHistory.get(size - 2) == offset) {
529       fHistory.set(size - 1, len);
530     }
531     fButtonPrev.setEnabled(fHistory.size() > 2);
532   }
533 
534   ////////////////////////////////////////////////////////////////////////
535 
536   void actionClose() {
537     if (fReplaceAllRunning) {
538       fReplaceAllRunning = false;
539       return;
540     }
541     if (fLastReplaced >= 0)
542       fEditor.selectAndReveal(fLastReplaced, 0);
543     okPressed();
544   }
545 
546   void actionFind() {
547     String pat = fPattern.getText();
548     savePattern(pat);
549     saveReplace(fReplace.getText());
550     if (DEBUG)
551       System.err.println(
552         NAME
553           + ".actionFind(): pattern="
554           + fPattern.getText()
555           + ", replace="
556           + fReplace.getText());
557     if (!find(pat, getSearchOptions())) {
558       getShell().getDisplay().beep();
559     }
560   }
561 
562   void actionReplaceFind() {
563     String pat = fPattern.getText();
564     String replace = fReplace.getText();
565     savePattern(pat);
566     saveReplace(replace);
567     if (DEBUG)
568       System.err.println(
569         NAME
570           + ".actionReplaceFind(): pattern="
571           + fPattern.getText()
572           + ", replace="
573           + fReplace.getText());
574     if (!replace(pat, replace, getSearchOptions(), null)) {
575       getShell().getDisplay().beep();
576       return;
577     }
578     fLastReplaced = fOffset;
579     fMatchInput.setInput(fDoc.get(), fStart, fEnd.offset - fStart);
580     fMatchInput.setCurrentOffset(fOffset);
581     int len = fHistory.size();
582     if (len > 0) {
583       fHistory.set(len - 2, fOffset);
584       fHistory.set(len - 1, -1);
585     }
586     if (!find(pat, getSearchOptions())) {
587       getShell().getDisplay().beep();
588       setEnableReplace(false);
589     } else {
590       fButtonPrev.setEnabled(fHistory.size() > 2);
591       setEnableReplace(fHistory.size() > 0 && fHistory.getLast() >= 0);
592     }
593   }
594 
595   void actionReplaceClose() {
596     String pat = fPattern.getText();
597     String replace = fReplace.getText();
598     savePattern(pat);
599     saveReplace(replace);
600     if (DEBUG)
601       System.err.println(
602         NAME
603           + ".actionReplaceClose(): pattern="
604           + fPattern.getText()
605           + ", replace="
606           + fReplace.getText());
607     if (!replace(pat, replace, getSearchOptions(), null)) {
608       getShell().getDisplay().beep();
609       return;
610     }
611     fEditor.selectAndReveal(fOffset, 0);
612     fLastReplaced = -1;
613     actionClose();
614   }
615 
616   void actionReplaceAll() {
617     StringBuffer b;
618     try {
619       b = new StringBuffer(fDoc.get(0, fEnd.offset));
620     } catch (BadLocationException e) {
621       JdtPlugin.log("Bad location: #1", e);
622       return;
623     }
624     final StringBuffer buf = b;
625     final int[] offset = new int[] { fOffset };
626     final int[] replaced = new int[] { 0 };
627     final String pat = fPattern.getText();
628     final String replace = fReplace.getText();
629     final String opt = getSearchOptions();
630     savePattern(pat);
631     saveReplace(replace);
632     if (DEBUG)
633       System.err.println(
634         NAME
635           + ".actionReplaceAll(): pattern="
636           + fPattern.getText()
637           + ", replace="
638           + fReplace.getText());
639     //    try {
640     //      new ProgressMonitorDialog(getShell()).run(true, true, new IRunnableWithProgress() {
641     //        public void run(IProgressMonitor monitor)
642     //          throws InvocationTargetException, InterruptedException {
643     //          int total = 100;
644     //          monitor.beginTask("Replace all ...", total);
645     Thread t = new Thread(new Runnable() {
646       public void run() {
647         int len;
648         do {
649           replace(pat, replace, opt, buf);
650           len = buf.length() - fStart;
651           fMatchInput.setInput(buf.toString(), fStart, len);
652           offset[0] = fOffset;
653           ++replaced[0];
654           fMatchInput.setCurrentOffset(fOffset);
655           //            monitor.worked(total * (fOffset - fStart) / len);
656         } while (find1(pat, opt) && fOffset < buf.length() && fReplaceAllRunning);
657         getShell().getDisplay().asyncExec(new Runnable() {
658           public void run() {
659             replaceAllDone(buf, offset, replaced);
660           }
661         });
662       }
663     });
664     fReplaceAllRunning = true;
665     fButtonClose.setText("Stop replace ...");
666     t.start();
667     //          monitor.done();
668     //        }
669     //      });
670     //    } catch (InvocationTargetException e) {
671     //      JdtPlugin.log("Replace failed", e);
672     //      getShell().getDisplay().beep();
673     //    } catch (InterruptedException e) {
674     //      JdtPlugin.log("Replace aborted", e);
675     //      getShell().getDisplay().beep();
676     //    }
677   }
678 
679   void replaceAllDone(final StringBuffer buf, final int[] offset, final int[] replaced) {
680     fReplaceAllRunning = false;
681     try {
682       fDoc.replace(fStart, fEnd.offset - fStart, buf.substring(fStart, buf.length()));
683     } catch (BadLocationException e) {
684       JdtPlugin.log("Bad location: #2", e);
685       getShell().getDisplay().beep();
686       return;
687     }
688     fEditor.selectAndReveal(offset[0], 0);
689     fStatus.setText("" + replaced[0] + " replaced.");
690     fButtonClose.setText("Close");
691     fLastReplaced = -1;
692     fHistory.clear();
693     wrap();
694     setEnableReplace(false);
695     //NOTE:
696     // Since the RegexReplaceDialog is deactivated when the progress dialog popup 
697     // and activated when the progress dialog close.  Thus input range would be reset to
698     // the whole buffer after each replaceall action.
699   }
700 
701   void actionHistoryPrev() {
702     if (fHistory.size() < 4) {
703       getShell().getDisplay().beep();
704       return;
705     }
706     fHistory.decreaseLength(2);
707     int size = fHistory.size();
708     int offset = fHistory.get(size - 2);
709     int len = fHistory.get(size - 1);
710     setEnableReplace(len >= 0);
711     if (len < 0)
712       len = 0;
713     fButtonPrev.setEnabled(size > 2);
714     fEditor.selectAndReveal(offset, len);
715     fOffset = offset;
716     fAnchor = offset;
717     isWrapped = false;
718     fMatchInput.setCurrentOffset(offset + len);
719   }
720 
721   String getSearchOptions() {
722     String opt = "/";
723     if (!fCaseSensitive.getSelection())
724       opt += 'i';
725     if (fSingleLine.getSelection())
726       opt += 's';
727     else
728       opt += 'm';
729     return opt;
730   }
731 
732   void patternChanged(ModifyEvent event) {
733     String pat = fPattern.getText();
734     setEnableFind(pat.length() > 0);
735     fMatchInput.setCurrentOffset(fAnchor);
736     fStatus.setText("");
737     try {
738       if (pat.length() > 0 && fReEngine.match("/" + pat + getSearchOptions(), fMatchInput)) {
739         //
740         fOffset = fReEngine.getMatch().beginOffset(0);
741         int len = fReEngine.getMatch().endOffset(0) - fOffset;
742         fEditor.selectAndReveal(fOffset, len);
743         setEnableReplace(true);
744         appendHistory(fOffset, len);
745         return;
746       }
747     } catch (Exception e) {
748       fStatus.setText("ERROR: " + e.getMessage());
749     }
750     setEnableReplace(false);
751   }
752 
753   ////////////////////////////////////////////////////////////////////////
754 
755   private void savePattern(String pat) {
756     if (fPatterns.size() > 0 && pat.equals(fPatterns.get(0)))
757       return;
758     if (fPatterns.size() >= MAX_PERSISTENT)
759       fPatterns.remove(fPatterns.size() - 1);
760     fPatterns.add(0, pat);
761   }
762 
763   private void saveReplace(String replace) {
764     if (fReplaces.size() > 0 && replace.equals(fReplaces.get(0)))
765       return;
766     if (fReplaces.size() >= MAX_PERSISTENT)
767       fReplaces.remove(fReplaces.size() - 1);
768     fReplaces.add(0, replace);
769   }
770 
771   /**
772    * @return true if there is a valid match in history, else false.
773    */
774   private boolean wrap() {
775     boolean ret = false;
776     isWrapped = true;
777     int len = fHistory.size();
778     if (len > 0) {
779       int offset = fHistory.get(len - 2);
780       len = fHistory.get(len - 1);
781       if (len < 0)
782         len = 0;
783       fEditor.selectAndReveal(offset, len);
784       getShell().getDisplay().beep();
785       ret = true;
786     }
787     fMatchInput.setCurrentOffset(fStart);
788     return ret;
789   }
790 
791   /** 
792    * Adjust fOffset after replace.  If 'pat' ends with '$' and current offset is at end of line,
793    * advance fOffset to next line.
794    *
795    * @param pat
796    * @return
797    */
798   int adjustOffset(String pat, StringBuffer buf) {
799     int start = fOffset;
800     if (pat.endsWith("$")) {
801       int end = (buf == null) ? fEnd.offset : buf.length();
802       char c;
803       while (start < end) {
804         if (buf == null) {
805           try {
806             c = fDoc.getChar(start);
807           } catch (BadLocationException e) {
808             e.printStackTrace();
809             return end;
810           }
811         } else
812           c = buf.charAt(start);
813         if (c == '\r' || c == '\n')
814           ++start;
815         else
816           break;
817       }
818     }
819     return start;
820   }
821 
822   /**
823    * Since we perfrom replace on the matched string of the previous find action,
824    * the leading look behind and trailing look ahead are not included in the matched string.
825    * Have to adjust the pattern for that.  Since the pattern is already matched, the
826    * zero-width pattern is not actually required, we just remove them from the pattern.
827    * 
828    * NOTE: As of jarkata-oro-2.0.6, it do not support look behind yet.
829    * 
830    * @param s The original search pattern.
831    * @return String The trimmed search pattern.
832    */
833   private String fixPatternForReplace(String s) {
834     StringBuffer ret = new StringBuffer();
835     if (s.length() > 0 && s.charAt(0) != '^')
836       ret.append('^');
837     char c, cc;
838     for (int i = 0; i < s.length(); ++i) {
839       c = s.charAt(i);
840       if (c == '\\') {
841         ret.append(c);
842         c = s.charAt(++i);
843       } else if (c == '/') {
844         ret.append('\\');
845       } else if (c == '(') {
846         cc = s.charAt(i + 1);
847         if (cc == '?') {
848           cc = s.charAt(i + 2);
849           if (cc == '=' || cc == '!' || cc == '<') {
850             i = skipTill(')', s, i);
851             continue;
852           }
853         }
854       }
855       ret.append(c);
856     }
857     return ret.toString();
858   }
859 
860   private int skipTill(char delim, String s, int start) {
861     char c;
862     for (int i = start; i < s.length(); ++i) {
863       c = s.charAt(i);
864       if (c == '\\')
865         ++i;
866       else if (c == delim)
867         return i;
868     }
869     return s.length();
870   }
871 
872   private String escapeReplaceString(String s) {
873     if (s == null)
874       return null;
875     StringBuffer ret = new StringBuffer();
876     char c;
877     for (int i = 0, len = s.length(); i < len; ++i) {
878       c = s.charAt(i);
879       if (c == '/')
880         ret.append("\\/");
881       else if (c == '\\' && i + 1 < len) {
882         c = s.charAt(i + 1);
883         if (c == 't') {
884           ret.append('\t');
885           ++i;
886         } else if (c == 'n') {
887           ret.append('\n');
888           ++i;
889         } else if (c == 'r') {
890           ret.append('\r');
891           ++i;
892         } else if (c == 'f') {
893           ret.append('\f');
894           ++i;
895         } else if (c == '\\') {
896           ret.append('\\');
897           ++i;
898         } else
899           ret.append('\\');
900       } else
901         ret.append(c);
902     }
903     return ret.toString();
904   }
905 
906   ////////////////////////////////////////////////////////////////////////
907 
908   private class PatternChanged implements ModifyListener {
909     public void modifyText(ModifyEvent event) {
910       patternChanged(event);
911     }
912   }
913 
914   ////////////////////////////////////////////////////////////////////////
915 
916 }