|
What this is
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.
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 |
Copyright 1998-2024 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.