/jEdit/tags/jedit-4-0-pre2/org/gjt/sp/jedit/search/SearchAndReplace.java

# · Java · 908 lines · 547 code · 117 blank · 244 comment · 111 complexity · 9c1669def35fe564b9a859ba7217d757 MD5 · raw file

  1. /*
  2. * SearchAndReplace.java - Search and replace
  3. * :tabSize=8:indentSize=8:noTabs=false:
  4. * :folding=explicit:collapseFolds=1:
  5. *
  6. * Copyright (C) 1999, 2000, 2001 Slava Pestov
  7. * Portions copyright (C) 2001 Tom Locke
  8. *
  9. * This program is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU General Public License
  11. * as published by the Free Software Foundation; either version 2
  12. * of the License, or any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with this program; if not, write to the Free Software
  21. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  22. */
  23. package org.gjt.sp.jedit.search;
  24. //{{{ Imports
  25. import javax.swing.text.Segment;
  26. import javax.swing.JOptionPane;
  27. import java.awt.Component;
  28. import org.gjt.sp.jedit.io.VFSManager;
  29. import org.gjt.sp.jedit.msg.SearchSettingsChanged;
  30. import org.gjt.sp.jedit.textarea.*;
  31. import org.gjt.sp.jedit.*;
  32. import org.gjt.sp.util.Log;
  33. //}}}
  34. /**
  35. * Class that implements regular expression and literal search within
  36. * jEdit buffers.
  37. * @author Slava Pestov
  38. * @version $Id: SearchAndReplace.java 3917 2001-11-25 03:42:16Z spestov $
  39. */
  40. public class SearchAndReplace
  41. {
  42. //{{{ Getters and setters
  43. //{{{ setSearchString() method
  44. /**
  45. * Sets the current search string.
  46. * @param search The new search string
  47. */
  48. public static void setSearchString(String search)
  49. {
  50. if(search.equals(SearchAndReplace.search))
  51. return;
  52. SearchAndReplace.search = search;
  53. matcher = null;
  54. EditBus.send(new SearchSettingsChanged(null));
  55. } //}}}
  56. //{{{ getSearchString() method
  57. /**
  58. * Returns the current search string.
  59. */
  60. public static String getSearchString()
  61. {
  62. return search;
  63. } //}}}
  64. //{{{ setReplaceString() method
  65. /**
  66. * Sets the current replacement string.
  67. * @param search The new replacement string
  68. */
  69. public static void setReplaceString(String replace)
  70. {
  71. if(replace.equals(SearchAndReplace.replace))
  72. return;
  73. SearchAndReplace.replace = replace;
  74. matcher = null;
  75. EditBus.send(new SearchSettingsChanged(null));
  76. } //}}}
  77. //{{{ getRepalceString() method
  78. /**
  79. * Returns the current replacement string.
  80. */
  81. public static String getReplaceString()
  82. {
  83. return replace;
  84. } //}}}
  85. //{{{ setIgnoreCase() method
  86. /**
  87. * Sets the ignore case flag.
  88. * @param ignoreCase True if searches should be case insensitive,
  89. * false otherwise
  90. */
  91. public static void setIgnoreCase(boolean ignoreCase)
  92. {
  93. if(ignoreCase == SearchAndReplace.ignoreCase)
  94. return;
  95. SearchAndReplace.ignoreCase = ignoreCase;
  96. matcher = null;
  97. EditBus.send(new SearchSettingsChanged(null));
  98. } //}}}
  99. //{{{ getIgnoreCase() method
  100. /**
  101. * Returns the state of the ignore case flag.
  102. * @return True if searches should be case insensitive,
  103. * false otherwise
  104. */
  105. public static boolean getIgnoreCase()
  106. {
  107. return ignoreCase;
  108. } //}}}
  109. //{{{ setRegexp() method
  110. /**
  111. * Sets the state of the regular expression flag.
  112. * @param regexp True if regular expression searches should be
  113. * performed
  114. */
  115. public static void setRegexp(boolean regexp)
  116. {
  117. if(regexp == SearchAndReplace.regexp)
  118. return;
  119. SearchAndReplace.regexp = regexp;
  120. matcher = null;
  121. EditBus.send(new SearchSettingsChanged(null));
  122. } //}}}
  123. //{{{ getRegexp() method
  124. /**
  125. * Returns the state of the regular expression flag.
  126. * @return True if regular expression searches should be performed
  127. */
  128. public static boolean getRegexp()
  129. {
  130. return regexp;
  131. } //}}}
  132. //{{{ setReverseSearch() method
  133. /**
  134. * Sets the reverse search flag. Note that currently, only literal
  135. * reverse searches are supported.
  136. * @param reverse True if searches should go backwards,
  137. * false otherwise
  138. */
  139. public static void setReverseSearch(boolean reverse)
  140. {
  141. if(reverse == SearchAndReplace.reverse)
  142. return;
  143. SearchAndReplace.reverse = reverse;
  144. matcher = null;
  145. EditBus.send(new SearchSettingsChanged(null));
  146. } //}}}
  147. //{{{ getReverseSearch() method
  148. /**
  149. * Returns the state of the reverse search flag.
  150. * @return True if searches should go backwards,
  151. * false otherwise
  152. */
  153. public static boolean getReverseSearch()
  154. {
  155. return reverse;
  156. } //}}}
  157. //{{{ setBeanShellReplace() method
  158. /**
  159. * Sets the state of the BeanShell replace flag.
  160. * @param regexp True if the replace string is a BeanShell expression
  161. * @since jEdit 3.2pre2
  162. */
  163. public static void setBeanShellReplace(boolean beanshell)
  164. {
  165. if(beanshell == SearchAndReplace.beanshell)
  166. return;
  167. SearchAndReplace.beanshell = beanshell;
  168. matcher = null;
  169. EditBus.send(new SearchSettingsChanged(null));
  170. } //}}}
  171. //{{{ getBeanShellReplace() method
  172. /**
  173. * Returns the state of the BeanShell replace flag.
  174. * @return True if the replace string is a BeanShell expression
  175. * @since jEdit 3.2pre2
  176. */
  177. public static boolean getBeanShellReplace()
  178. {
  179. return beanshell;
  180. } //}}}
  181. //{{{ setAutoWrap() method
  182. /**
  183. * Sets the state of the auto wrap around flag.
  184. * @param wrap If true, the 'continue search from start' dialog
  185. * will not be displayed
  186. * @since jEdit 3.2pre2
  187. */
  188. public static void setAutoWrapAround(boolean wrap)
  189. {
  190. if(wrap == SearchAndReplace.wrap)
  191. return;
  192. SearchAndReplace.wrap = wrap;
  193. EditBus.send(new SearchSettingsChanged(null));
  194. } //}}}
  195. //{{{ getAutoWrap() method
  196. /**
  197. * Returns the state of the auto wrap around flag.
  198. * @param wrap If true, the 'continue search from start' dialog
  199. * will not be displayed
  200. * @since jEdit 3.2pre2
  201. */
  202. public static boolean getAutoWrapAround()
  203. {
  204. return wrap;
  205. } //}}}
  206. //{{{ setSearchMatcher() method
  207. /**
  208. * Sets the current search string matcher. Note that calling
  209. * <code>setSearchString</code>, <code>setReplaceString</code>,
  210. * <code>setIgnoreCase</code> or <code>setRegExp</code> will
  211. * reset the matcher to the default.
  212. */
  213. public static void setSearchMatcher(SearchMatcher matcher)
  214. {
  215. SearchAndReplace.matcher = matcher;
  216. EditBus.send(new SearchSettingsChanged(null));
  217. } //}}}
  218. //{{{ getSearchMatcher() method
  219. /**
  220. * Returns the current search string matcher.
  221. * @exception IllegalArgumentException if regular expression search
  222. * is enabled, the search string or replacement string is invalid
  223. */
  224. public static SearchMatcher getSearchMatcher()
  225. throws Exception
  226. {
  227. return getSearchMatcher(true);
  228. } //}}}
  229. //{{{ getSearchMatcher() method
  230. /**
  231. * Returns the current search string matcher.
  232. * @param reverseOK Replacement commands need a non-reversed matcher,
  233. * so they set this to false
  234. * @exception IllegalArgumentException if regular expression search
  235. * is enabled, the search string or replacement string is invalid
  236. */
  237. public static SearchMatcher getSearchMatcher(boolean reverseOK)
  238. throws Exception
  239. {
  240. reverseOK &= (fileset instanceof CurrentBufferSet);
  241. if(matcher != null && (reverseOK || !reverse))
  242. return matcher;
  243. if(search == null || "".equals(search))
  244. return null;
  245. // replace must not be null
  246. String replace = (SearchAndReplace.replace == null ? "" : SearchAndReplace.replace);
  247. String replaceMethod;
  248. if(beanshell && replace.length() != 0)
  249. {
  250. replaceMethod = BeanShell.cacheBlock("replace","return ("
  251. + replace + ");",false);
  252. }
  253. else
  254. replaceMethod = null;
  255. if(regexp)
  256. matcher = new RESearchMatcher(search,replace,ignoreCase,
  257. beanshell,replaceMethod);
  258. else
  259. {
  260. matcher = new BoyerMooreSearchMatcher(search,replace,
  261. ignoreCase,reverse && reverseOK,beanshell,
  262. replaceMethod);
  263. }
  264. return matcher;
  265. } //}}}
  266. //{{{ setSearchFileSet() method
  267. /**
  268. * Sets the current search file set.
  269. * @param fileset The file set to perform searches in
  270. */
  271. public static void setSearchFileSet(SearchFileSet fileset)
  272. {
  273. SearchAndReplace.fileset = fileset;
  274. EditBus.send(new SearchSettingsChanged(null));
  275. } //}}}
  276. //{{{ getSearchFileSet() method
  277. /**
  278. * Returns the current search file set.
  279. */
  280. public static SearchFileSet getSearchFileSet()
  281. {
  282. return fileset;
  283. } //}}}
  284. //}}}
  285. //{{{ Actions
  286. //{{{ hyperSearch() method
  287. /**
  288. * Performs a HyperSearch.
  289. * @param view The view
  290. * @since jEdit 2.7pre3
  291. */
  292. public static boolean hyperSearch(View view)
  293. {
  294. return hyperSearch(view,false);
  295. } //}}}
  296. //{{{ hyperSearch() method
  297. /**
  298. * Performs a HyperSearch.
  299. * @param view The view
  300. * @param selection If true, will only search in the current selection.
  301. * Note that the file set must be the current buffer file set for this
  302. * to work.
  303. * @since jEdit 4.0pre1
  304. */
  305. public static boolean hyperSearch(View view, boolean selection)
  306. {
  307. record(view,"hyperSearch(view," + selection + ")",false,
  308. !selection);
  309. view.getDockableWindowManager().addDockableWindow(
  310. HyperSearchResults.NAME);
  311. final HyperSearchResults results = (HyperSearchResults)
  312. view.getDockableWindowManager()
  313. .getDockable(HyperSearchResults.NAME);
  314. results.searchStarted();
  315. try
  316. {
  317. Selection[] s;
  318. if(selection)
  319. {
  320. s = view.getTextArea().getSelection();
  321. if(s == null)
  322. return false;
  323. }
  324. else
  325. s = null;
  326. VFSManager.runInWorkThread(new HyperSearchRequest(view,
  327. getSearchMatcher(false),results,s));
  328. return true;
  329. }
  330. catch(Exception e)
  331. {
  332. Log.log(Log.ERROR,SearchAndReplace.class,e);
  333. Object[] args = { e.getMessage() };
  334. if(args[0] == null)
  335. args[0] = e.toString();
  336. GUIUtilities.error(view,"searcherror",args);
  337. return false;
  338. }
  339. } //}}}
  340. //{{{ find() method
  341. /**
  342. * Finds the next occurance of the search string.
  343. * @param view The view
  344. * @return True if the operation was successful, false otherwise
  345. */
  346. public static boolean find(View view)
  347. {
  348. boolean repeat = false;
  349. Buffer buffer = fileset.getNextBuffer(view,null);
  350. try
  351. {
  352. SearchMatcher matcher = getSearchMatcher(true);
  353. if(matcher == null)
  354. {
  355. view.getToolkit().beep();
  356. return false;
  357. }
  358. record(view,"find(view)",false,true);
  359. view.showWaitCursor();
  360. loop: for(;;)
  361. {
  362. while(buffer != null)
  363. {
  364. // Wait for the buffer to load
  365. if(!buffer.isLoaded())
  366. VFSManager.waitForRequests();
  367. int start;
  368. if(view.getBuffer() == buffer && !repeat)
  369. {
  370. JEditTextArea textArea = view.getTextArea();
  371. Selection s = textArea.getSelectionAtOffset(
  372. textArea.getCaretPosition());
  373. if(s == null)
  374. start = textArea.getCaretPosition();
  375. else if(reverse)
  376. start = s.getStart();
  377. else
  378. start = s.getEnd();
  379. }
  380. else if(reverse)
  381. start = buffer.getLength();
  382. else
  383. start = 0;
  384. if(find(view,buffer,start))
  385. return true;
  386. buffer = fileset.getNextBuffer(view,buffer);
  387. }
  388. if(repeat)
  389. {
  390. // no point showing this dialog box twice
  391. view.getToolkit().beep();
  392. return false;
  393. }
  394. /* Don't do this when playing a macro */
  395. if(BeanShell.isScriptRunning())
  396. break loop;
  397. boolean restart;
  398. if(wrap)
  399. {
  400. view.getStatus().setMessageAndClear(
  401. jEdit.getProperty("view.status.auto-wrap"));
  402. // beep if beep property set
  403. if(jEdit.getBooleanProperty("search.beepOnSearchAutoWrap"))
  404. {
  405. view.getToolkit().beep();
  406. }
  407. restart = true;
  408. }
  409. else
  410. {
  411. Integer[] args = { new Integer(reverse ? 1 : 0) };
  412. int result = GUIUtilities.confirm(view,
  413. "keepsearching",args,
  414. JOptionPane.YES_NO_OPTION,
  415. JOptionPane.QUESTION_MESSAGE);
  416. restart = (result == JOptionPane.YES_OPTION);
  417. }
  418. if(restart)
  419. {
  420. // start search from beginning
  421. buffer = fileset.getFirstBuffer(view);
  422. repeat = true;
  423. }
  424. else
  425. break loop;
  426. }
  427. }
  428. catch(Exception e)
  429. {
  430. Log.log(Log.ERROR,SearchAndReplace.class,e);
  431. Object[] args = { e.getMessage() };
  432. if(args[0] == null)
  433. args[0] = e.toString();
  434. GUIUtilities.error(view,"searcherror",args);
  435. }
  436. finally
  437. {
  438. view.hideWaitCursor();
  439. }
  440. return false;
  441. } //}}}
  442. //{{{ find() method
  443. /**
  444. * Finds the next instance of the search string in the specified
  445. * buffer.
  446. * @param view The view
  447. * @param buffer The buffer
  448. * @param start Location where to start the search
  449. */
  450. public static boolean find(final View view, final Buffer buffer, final int start)
  451. throws Exception
  452. {
  453. SearchMatcher matcher = getSearchMatcher(true);
  454. Segment text = new Segment();
  455. if(reverse)
  456. buffer.getText(0,start,text);
  457. else
  458. buffer.getText(start,buffer.getLength() - start,text);
  459. // the start and end flags will be wrong with reverse search enabled,
  460. // but they are only used by the regexp matcher, which doesn't
  461. // support reverse search yet.
  462. //
  463. // REMIND: fix flags when adding reverse regexp search.
  464. int[] match = matcher.nextMatch(text,start == 0,true);
  465. if(match != null)
  466. {
  467. fileset.matchFound(buffer);
  468. view.setBuffer(buffer);
  469. JEditTextArea textArea = view.getTextArea();
  470. int matchStart = (reverse ? 0 : start);
  471. textArea.setSelection(new Selection.Range(
  472. matchStart + match[0],
  473. matchStart + match[1]));
  474. textArea.moveCaretPosition(matchStart + match[1]);
  475. return true;
  476. }
  477. else
  478. return false;
  479. } //}}}
  480. //{{{ replace() method
  481. /**
  482. * Replaces the current selection with the replacement string.
  483. * @param view The view
  484. * @return True if the operation was successful, false otherwise
  485. */
  486. public static boolean replace(View view)
  487. {
  488. JEditTextArea textArea = view.getTextArea();
  489. Selection[] selection = textArea.getSelection();
  490. if(selection.length == 0)
  491. {
  492. view.getToolkit().beep();
  493. return false;
  494. }
  495. record(view,"replace(view)",true,false);
  496. Buffer buffer = view.getBuffer();
  497. try
  498. {
  499. buffer.beginCompoundEdit();
  500. int retVal = 0;
  501. for(int i = 0; i < selection.length; i++)
  502. {
  503. Selection s = selection[i];
  504. /* if an occurence occurs at the
  505. beginning of the selection, the
  506. selection start will get moved.
  507. this sucks, so we hack to avoid it. */
  508. int start = s.getStart();
  509. retVal += _replace(view,buffer,
  510. s.getStart(),s.getEnd());
  511. // this has no effect if the selection
  512. // no longer exists
  513. textArea.removeFromSelection(s);
  514. if(s instanceof Selection.Range)
  515. {
  516. textArea.addToSelection(new Selection.Range(
  517. start,s.getEnd()));
  518. }
  519. else if(s instanceof Selection.Rect)
  520. {
  521. textArea.addToSelection(new Selection.Rect(
  522. start,s.getEnd()));
  523. }
  524. }
  525. if(retVal == 0)
  526. {
  527. view.getToolkit().beep();
  528. return false;
  529. }
  530. return true;
  531. }
  532. catch(Exception e)
  533. {
  534. Log.log(Log.ERROR,SearchAndReplace.class,e);
  535. Object[] args = { e.getMessage() };
  536. if(args[0] == null)
  537. args[0] = e.toString();
  538. GUIUtilities.error(view,"searcherror",args);
  539. }
  540. finally
  541. {
  542. buffer.endCompoundEdit();
  543. }
  544. return false;
  545. } //}}}
  546. //{{{ replace() method
  547. /**
  548. * Replaces text in the specified range with the replacement string.
  549. * @param view The view
  550. * @param buffer The buffer
  551. * @param start The start offset
  552. * @param end The end offset
  553. * @return True if the operation was successful, false otherwise
  554. */
  555. public static boolean replace(View view, Buffer buffer, int start, int end)
  556. {
  557. JEditTextArea textArea = view.getTextArea();
  558. try
  559. {
  560. int retVal = 0;
  561. buffer.beginCompoundEdit();
  562. retVal += _replace(view,buffer,start,end);
  563. if(retVal != 0)
  564. return true;
  565. }
  566. catch(Exception e)
  567. {
  568. Log.log(Log.ERROR,SearchAndReplace.class,e);
  569. Object[] args = { e.getMessage() };
  570. if(args[0] == null)
  571. args[0] = e.toString();
  572. GUIUtilities.error(view,"searcherror",args);
  573. }
  574. finally
  575. {
  576. buffer.endCompoundEdit();
  577. }
  578. return false;
  579. } //}}}
  580. //{{{ replaceAll() method
  581. /**
  582. * Replaces all occurances of the search string with the replacement
  583. * string.
  584. * @param view The view
  585. */
  586. public static boolean replaceAll(View view)
  587. {
  588. int fileCount = 0;
  589. int occurCount = 0;
  590. record(view,"replaceAll(view)",true,true);
  591. view.showWaitCursor();
  592. try
  593. {
  594. Buffer buffer = fileset.getFirstBuffer(view);
  595. do
  596. {
  597. // Wait for buffer to finish loading
  598. if(buffer.isPerformingIO())
  599. VFSManager.waitForRequests();
  600. // Leave buffer in a consistent state if
  601. // an error occurs
  602. try
  603. {
  604. buffer.beginCompoundEdit();
  605. int retVal = _replace(view,buffer,
  606. 0,buffer.getLength());
  607. if(retVal != 0)
  608. {
  609. fileCount++;
  610. occurCount += retVal;
  611. fileset.matchFound(buffer);
  612. }
  613. }
  614. finally
  615. {
  616. buffer.endCompoundEdit();
  617. }
  618. }
  619. while((buffer = fileset.getNextBuffer(view,buffer)) != null);
  620. }
  621. catch(Exception e)
  622. {
  623. Log.log(Log.ERROR,SearchAndReplace.class,e);
  624. Object[] args = { e.getMessage() };
  625. if(args[0] == null)
  626. args[0] = e.toString();
  627. GUIUtilities.error(view,"searcherror",args);
  628. }
  629. finally
  630. {
  631. view.hideWaitCursor();
  632. }
  633. /* Don't do this when playing a macro, cos it's annoying */
  634. if(!BeanShell.isScriptRunning())
  635. {
  636. Object[] args = { new Integer(occurCount),
  637. new Integer(fileCount) };
  638. view.getStatus().setMessageAndClear(jEdit.getProperty(
  639. "view.status.replace-all",args));
  640. }
  641. return (fileCount != 0);
  642. } //}}}
  643. //}}}
  644. //{{{ load() method
  645. /**
  646. * Loads search and replace state from the properties.
  647. */
  648. public static void load()
  649. {
  650. search = jEdit.getProperty("search.find.value");
  651. replace = jEdit.getProperty("search.replace.value");
  652. ignoreCase = jEdit.getBooleanProperty("search.ignoreCase.toggle");
  653. regexp = jEdit.getBooleanProperty("search.regexp.toggle");
  654. reverse = jEdit.getBooleanProperty("search.reverse.toggle");
  655. beanshell = jEdit.getBooleanProperty("search.beanshell.toggle");
  656. wrap = jEdit.getBooleanProperty("search.wrap.toggle");
  657. fileset = new CurrentBufferSet();
  658. // Tags plugin likes to call this method at times other than
  659. // startup; so we need to fire a SearchSettingsChanged to
  660. // notify the search bar and so on.
  661. EditBus.send(new SearchSettingsChanged(null));
  662. } //}}}
  663. //{{{ save() method
  664. /**
  665. * Saves search and replace state to the properties.
  666. */
  667. public static void save()
  668. {
  669. jEdit.setProperty("search.find.value",search);
  670. jEdit.setProperty("search.replace.value",replace);
  671. jEdit.setBooleanProperty("search.ignoreCase.toggle",ignoreCase);
  672. jEdit.setBooleanProperty("search.regexp.toggle",regexp);
  673. jEdit.setBooleanProperty("search.reverse.toggle",reverse);
  674. jEdit.setBooleanProperty("search.beanshell.toggle",beanshell);
  675. jEdit.setBooleanProperty("search.wrap.toggle",wrap);
  676. } //}}}
  677. //{{{ Private members
  678. //{{{ Instance variables
  679. private static String search;
  680. private static String replace;
  681. private static boolean regexp;
  682. private static boolean ignoreCase;
  683. private static boolean reverse;
  684. private static boolean beanshell;
  685. private static boolean wrap;
  686. private static SearchMatcher matcher;
  687. private static SearchFileSet fileset;
  688. //}}}
  689. //{{{ record() method
  690. private static void record(View view, String action,
  691. boolean replaceAction, boolean recordFileSet)
  692. {
  693. Macros.Recorder recorder = view.getMacroRecorder();
  694. if(recorder != null)
  695. {
  696. recorder.record("SearchAndReplace.setSearchString(\""
  697. + MiscUtilities.charsToEscapes(search) + "\");");
  698. if(replaceAction)
  699. {
  700. recorder.record("SearchAndReplace.setReplaceString(\""
  701. + MiscUtilities.charsToEscapes(replace) + "\");");
  702. recorder.record("SearchAndReplace.setBeanShellReplace("
  703. + beanshell + ");");
  704. }
  705. else
  706. {
  707. // only record this if doing a find next
  708. recorder.record("SearchAndReplace.setAutoWrapAround("
  709. + wrap + ");");
  710. recorder.record("SearchAndReplace.setReverseSearch("
  711. + reverse + ");");
  712. }
  713. recorder.record("SearchAndReplace.setIgnoreCase("
  714. + ignoreCase + ");");
  715. recorder.record("SearchAndReplace.setRegexp("
  716. + regexp + ");");
  717. if(recordFileSet)
  718. {
  719. recorder.record("SearchAndReplace.setSearchFileSet("
  720. + fileset.getCode() + ");");
  721. }
  722. recorder.record("SearchAndReplace." + action + ";");
  723. }
  724. } //}}}
  725. //{{{ _replace() method
  726. /**
  727. * Replaces all occurances of the search string with the replacement
  728. * string.
  729. * @param view The view
  730. * @param buffer The buffer
  731. * @param start The start offset
  732. * @param end The end offset
  733. * @return True if the replace operation was successful, false
  734. * if no matches were found
  735. */
  736. private static int _replace(View view, Buffer buffer,
  737. int start, int end) throws Exception
  738. {
  739. if(!buffer.isEditable())
  740. return 0;
  741. SearchMatcher matcher = getSearchMatcher(false);
  742. if(matcher == null)
  743. return 0;
  744. int occurCount = 0;
  745. boolean smartCaseReplace = (TextUtilities.getStringCase(replace)
  746. == TextUtilities.LOWER_CASE);
  747. Segment text = new Segment();
  748. int offset = start;
  749. loop: for(;;)
  750. {
  751. buffer.getText(offset,end - offset,text);
  752. int[] occur = matcher.nextMatch(text,start == 0,
  753. end == buffer.getLength());
  754. if(occur == null)
  755. break loop;
  756. int _start = occur[0];
  757. int _length = occur[1] - occur[0];
  758. String found = new String(text.array,text.offset + _start,_length);
  759. String subst = matcher.substitute(found);
  760. if(smartCaseReplace && ignoreCase)
  761. {
  762. int strCase = TextUtilities.getStringCase(found);
  763. if(strCase == TextUtilities.LOWER_CASE)
  764. subst = subst.toLowerCase();
  765. else if(strCase == TextUtilities.UPPER_CASE)
  766. subst = subst.toUpperCase();
  767. else if(strCase == TextUtilities.TITLE_CASE)
  768. subst = TextUtilities.toTitleCase(subst);
  769. }
  770. if(subst != null)
  771. {
  772. buffer.remove(offset + _start,_length);
  773. buffer.insert(offset + _start,subst);
  774. occurCount++;
  775. offset += _start + subst.length();
  776. if(subst.length() == 0 && _length == 0)
  777. offset++;
  778. end += (subst.length() - found.length());
  779. }
  780. else if(_length == 0)
  781. {
  782. // avoid infinite loop
  783. offset += _start + 1;
  784. }
  785. else
  786. offset += _start + _length;
  787. }
  788. return occurCount;
  789. } //}}}
  790. //}}}
  791. }