alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 * SearchAndReplace.java - Search and replace
 * :tabSize=8:indentSize=8:noTabs=false:
 * :folding=explicit:collapseFolds=1:
 *
 * Copyright (C) 1999, 2004 Slava Pestov
 * Portions copyright (C) 2001 Tom Locke
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

package org.gjt.sp.jedit.search;

//{{{ Imports
import bsh.*;
import java.awt.*;
import javax.swing.JOptionPane;
import javax.swing.text.Segment;
import org.gjt.sp.jedit.*;
import org.gjt.sp.jedit.gui.TextAreaDialog;
import org.gjt.sp.jedit.io.VFSManager;
import org.gjt.sp.jedit.msg.SearchSettingsChanged;
import org.gjt.sp.jedit.textarea.*;
import org.gjt.sp.util.CharIndexedSegment;
import org.gjt.sp.util.Log;
//}}}

/**
 * Class that implements regular expression and literal search within
 * jEdit buffers.

* * There are two main groups of methods in this class: *

    *
  • Property accessors - for changing search and replace settings.
  • *
  • Actions - for performing search and replace.
  • *
* * The "HyperSearch" and "Keep dialog" features, as reflected in * checkbox options in the search dialog, are not handled from within * this class. If you wish to have these options set before the search dialog * appears, make a prior call to either or both of the following: * *
 jEdit.setBooleanProperty("search.hypersearch.toggle",true);
 * jEdit.setBooleanProperty("search.keepDialog.toggle",true);
* * If you are not using the dialog to undertake a search or replace, you may * call any of the search and replace methods (including * {@link #hyperSearch(View)}) without concern for the value of these * properties. * * @author Slava Pestov * @author John Gellene (API documentation) * @version $Id: SearchAndReplace.java,v 1.64 2004/08/08 03:41:34 spestov Exp $ */ public class SearchAndReplace { //{{{ Getters and setters //{{{ setSearchString() method /** * Sets the current search string. * @param search The new search string */ public static void setSearchString(String search) { if(search.equals(SearchAndReplace.search)) return; SearchAndReplace.search = search; matcher = null; EditBus.send(new SearchSettingsChanged(null)); } //}}} //{{{ getSearchString() method /** * Returns the current search string. */ public static String getSearchString() { return search; } //}}} //{{{ setReplaceString() method /** * Sets the current replacement string. * @param replace The new replacement string */ public static void setReplaceString(String replace) { if(replace.equals(SearchAndReplace.replace)) return; SearchAndReplace.replace = replace; EditBus.send(new SearchSettingsChanged(null)); } //}}} //{{{ getReplaceString() method /** * Returns the current replacement string. */ public static String getReplaceString() { return replace; } //}}} //{{{ setIgnoreCase() method /** * Sets the ignore case flag. * @param ignoreCase True if searches should be case insensitive, * false otherwise */ public static void setIgnoreCase(boolean ignoreCase) { if(ignoreCase == SearchAndReplace.ignoreCase) return; SearchAndReplace.ignoreCase = ignoreCase; matcher = null; EditBus.send(new SearchSettingsChanged(null)); } //}}} //{{{ getIgnoreCase() method /** * Returns the state of the ignore case flag. * @return True if searches should be case insensitive, * false otherwise */ public static boolean getIgnoreCase() { return ignoreCase; } //}}} //{{{ setRegexp() method /** * Sets the state of the regular expression flag. * @param regexp True if regular expression searches should be * performed */ public static void setRegexp(boolean regexp) { if(regexp == SearchAndReplace.regexp) return; SearchAndReplace.regexp = regexp; if(regexp && reverse) reverse = false; matcher = null; EditBus.send(new SearchSettingsChanged(null)); } //}}} //{{{ getRegexp() method /** * Returns the state of the regular expression flag. * @return True if regular expression searches should be performed */ public static boolean getRegexp() { return regexp; } //}}} //{{{ setReverseSearch() method /** * Determines whether a reverse search will conducted from the current * position to the beginning of a buffer. Note that reverse search and * regular expression search is mutually exclusive; enabling one will * disable the other. * @param reverse True if searches should go backwards, * false otherwise */ public static void setReverseSearch(boolean reverse) { if(reverse == SearchAndReplace.reverse) return; SearchAndReplace.reverse = reverse; EditBus.send(new SearchSettingsChanged(null)); } //}}} //{{{ getReverseSearch() method /** * Returns the state of the reverse search flag. * @return True if searches should go backwards, * false otherwise */ public static boolean getReverseSearch() { return reverse; } //}}} //{{{ setBeanShellReplace() method /** * Sets the state of the BeanShell replace flag. * @param beanshell True if the replace string is a BeanShell expression * @since jEdit 3.2pre2 */ public static void setBeanShellReplace(boolean beanshell) { if(beanshell == SearchAndReplace.beanshell) return; SearchAndReplace.beanshell = beanshell; EditBus.send(new SearchSettingsChanged(null)); } //}}} //{{{ getBeanShellReplace() method /** * Returns the state of the BeanShell replace flag. * @return True if the replace string is a BeanShell expression * @since jEdit 3.2pre2 */ public static boolean getBeanShellReplace() { return beanshell; } //}}} //{{{ setAutoWrap() method /** * Sets the state of the auto wrap around flag. * @param wrap If true, the 'continue search from start' dialog * will not be displayed * @since jEdit 3.2pre2 */ public static void setAutoWrapAround(boolean wrap) { if(wrap == SearchAndReplace.wrap) return; SearchAndReplace.wrap = wrap; EditBus.send(new SearchSettingsChanged(null)); } //}}} //{{{ getAutoWrap() method /** * Returns the state of the auto wrap around flag. * @since jEdit 3.2pre2 */ public static boolean getAutoWrapAround() { return wrap; } //}}} //{{{ setSearchMatcher() method /** * Sets a custom search string matcher. Note that calling * {@link #setSearchString(String)}, * {@link #setIgnoreCase(boolean)}, or {@link #setRegexp(boolean)} * will reset the matcher to the default. */ public static void setSearchMatcher(SearchMatcher matcher) { SearchAndReplace.matcher = matcher; EditBus.send(new SearchSettingsChanged(null)); } //}}} //{{{ getSearchMatcher() method /** * Returns the current search string matcher. * @exception IllegalArgumentException if regular expression search * is enabled, the search string or replacement string is invalid * @since jEdit 4.1pre7 */ public static SearchMatcher getSearchMatcher() throws Exception { if(matcher != null) return matcher; if(search == null || "".equals(search)) return null; if(regexp) matcher = new RESearchMatcher(search,ignoreCase); else { matcher = new BoyerMooreSearchMatcher(search,ignoreCase); } return matcher; } //}}} //{{{ setSearchFileSet() method /** * Sets the current search file set. * @param fileset The file set to perform searches in * @see AllBufferSet * @see CurrentBufferSet * @see DirectoryListSet */ public static void setSearchFileSet(SearchFileSet fileset) { SearchAndReplace.fileset = fileset; EditBus.send(new SearchSettingsChanged(null)); } //}}} //{{{ getSearchFileSet() method /** * Returns the current search file set. */ public static SearchFileSet getSearchFileSet() { return fileset; } //}}} //{{{ getSmartCaseReplace() method /** * Returns if the replacement string will assume the same case as * each specific occurrence of the search string. * @since jEdit 4.2pre10 */ public static boolean getSmartCaseReplace() { return (replace != null && TextUtilities.getStringCase(replace) == TextUtilities.LOWER_CASE); } //}}} //}}} //{{{ Actions //{{{ hyperSearch() method /** * Performs a HyperSearch. * @param view The view * @since jEdit 2.7pre3 */ public static boolean hyperSearch(View view) { return hyperSearch(view,false); } //}}} //{{{ hyperSearch() method /** * Performs a HyperSearch. * @param view The view * @param selection If true, will only search in the current selection. * Note that the file set must be the current buffer file set for this * to work. * @since jEdit 4.0pre1 */ public static boolean hyperSearch(View view, boolean selection) { // component that will parent any dialog boxes Component comp = SearchDialog.getSearchDialog(view); if(comp == null) comp = view; record(view,"hyperSearch(view," + selection + ")",false, !selection); view.getDockableWindowManager().addDockableWindow( HyperSearchResults.NAME); final HyperSearchResults results = (HyperSearchResults) view.getDockableWindowManager() .getDockable(HyperSearchResults.NAME); results.searchStarted(); try { SearchMatcher matcher = getSearchMatcher(); if(matcher == null) { view.getToolkit().beep(); results.searchFailed(); return false; } Selection[] s; if(selection) { s = view.getTextArea().getSelection(); if(s == null) { results.searchFailed(); return false; } } else s = null; VFSManager.runInWorkThread(new HyperSearchRequest(view, matcher,results,s)); return true; } catch(Exception e) { results.searchFailed(); handleError(comp,e); return false; } } //}}} //{{{ find() method /** * Finds the next occurance of the search string. * @param view The view * @return True if the operation was successful, false otherwise */ public static boolean find(View view) { // component that will parent any dialog boxes Component comp = SearchDialog.getSearchDialog(view); if(comp == null || !comp.isShowing()) comp = view; boolean repeat = false; String path = fileset.getNextFile(view,null); if(path == null) { GUIUtilities.error(comp,"empty-fileset",null); return false; } boolean _reverse = reverse && fileset instanceof CurrentBufferSet; if(_reverse && regexp) { GUIUtilities.error(comp,"regexp-reverse",null); return false; } try { view.showWaitCursor(); SearchMatcher matcher = getSearchMatcher(); if(matcher == null) { view.getToolkit().beep(); return false; } record(view,"find(view)",false,true); loop: for(;;) { while(path != null) { Buffer buffer = jEdit.openTemporary( view,null,path,false); /* this is stupid and misleading. * but 'path' is not used anywhere except * the above line, and if this is done * after the 'continue', then we will * either hang, or be forced to duplicate * it inside the buffer == null, or add * a 'finally' clause. you decide which one's * worse. */ path = fileset.getNextFile(view,path); if(buffer == null) continue loop; // Wait for the buffer to load if(!buffer.isLoaded()) VFSManager.waitForRequests(); int start; if(view.getBuffer() == buffer && !repeat) { JEditTextArea textArea = view.getTextArea(); Selection s = textArea.getSelectionAtOffset( textArea.getCaretPosition()); if(s == null) start = textArea.getCaretPosition(); else if(_reverse) start = s.getStart(); else start = s.getEnd(); } else if(_reverse) start = buffer.getLength(); else start = 0; if(find(view,buffer,start,repeat,_reverse)) return true; } if(repeat) { if(!BeanShell.isScriptRunning()) { view.getStatus().setMessageAndClear( jEdit.getProperty("view.status.search-not-found")); view.getToolkit().beep(); } return false; } boolean restart; // if auto wrap is on, always restart search. // if auto wrap is off, and we're called from // a macro, stop search. If we're called // interactively, ask the user what to do. if(wrap) { if(!BeanShell.isScriptRunning()) { view.getStatus().setMessageAndClear( jEdit.getProperty("view.status.auto-wrap")); // beep if beep property set if(jEdit.getBooleanProperty("search.beepOnSearchAutoWrap")) { view.getToolkit().beep(); } } restart = true; } else if(BeanShell.isScriptRunning()) { restart = false; } else { Integer[] args = { new Integer(_reverse ? 1 : 0) }; int result = GUIUtilities.confirm(comp, "keepsearching",args, JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE); restart = (result == JOptionPane.YES_OPTION); } if(restart) { // start search from beginning path = fileset.getFirstFile(view); repeat = true; } else break loop; } } catch(Exception e) { handleError(comp,e); } finally { view.hideWaitCursor(); } return false; } //}}} //{{{ find() method /** * Finds the next instance of the search string in the specified * buffer. * @param view The view * @param buffer The buffer * @param start Location where to start the search */ public static boolean find(View view, Buffer buffer, int start) throws Exception { return find(view,buffer,start,false,false); } //}}} //{{{ find() method /** * Finds the next instance of the search string in the specified * buffer. * @param view The view * @param buffer The buffer * @param start Location where to start the search * @param firstTime See {@link SearchMatcher#nextMatch(CharIndexed, * boolean,boolean,boolean,boolean)}. * @since jEdit 4.1pre7 */ public static boolean find(View view, Buffer buffer, int start, boolean firstTime, boolean reverse) throws Exception { SearchMatcher matcher = getSearchMatcher(); if(matcher == null) { view.getToolkit().beep(); return false; } Segment text = new Segment(); if(reverse) buffer.getText(0,start,text); else buffer.getText(start,buffer.getLength() - start,text); // the start and end flags will be wrong with reverse search enabled, // but they are only used by the regexp matcher, which doesn't // support reverse search yet. // // REMIND: fix flags when adding reverse regexp search. SearchMatcher.Match match = matcher.nextMatch(new CharIndexedSegment(text,reverse), start == 0,true,firstTime,reverse); if(match != null) { jEdit.commitTemporary(buffer); view.setBuffer(buffer); JEditTextArea textArea = view.getTextArea(); if(reverse) { textArea.setSelection(new Selection.Range( start - match.end, start - match.start)); // make sure end of match is visible textArea.scrollTo(start - match.start,false); textArea.moveCaretPosition(start - match.end); } else { textArea.setSelection(new Selection.Range( start + match.start, start + match.end)); textArea.moveCaretPosition(start + match.end); // make sure start of match is visible textArea.scrollTo(start + match.start,false); } return true; } else return false; } //}}} //{{{ replace() method /** * Replaces the current selection with the replacement string. * @param view The view * @return True if the operation was successful, false otherwise */ public static boolean replace(View view) { // component that will parent any dialog boxes Component comp = SearchDialog.getSearchDialog(view); if(comp == null) comp = view; JEditTextArea textArea = view.getTextArea(); Buffer buffer = view.getBuffer(); if(!buffer.isEditable()) return false; boolean smartCaseReplace = getSmartCaseReplace(); Selection[] selection = textArea.getSelection(); if(selection.length == 0) { view.getToolkit().beep(); return false; } record(view,"replace(view)",true,false); // a little hack for reverse replace and find int caret = textArea.getCaretPosition(); Selection s = textArea.getSelectionAtOffset(caret); if(s != null) caret = s.getStart(); try { buffer.beginCompoundEdit(); SearchMatcher matcher = getSearchMatcher(); if(matcher == null) return false; initReplace(); int retVal = 0; for(int i = 0; i < selection.length; i++) { s = selection[i]; retVal += replaceInSelection(textArea, buffer,matcher,smartCaseReplace,s); } boolean _reverse = !regexp && reverse && fileset instanceof CurrentBufferSet; if(_reverse) { // so that Replace and Find continues from // the right location textArea.moveCaretPosition(caret); } else { s = textArea.getSelectionAtOffset( textArea.getCaretPosition()); if(s != null) textArea.moveCaretPosition(s.getEnd()); } if(retVal == 0) { view.getToolkit().beep(); return false; } return true; } catch(Exception e) { handleError(comp,e); } finally { buffer.endCompoundEdit(); } return false; } //}}} //{{{ replace() method /** * Replaces text in the specified range with the replacement string. * @param view The view * @param buffer The buffer * @param start The start offset * @param end The end offset * @return True if the operation was successful, false otherwise */ public static boolean replace(View view, Buffer buffer, int start, int end) { if(!buffer.isEditable()) return false; // component that will parent any dialog boxes Component comp = SearchDialog.getSearchDialog(view); if(comp == null) comp = view; boolean smartCaseReplace = getSmartCaseReplace(); try { buffer.beginCompoundEdit(); SearchMatcher matcher = getSearchMatcher(); if(matcher == null) return false; initReplace(); int retVal = 0; retVal += _replace(buffer,matcher,start,end, smartCaseReplace); if(retVal != 0) return true; } catch(Exception e) { handleError(comp,e); } finally { buffer.endCompoundEdit(); } return false; } //}}} //{{{ replaceAll() method /** * Replaces all occurances of the search string with the replacement * string. * @param view The view */ public static boolean replaceAll(View view) { // component that will parent any dialog boxes Component comp = SearchDialog.getSearchDialog(view); if(comp == null) comp = view; int fileCount = 0; int occurCount = 0; if(fileset.getFileCount(view) == 0) { GUIUtilities.error(comp,"empty-fileset",null); return false; } record(view,"replaceAll(view)",true,true); view.showWaitCursor(); boolean smartCaseReplace = (replace != null && TextUtilities.getStringCase(replace) == TextUtilities.LOWER_CASE); try { SearchMatcher matcher = getSearchMatcher(); if(matcher == null) return false; initReplace(); String path = fileset.getFirstFile(view); loop: while(path != null) { Buffer buffer = jEdit.openTemporary( view,null,path,false); /* this is stupid and misleading. * but 'path' is not used anywhere except * the above line, and if this is done * after the 'continue', then we will * either hang, or be forced to duplicate * it inside the buffer == null, or add * a 'finally' clause. you decide which one's * worse. */ path = fileset.getNextFile(view,path); if(buffer == null) continue loop; // Wait for buffer to finish loading if(buffer.isPerformingIO()) VFSManager.waitForRequests(); if(!buffer.isEditable()) continue loop; // Leave buffer in a consistent state if // an error occurs int retVal = 0; try { buffer.beginCompoundEdit(); retVal = _replace(buffer,matcher, 0,buffer.getLength(), smartCaseReplace); } finally { buffer.endCompoundEdit(); } if(retVal != 0) { fileCount++; occurCount += retVal; jEdit.commitTemporary(buffer); } } } catch(Exception e) { handleError(comp,e); } finally { view.hideWaitCursor(); } /* Don't do this when playing a macro, cos it's annoying */ if(!BeanShell.isScriptRunning()) { Object[] args = { new Integer(occurCount), new Integer(fileCount) }; view.getStatus().setMessageAndClear(jEdit.getProperty( "view.status.replace-all",args)); if(occurCount == 0) view.getToolkit().beep(); } return (fileCount != 0); } //}}} //}}} //{{{ load() method /** * Loads search and replace state from the properties. */ public static void load() { search = jEdit.getProperty("search.find.value"); replace = jEdit.getProperty("search.replace.value"); ignoreCase = jEdit.getBooleanProperty("search.ignoreCase.toggle"); regexp = jEdit.getBooleanProperty("search.regexp.toggle"); beanshell = jEdit.getBooleanProperty("search.beanshell.toggle"); wrap = jEdit.getBooleanProperty("search.wrap.toggle"); fileset = new CurrentBufferSet(); // Tags plugin likes to call this method at times other than // startup; so we need to fire a SearchSettingsChanged to // notify the search bar and so on. matcher = null; EditBus.send(new SearchSettingsChanged(null)); } //}}} //{{{ save() method /** * Saves search and replace state to the properties. */ public static void save() { jEdit.setProperty("search.find.value",search); jEdit.setProperty("search.replace.value",replace); jEdit.setBooleanProperty("search.ignoreCase.toggle",ignoreCase); jEdit.setBooleanProperty("search.regexp.toggle",regexp); jEdit.setBooleanProperty("search.beanshell.toggle",beanshell); jEdit.setBooleanProperty("search.wrap.toggle",wrap); } //}}} //{{{ handleError() method static void handleError(Component comp, Exception e) { Log.log(Log.ERROR,SearchAndReplace.class,e); if(comp instanceof Dialog) { new TextAreaDialog((Dialog)comp, beanshell ? "searcherror-bsh" : "searcherror",e); } else { new TextAreaDialog((Frame)comp, beanshell ? "searcherror-bsh" : "searcherror",e); } } //}}} //{{{ Private members //{{{ Instance variables private static String search; private static String replace; private static BshMethod replaceMethod; private static NameSpace replaceNS = new NameSpace( BeanShell.getNameSpace(), BeanShell.getNameSpace().getClassManager(), "search and replace"); private static boolean regexp; private static boolean ignoreCase; private static boolean reverse; private static boolean beanshell; private static boolean wrap; private static SearchMatcher matcher; private static SearchFileSet fileset; //}}} //{{{ initReplace() method /** * Set up BeanShell replace if necessary. */ private static void initReplace() throws Exception { if(beanshell && replace.length() != 0) { replaceMethod = BeanShell.cacheBlock("replace", "return (" + replace + ");",true); } else replaceMethod = null; } //}}} //{{{ record() method private static void record(View view, String action, boolean replaceAction, boolean recordFileSet) { Macros.Recorder recorder = view.getMacroRecorder(); if(recorder != null) { recorder.record("SearchAndReplace.setSearchString(\"" + MiscUtilities.charsToEscapes(search) + "\");"); if(replaceAction) { recorder.record("SearchAndReplace.setReplaceString(\"" + MiscUtilities.charsToEscapes(replace) + "\");"); recorder.record("SearchAndReplace.setBeanShellReplace(" + beanshell + ");"); } else { // only record this if doing a find next recorder.record("SearchAndReplace.setAutoWrapAround(" + wrap + ");"); recorder.record("SearchAndReplace.setReverseSearch(" + reverse + ");"); } recorder.record("SearchAndReplace.setIgnoreCase(" + ignoreCase + ");"); recorder.record("SearchAndReplace.setRegexp(" + regexp + ");"); if(recordFileSet) { recorder.record("SearchAndReplace.setSearchFileSet(" + fileset.getCode() + ");"); } recorder.record("SearchAndReplace." + action + ";"); } } //}}} //{{{ replaceInSelection() method private static int replaceInSelection(JEditTextArea textArea, Buffer buffer, SearchMatcher matcher, boolean smartCaseReplace, Selection s) throws Exception { /* if an occurence occurs at the beginning of the selection, the selection start will get moved. this sucks, so we hack to avoid it. */ int start = s.getStart(); int returnValue; if(s instanceof Selection.Range) { returnValue = _replace(buffer,matcher, s.getStart(),s.getEnd(), smartCaseReplace); textArea.removeFromSelection(s); textArea.addToSelection(new Selection.Range( start,s.getEnd())); } else if(s instanceof Selection.Rect) { Selection.Rect rect = (Selection.Rect)s; int startCol = rect.getStartColumn( buffer); int endCol = rect.getEndColumn( buffer); returnValue = 0; for(int j = s.getStartLine(); j <= s.getEndLine(); j++) { returnValue += _replace(buffer,matcher, getColumnOnOtherLine(buffer,j,startCol), getColumnOnOtherLine(buffer,j,endCol), smartCaseReplace); } textArea.addToSelection(new Selection.Rect( start,s.getEnd())); } else throw new RuntimeException("Unsupported: " + s); return returnValue; } //}}} //{{{ _replace() method /** * Replaces all occurances of the search string with the replacement * string. * @param buffer The buffer * @param start The start offset * @param end The end offset * @param matcher The search matcher to use * @param smartCaseReplace See user's guide * @return The number of occurrences replaced */ private static int _replace(Buffer buffer, SearchMatcher matcher, int start, int end, boolean smartCaseReplace) throws Exception { int occurCount = 0; boolean endOfLine = (buffer.getLineEndOffset( buffer.getLineOfOffset(end)) - 1 == end); Segment text = new Segment(); int offset = start; loop: for(int counter = 0; ; counter++) { buffer.getText(offset,end - offset,text); boolean startOfLine = (buffer.getLineStartOffset( buffer.getLineOfOffset(offset)) == offset); SearchMatcher.Match occur = matcher.nextMatch( new CharIndexedSegment(text,false), startOfLine,endOfLine,counter == 0, false); if(occur == null) break loop; String found = new String(text.array, text.offset + occur.start, occur.end - occur.start); int length = replaceOne(buffer,occur,offset,found, smartCaseReplace); if(length == -1) offset += occur.end; else { offset += occur.start + length; end += (length - found.length()); occurCount++; } } return occurCount; } //}}} //{{{ replaceOne() method /** * Replace one occurrence of the search string with the * replacement string. */ private static int replaceOne(Buffer buffer, SearchMatcher.Match occur, int offset, String found, boolean smartCaseReplace) throws Exception { String subst = replaceOne(occur,found); if(smartCaseReplace && ignoreCase) { int strCase = TextUtilities.getStringCase(found); if(strCase == TextUtilities.LOWER_CASE) subst = subst.toLowerCase(); else if(strCase == TextUtilities.UPPER_CASE) subst = subst.toUpperCase(); else if(strCase == TextUtilities.TITLE_CASE) subst = TextUtilities.toTitleCase(subst); } if(subst != null) { int start = offset + occur.start; int end = offset + occur.end; buffer.remove(start,end - start); buffer.insert(start,subst); return subst.length(); } else return -1; } //}}} //{{{ replaceOne() method private static String replaceOne(SearchMatcher.Match occur, String found) throws Exception { if(regexp) { if(replaceMethod != null) return regexpBeanShellReplace(occur); else return regexpReplace(occur,found); } else { if(replaceMethod != null) return literalBeanShellReplace(found); else return replace; } } //}}} //{{{ regexpBeanShellReplace() method private static String regexpBeanShellReplace(SearchMatcher.Match occur) throws Exception { for(int i = 0; i < occur.substitutions.length; i++) { replaceNS.setVariable("_" + i, occur.substitutions[i]); } Object obj = BeanShell.runCachedBlock( replaceMethod,null,replaceNS); if(obj == null) return ""; else return obj.toString(); } //}}} //{{{ regexpReplace() method private static String regexpReplace(SearchMatcher.Match occur, String found) throws Exception { StringBuffer buf = new StringBuffer(); for(int i = 0; i < replace.length(); i++) { char ch = replace.charAt(i); switch(ch) { case '$': if(i == replace.length() - 1) { buf.append(ch); break; } ch = replace.charAt(++i); if(ch == '$') buf.append('$'); else if(ch == '0') buf.append(found); else if(Character.isDigit(ch)) { int n = ch - '0'; if(n < occur .substitutions .length) { buf.append( occur .substitutions [n] ); } } break; case '\\': if(i == replace.length() - 1) { buf.append('\\'); break; } ch = replace.charAt(++i); switch(ch) { case 'n': buf.append('\n'); break; case 't': buf.append('\t'); break; default: buf.append(ch); break; } break; default: buf.append(ch); break; } } return buf.toString(); } //}}} //{{{ literalBeanShellReplace() method private static String literalBeanShellReplace(String found) throws Exception { replaceNS.setVariable("_0",found); Object obj = BeanShell.runCachedBlock( replaceMethod, null,replaceNS); if(obj == null) return ""; else return obj.toString(); } //}}} //{{{ getColumnOnOtherLine() method /** * Should be somewhere else... */ private static int getColumnOnOtherLine(Buffer buffer, int line, int col) { int returnValue = buffer.getOffsetOfVirtualColumn( line,col,null); if(returnValue == -1) return buffer.getLineEndOffset(line) - 1; else return buffer.getLineStartOffset(line) + returnValue; } //}}} //}}} }
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.