/*******************************************************************************
* Distributed under license by Red Hat, Inc. All rights reserved.
* This program is made available under the terms of the
* Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
******************************************************************************/
package org.jboss.tools.seam.ui.search;
import java.util.ArrayList;
import java.util.regex.Pattern;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.ui.search.ISearchRequestor;
import org.eclipse.search.core.text.TextSearchMatchAccess;
import org.eclipse.search.internal.core.text.PatternConstructor;
import org.eclipse.search.internal.ui.Messages;
import org.eclipse.search.internal.ui.text.FileMatch;
import org.eclipse.search.internal.ui.text.LineElement;
import org.eclipse.search.internal.ui.text.SearchResultUpdater;
import org.eclipse.search.ui.ISearchQuery;
import org.eclipse.search.ui.ISearchResult;
import org.eclipse.search.ui.text.AbstractTextSearchResult;
import org.eclipse.search.ui.text.Match;
import org.jboss.tools.common.el.core.model.ELInvocationExpression;
import org.jboss.tools.common.java.IJavaSourceReference;
import org.jboss.tools.seam.core.ISeamDeclaration;
import org.jboss.tools.seam.core.ISeamProject;
import org.jboss.tools.seam.core.SeamCorePlugin;
import org.jboss.tools.seam.core.SeamCoreMessages;
import org.jboss.tools.seam.internal.core.el.SeamELCompletionEngine;
/**
* Seam search query implementation
*
* @author Jeremy
*/
public class SeamSearchQuery implements ISearchQuery {
/**
* Result collector is used be a holder for search results
*
* @author Jeremy
*/
public final static class SeamSearchResultCollector extends SeamSearchRequestor {
private final AbstractTextSearchResult fResult;
private ArrayList<Match> fCachedMatches;
private final ISearchRequestor fParentRequestor;
public SeamSearchResultCollector(AbstractTextSearchResult result, ISearchRequestor parentRequestor) {
fResult= result;
fParentRequestor = parentRequestor;
}
public boolean acceptFile(IFile file) throws CoreException {
flushMatches();
return true;
}
/* (non-Javadoc)
* @see org.eclipse.search.core.text.TextSearchRequestor#reportBinaryFile(org.eclipse.core.resources.IFile)
*/
public boolean reportBinaryFile(IFile file) {
return false;
}
public boolean acceptPatternMatch(TextSearchMatchAccess matchRequestor) throws CoreException {
int matchOffset= matchRequestor.getMatchOffset();
LineElement lineElement= getLineElement(matchOffset, matchRequestor);
if (lineElement != null)
fCachedMatches.add(new FileMatch(matchRequestor.getFile(), matchRequestor.getMatchOffset(), matchRequestor.getMatchLength(),lineElement));
return true;
}
private LineElement getLineElement(int offset, TextSearchMatchAccess matchRequestor) {
int lineNumber= 1;
int lineStart= 0;
if (!fCachedMatches.isEmpty()) {
// match on same line as last?
FileMatch last= (FileMatch) fCachedMatches.get(fCachedMatches.size() - 1);
LineElement lineElement= last.getLineElement();
if (lineElement.contains(offset)) {
return lineElement;
}
// start with the offset and line information from the last match
lineStart= lineElement.getOffset() + lineElement.getLength();
lineNumber= lineElement.getLine() + 1;
}
if (offset < lineStart) {
return null; // offset before the last line
}
int i= lineStart;
int contentLength= matchRequestor.getFileContentLength();
while (i < contentLength) {
char ch= matchRequestor.getFileContentChar(i++);
if (ch == '\n' || ch == '\r') {
if (ch == '\r' && i < contentLength && matchRequestor.getFileContentChar(i) == '\n') {
i++;
}
if (offset < i) {
String lineContent= getContents(matchRequestor, lineStart, i); // include line delimiter
return new LineElement(matchRequestor.getFile(), lineNumber, lineStart, lineContent);
}
lineNumber++;
lineStart= i;
}
}
if (offset < i) {
String lineContent= getContents(matchRequestor, lineStart, i); // until end of file
return new LineElement(matchRequestor.getFile(), lineNumber, lineStart, lineContent);
}
return null; // offset outside of range
}
private static String getContents(TextSearchMatchAccess matchRequestor, int start, int end) {
StringBuffer buf= new StringBuffer();
for (int i= start; i < end; i++) {
char ch= matchRequestor.getFileContentChar(i);
if (Character.isWhitespace(ch) || Character.isISOControl(ch)) {
buf.append(' ');
} else {
buf.append(ch);
}
}
return buf.toString();
}
public boolean acceptSeamDeclarationSourceReferenceMatch(IJavaSourceReference element) throws CoreException {
fCachedMatches.add(new SeamElementMatch(element));
return true;
}
public boolean acceptSeamDeclarationMatch(ISeamDeclaration element) throws CoreException {
fCachedMatches.add(new SeamElementMatch(element));
return true;
}
public void beginReporting() {
fCachedMatches= new ArrayList();
}
public void endReporting() {
flushMatches();
fCachedMatches= null;
}
private void flushMatches() {
if (!fCachedMatches.isEmpty()) {
if (fResult != null) fResult.addMatches((Match[]) fCachedMatches.toArray(new Match[fCachedMatches.size()]));
if (fParentRequestor != null) {
for (Match match : fCachedMatches) {
fParentRequestor.reportMatch(match);
}
}
fCachedMatches.clear();
}
}
public void reportMatch(Match match) {
fCachedMatches.add(match);
}
}
private ELInvocationExpression fTokens;
private IJavaElement[] fJavaElements;
private final SeamSearchScope fScope;
private SeamSearchResult fResult;
private IFile fSourceFile;
private ISearchRequestor fParentRequestor;
/**
* Constructs Seam search query for a given {@link ELInvocationExpression} objects list
*
* @param tokens
* @param sourceFile
* @param scope
*/
public SeamSearchQuery(ELInvocationExpression tokens, IFile sourceFile, SeamSearchScope scope) {
fTokens = tokens;
fJavaElements = null;
fSourceFile = sourceFile;
fScope= scope;
}
/**
* Constructs Seam search query for a given {@link IJavaElement} objects array
*
* @param javaElements
* @param sourceFile
* @param scope
*/
public SeamSearchQuery(IJavaElement[] javaElements, IFile sourceFile, SeamSearchScope scope) {
fTokens = null;
fJavaElements = javaElements;
fSourceFile = sourceFile;
fScope= scope;
}
/**
* Sets up a parent ISearchRequestor
*
* @param requestor
*/
public void setParentRequestor(ISearchRequestor requestor) {
this.fParentRequestor = requestor;
}
/**
* Returns parent requestor
*
* @return
*/
public ISearchRequestor getParentRequestor() {
return this.fParentRequestor;
}
/**
* Returns Seam Search Scope
*
* @return
*/
public SeamSearchScope getSearchScope() {
return fScope;
}
/**
* @Override
*/
public boolean canRunInBackground() {
return false;
}
/**
* @Override
*/
public IStatus run(final IProgressMonitor monitor) {
AbstractTextSearchResult textResult= (AbstractTextSearchResult) getSearchResult();
textResult.removeAll();
if (fJavaElements != null) {
return queryByJavaElements(textResult, monitor);
}
if (fTokens != null) {
return queryByTokens(textResult, monitor);
}
return Status.OK_STATUS;
}
private IStatus queryByTokens(AbstractTextSearchResult textResult,
final IProgressMonitor monitor) {
IProject project = (fSourceFile == null ? null : fSourceFile.getProject());
ISeamProject seamProject = SeamCorePlugin.getSeamProject(project, true);
if (seamProject == null)
return Status.OK_STATUS;
SeamELCompletionEngine engine = new SeamELCompletionEngine();
// List<IJavaElement> elements = engine.getJavaElementsForELOperandTokens(seamProject, fSourceFile, fTokens)
SeamSearchResultCollector collector= new SeamSearchResultCollector(textResult, getParentRequestor());
return SeamSearchEngine.getInstance().search(fScope, collector, fSourceFile, fTokens, monitor);
}
private IStatus queryByJavaElements(
AbstractTextSearchResult textResult,
final IProgressMonitor monitor) {
IProject project = (fSourceFile == null ? null : fSourceFile.getProject());
ISeamProject seamProject = SeamCorePlugin.getSeamProject(project, true);
if (seamProject == null)
return Status.OK_STATUS;
SeamELCompletionEngine engine = new SeamELCompletionEngine();
SeamSearchResultCollector collector= new SeamSearchResultCollector(textResult, getParentRequestor());
return SeamSearchEngine.getInstance().search(fScope, collector, fSourceFile, fJavaElements, monitor);
}
private boolean isScopeAllFileTypes() {
String[] fileNamePatterns= fScope.getFileNamePatterns();
if (fileNamePatterns == null)
return true;
for (int i= 0; i < fileNamePatterns.length; i++) {
if ("*".equals(fileNamePatterns[i])) { //$NON-NLS-1$
return true;
}
}
return false;
}
/**
* @see org.eclipse.search.ui.ISearchQuery#getLabel()
*/
public String getLabel() {
Object[] args= { fScope.getLimitToDescription() };
return Messages.format(SeamCoreMessages.SeamSearchQuery_label, args);
}
/**
* Returns Search String
* @return
*/
public String getSearchString() {
String searchString = "";
if (fJavaElements != null) {
StringBuffer buf= new StringBuffer();
for (int i= 0; i < fJavaElements.length; i++) {
if (i > 0) {
buf.append(", "); //$NON-NLS-1$
}
buf.append(fJavaElements[i]);
}
searchString = buf.toString();
} else if (fTokens != null) {
searchString = fTokens.getText();
}
return searchString;
}
/**
* Returns Search Result Label
*
* @param nMatches
* @return
*/
public String getResultLabel(int nMatches) {
String searchString= getSearchString();
if (searchString.length() > 0) {
if (SeamSearchEngine.isSearchForDeclarations(fScope.getLimitTo())) {
// search is limited to declarations only
if (nMatches == 1) {
Object[] args= { searchString, fScope.getDescription(), fScope.getLimitToDescription() };
return Messages.format(SeamCoreMessages.SeamSearchQuery_singularPatternWithLimitTo, args);
}
Object[] args= { searchString, new Integer(nMatches), fScope.getDescription(), fScope.getLimitToDescription() };
return Messages.format(SeamCoreMessages.SeamSearchQuery_pluralPatternWithLimitTo, args);
}
if (SeamSearchEngine.isSearchForReferences(fScope.getLimitTo())) {
// text search
if (isScopeAllFileTypes()) {
// search all file extensions
if (nMatches == 1) {
Object[] args= { searchString, fScope.getDescription(), fScope.getLimitToDescription() };
return Messages.format(SeamCoreMessages.SeamSearchQuery_singularLabel, args);
}
Object[] args= { searchString, new Integer(nMatches), fScope.getDescription(), fScope.getLimitToDescription() };
return Messages.format(SeamCoreMessages.SeamSearchQuery_pluralPattern, args);
}
}
}
return "";
}
/*
* returns a search pattern for a given name
*/
public static Pattern getSearchPattern(String variableName) {
return PatternConstructor.createPattern(variableName, true, false);
}
/*
* (non-Javadoc)
* @see org.eclipse.search.ui.ISearchQuery#canRerun()
*/
public boolean canRerun() {
return true;
}
/*
* (non-Javadoc)
* @see org.eclipse.search.ui.ISearchQuery#getSearchResult()
*/
public ISearchResult getSearchResult() {
if (fResult == null) {
fResult= new SeamSearchResult(this);
new SearchResultUpdater(fResult);
}
return fResult;
}
}