org.fife.ui.search.FindInFilesThread Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of rtext Show documentation
Show all versions of rtext Show documentation
RText is a powerful, cross-platform programmer's text editor written in Java. It is designed
to be easy to use, highly customizable and flexible. Part of RText's design is for the source code
to be simple, easy to understand, and well documented, so that other programmers can look into its
inner-workings and figure out how RText ticks with ease. A good place to start (besides the source
code) is the Javadoc for all classes used in the project.
/*
* 10/03/2005
*
* FindInFilesThread.java - Thread that does the searching for a
* Find in Files dialog.
* Copyright (C) 2005 Robert Futrell
* robert_futrell at users.sourceforge.net
* http://fifesoft.com
*
* 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.fife.ui.search;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.Reader;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.ResourceBundle;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import javax.swing.text.Document;
import javax.swing.text.Element;
import javax.swing.text.Segment;
import org.fife.io.UnicodeReader;
import org.fife.rtext.AbstractMainView;
import org.fife.rtext.RText;
import org.fife.ui.GUIWorkerThread;
import org.fife.ui.rsyntaxtextarea.RSyntaxDocument;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rsyntaxtextarea.Token;
/**
* A thread created by a FindInFilesDialog
to do the searching.
*
* @author Robert Futrell
* @version 1.0
* @see FindInFilesDialog
*/
class FindInFilesThread extends GUIWorkerThread {
protected static final String NO_LINE_NUMBER = "--";
protected FindInFilesDialog dialog;
protected File directory;
private String verboseLabelString;
private String errorLabelString;
protected String verboseNoFiltMatchString;
protected String dontSearchSubfoldersString;
protected String newFilesToExamineString;
protected String occurrencesString;
// private static final Pattern TAB_PATTERN = Pattern.compile("\\t");
/**
* Constructor.
*
* @param dialog The "find in files" dialog.
* @param directory The directory in which to search.
*/
public FindInFilesThread(FindInFilesDialog dialog, File directory) {
this.dialog = dialog;
this.directory = directory;
ResourceBundle msg = dialog.getBundle();
verboseLabelString = "" + msg.getString("VerboseLabel") +
"";
errorLabelString = "" + msg.getString("ErrorLabel") +
"";
verboseNoFiltMatchString = msg.getString("VerboseNoFiltMatch");
dontSearchSubfoldersString = msg.getString("SearchSubFoldUnchecked");
newFilesToExamineString = msg.getString("NewFilesToExamine");
occurrencesString = msg.getString("Occurrences");
}
protected MatchData createErrorMatchData(String filePath, String msg) {
return new MatchData(filePath, NO_LINE_NUMBER, errorLabelString + msg,
MatchData.TYPE_ERROR);
}
protected MatchData createVerboseMatchData(String filePath, String msg) {
return new MatchData(filePath, NO_LINE_NUMBER,
verboseLabelString + msg, MatchData.TYPE_VERBOSE);
}
/**
* Runs the search.
*/
public Object construct() {
RText parent = (RText)dialog.getOwner();
AbstractMainView view = parent.getMainView();
// Get the string to search for and filters for the files to search.
String searchString = dialog.getSearchString();
Pattern[] filterStrings = getFilterStrings();
if (filterStrings==null) {
dialog.searchCompleted("");
return null;
}
// Then, do the search.
dialog.clearSearchResults();
File[] files = directory.listFiles();
List fileList = new ArrayList();
fileList.addAll(Arrays.asList(files));
boolean checkSubfolders = dialog.getCheckSubfolders();
boolean matchingLines = dialog.getShowMatchingLines();
boolean matchCase = dialog.getMatchCase();
boolean wholeWord = dialog.getMatchWholeWord();
boolean useRegex = dialog.getUseRegEx();
boolean doVerboseOutput = dialog.getDoVerboseOutput();
Segment seg = new Segment();
String searchingFile = dialog.getBundle().getString("SearchingFile");
if (!useRegex && !matchCase)
searchString = searchString.toLowerCase();
RSyntaxTextArea textArea = new RSyntaxTextArea();
long startMillis = System.currentTimeMillis();
// Keep looping while there are more files to search.
int numFiles = fileList.size();
for (int i=0; i0) {
fileList.addAll(moreFilesList);
numFiles += count;
}
if (doVerboseOutput) {
MatchData data = createVerboseMatchData(
fileFullPath, newFilesToExamineString +
": " + count);
dialog.addMatchData(data);
}
} // End of else if (temp.isDirectory()).
} // End of for (int i=0; i0) {
String text = MessageFormat.format(occurrencesString,
new Object[] { new Integer(numMatches) });
MatchData data = new MatchData(fileFullPath, NO_LINE_NUMBER,
text);
dialog.addMatchData(data);
}
}
/**
* Performs a regex "Find in Files" operation on a single file.
*/
private void doSearchRegex(String buffer, String searchString,
RSyntaxTextArea textArea, boolean matchCase,
boolean wholeWord, boolean matchingLines,
String fileFullPath, Segment seg) {
Document doc = textArea.getDocument();
Element map = doc.getDefaultRootElement();
Element elem = null;
int lineCount = map.getElementCount();
int numMatches = 0;
int lastStartLine = -1;
// Create a Matcher to find the text we're looking for.
int flags = matchCase ? 0 : (Pattern.CASE_INSENSITIVE|Pattern.UNICODE_CASE);
Pattern pattern = Pattern.compile(searchString, flags);
Matcher m = pattern.matcher(buffer);
// Loop through all matches.
while (m.find()) {
int start = m.start();
int end = m.end();
// If we found a match...
if (!wholeWord || FindDialog.isWholeWord(buffer, start, end-start)) {
numMatches++;
// If we're interested in seeing each match...
if (matchingLines) {
// Get the text of the first line of the match.
int startLine = map.getElementIndex(start);
if (startLine==lastStartLine) {
// If a single line has > 1 match, don't show
// the same line multiple times.
continue;
}
lastStartLine = startLine;
int endLine = map.getElementIndex(end);
elem = map.getElement(startLine);
start = elem.getStartOffset();
end = startLine==lineCount-1 ? elem.getEndOffset()-1 :
elem.getEndOffset();
Token t = textArea.getTokenListForLine(startLine);
String text = getHtml(t, textArea);
// Add an item to our results.
boolean oneLine = startLine==endLine;
String lineStr = oneLine ? Integer.toString(startLine+1) :
((startLine+1) + "-" + (endLine+1));
if (!oneLine) {
text += " " +
dialog.getBundle().getString("MultiLineMatch") +
"";
}
MatchData data = new MatchData(
fileFullPath, lineStr, text);
dialog.addMatchData(data);
} // End of if (matchingLines)
} // End of if (!wholeWord || FindDialog.isWholeWord(...))
} // End of while (m.find())
// If we're only interested in the match count, not individual
// matches, add an entry for this file.
if (matchingLines==false && numMatches>0) {
String text = MessageFormat.format(occurrencesString,
new Object[] { new Integer(numMatches) });
MatchData data = new MatchData(fileFullPath,
NO_LINE_NUMBER, text);
dialog.addMatchData(data);
}
}
/**
* Returns the files contained in the specified directory as a list.
*
* @param dir The directory.
* @return The files in the directory, as a list.
*/
protected static final List getFilesFromDirectory(File dir) {
// Get the list of files in this directory.
File[] moreFiles = dir.listFiles();
if (moreFiles==null) {
// Should never happen (as dirs return empty arrays).
return new ArrayList(0);
}
return Arrays.asList(moreFiles);
}
protected Pattern[] getFilterStrings() {
// Get the list of regular expressions to apply when deciding
// whether or not to look in a file. If we're on Windows, or OS X,
// do case-insensitive regexes.
String[] tokens = dialog.getInFilesComboBoxContents().trim().
split("\\s*,?\\s+");
if (tokens==null || tokens.length==0) {
return null;
}
int tokenCount = tokens.length;
Pattern[] filterStrings = new Pattern[tokenCount];
int flags = 0;
String os = System.getProperty("os.name");
if (os!=null) { // Windows and OS X need case-insensitive searching.
os = os.toLowerCase();
if (os.indexOf("windows")>-1 || os.indexOf("mac")>-1)
flags = Pattern.CASE_INSENSITIVE;
}
try {
for (int i=0; i");
boolean firstNonWhitespace = false; // Skip leading whitespace
while (t!=null && t.isPaintable() && sb.length()=maxLen) {
sb.append("...");
}
//System.out.println(sb.toString());
return sb.toString();
}
/**
* Converts a String
representing a wildcard file filter into
* another String
containing a regular expression good for
* finding files that match the wildcard expressions.
* Example: For
* String regEx = getRegexForFileFilter("*.c");
*
* regEx
will contain ^.*\.c$
.
*
* @param fileFilter The file filter for which to create equivalent
* regular expressions. This filter can currently only contain
* the wildcards '*' and '?'.
* @return A String
representing an equivalent regular
* expression for the string passed in. If an error occurs,
* null
is returned.
*/
protected static final String getRegexForFileFilter(String filter) {
filter = filter.replaceAll("\\.", "\\\\."); // '.' => '\.'
filter = filter.replaceAll("\\*", ".*"); // '*' => '.*'
filter = filter.replaceAll("\\?", "."); // '?' => '.'
filter = filter.replaceAll("\\$", "\\\\\\$"); // '$' => '\$'
return "^" + filter + "$";
}
/**
* Returns whether the specified file is "filtered out" and should
* not be searched.
*
* @param file The file name.
* @param filters The filters for files to search.
* @return Whether the file is filtered out.
*/
protected static final boolean isFilteredOut(String file,
Pattern[] filters) {
for (int j=0; j