/**
* Copyright (c) 2005-2012 by Appcelerator, Inc. All Rights Reserved.
* Licensed under the terms of the Eclipse Public License (EPL).
* Please see the license.txt included with this distribution for details.
* Any modifications to this file must keep this entire header intact.
*/
package com.python.pydev.analysis.scopeanalysis;
import java.util.ArrayList;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.search.core.text.TextSearchMatchAccess;
import org.eclipse.search.core.text.TextSearchRequestor;
public class TokenMatching {
public static class ReusableMatchAccess extends TextSearchMatchAccess {
private int fOffset;
private int fLength;
private IFile fFile;
private CharSequence fContent;
public void initialize(IFile file, int offset, int length, CharSequence content) {
fFile = file;
fOffset = offset;
fLength = length;
fContent = content;
}
@Override
public IFile getFile() {
return fFile;
}
@Override
public int getMatchOffset() {
return fOffset;
}
@Override
public int getMatchLength() {
return fLength;
}
@Override
public int getFileContentLength() {
return fContent.length();
}
@Override
public char getFileContentChar(int offset) {
return fContent.charAt(offset);
}
@Override
public String getFileContent(int offset, int length) {
return fContent.subSequence(offset, offset + length).toString(); // must pass a copy!
}
}
private final ReusableMatchAccess fMatchAccess;
private final TextSearchRequestor fCollector;
private final CharSequence fSearchText;
public TokenMatching(TextSearchRequestor collector, ReusableMatchAccess matchAccess, CharSequence searchText) {
this.fCollector = collector;
this.fMatchAccess = matchAccess;
this.fSearchText = searchText;
}
public TokenMatching(TextSearchRequestor collector, CharSequence searchText) {
this(collector, new ReusableMatchAccess(), searchText);
}
public TokenMatching(CharSequence searchText) {
this(new TextSearchRequestor() {
}, new ReusableMatchAccess(), searchText);
}
/**
* @return whether we have some match (will collect the first match and return)
*/
public boolean hasMatch(String searchInput) throws CoreException {
return hasMatch(null, searchInput, new NullProgressMonitor());
}
/**
* @return whether we have some match (will collect the first match and return)
*/
public boolean hasMatch(IFile file, String searchInput, IProgressMonitor monitor) throws CoreException {
return collectMatches(null, searchInput, new NullProgressMonitor(), true);
}
/**
* This method will return true if there is any match in the given searchInput regarding the
* fSearchText.
*
* It will call the TextSearchRequestor.acceptPatternMatch on the first match and then bail out...
*
* @note that it has to be a 'token' match, and not only a substring match for it to be valid.
*
* @param file this is the file that contains the match
* @param searchInput the sequence where we want to find the match
* @return true if it did collect something and false otherwise
* @throws CoreException
*/
public boolean collectMatches(IFile file, final String searchInput, IProgressMonitor monitor, boolean onlyFirstMatch)
throws CoreException {
boolean foundMatch = false;
try {
int k = 0;
int total = 0;
char prev = (char) -1;
final int searchTextLen = fSearchText.length();
final int searchInputLen = searchInput.length();
try {
for (int i = 0; i < searchInputLen; i++) {
total += 1;
char c = searchInput.charAt(i);
if (c == fSearchText.charAt(k) && (k > 0 || !Character.isJavaIdentifierPart(prev))) {
k += 1;
if (k == searchTextLen) {
k = 0;
//now, we have to see if is really an 'exact' match (so, either we're in the last
//char or the next char is not actually a word)
boolean ok = false;
if (i + 1 == searchInputLen) {
ok = true;
} else {
c = searchInput.charAt(i + 1);
if (!Character.isJavaIdentifierPart(c)) {
ok = true;
}
}
if (ok) {
fMatchAccess.initialize(file, i - searchTextLen + 1, searchTextLen, searchInput);
fCollector.acceptPatternMatch(fMatchAccess);
foundMatch = true;
if (onlyFirstMatch) {
return foundMatch; //return on first match
}
}
}
} else {
k = 0;
}
prev = c;
if (total++ == 20) {
if (monitor.isCanceled()) {
throw new OperationCanceledException("Operation Canceled");
}
total = 0;
}
}
} catch (IndexOutOfBoundsException e) {
//That's Ok...
}
} finally {
fMatchAccess.initialize(null, 0, 0, ""); // clear references
}
return foundMatch;
}
/**
* @return a list with the offsets for the match in the full string.
* Note that the offsets will be ordered
* @throws CoreException
*/
public static ArrayList<Integer> getMatchOffsets(String match, String fullString) throws CoreException {
final ArrayList<Integer> offsets = new ArrayList<Integer>();
TextSearchRequestor textSearchRequestor = new TextSearchRequestor() {
@Override
public boolean acceptPatternMatch(TextSearchMatchAccess matchAccess) throws CoreException {
offsets.add(matchAccess.getMatchOffset());
return true;
}
};
TokenMatching matching = new TokenMatching(textSearchRequestor, match);
matching.collectMatches(null, fullString, new NullProgressMonitor(), false);
return offsets;
}
}