|
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.