blob: 90eb7c9f9cfb5d44192ed7dc7421223c0dc45e3f [file] [log] [blame]
/*
* Copyright 2006 Sascha Weinreuter
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.intellij.plugins.xpathView.search;
import com.intellij.find.FindBundle;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.FileViewProvider;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.templateLanguages.TemplateLanguageFileViewProvider;
import com.intellij.psi.xml.XmlDocument;
import com.intellij.psi.xml.XmlFile;
import com.intellij.usageView.UsageInfo;
import com.intellij.usages.Usage;
import com.intellij.usages.UsageInfo2UsageAdapter;
import com.intellij.usages.UsageSearcher;
import com.intellij.util.Processor;
import org.intellij.plugins.xpathView.HistoryElement;
import org.intellij.plugins.xpathView.support.XPathSupport;
import org.intellij.plugins.xpathView.util.CachedVariableContext;
import org.jaxen.Context;
import org.jaxen.ContextSupport;
import org.jaxen.JaxenException;
import org.jaxen.XPath;
import org.jaxen.pattern.Pattern;
import org.jaxen.pattern.PatternParser;
import org.jaxen.saxpath.SAXPathException;
import org.jetbrains.annotations.NotNull;
import java.util.List;
class XPathUsageSearcher implements UsageSearcher {
private final ProgressIndicator myIndicator;
private final PsiManager myManager;
private final HistoryElement myExpression;
private final Project myProject;
private final SearchScope myScope;
private final boolean myMatchRecursively;
private final XPathSupport mySupport;
public XPathUsageSearcher(Project project, HistoryElement expression, SearchScope scope, boolean matchRecursively) {
myExpression = expression;
myProject = project;
myScope = scope;
myMatchRecursively = matchRecursively && !expression.expression.trim().startsWith("//");
mySupport = XPathSupport.getInstance();
myIndicator = ProgressManager.getInstance().getProgressIndicator();
myManager = PsiManager.getInstance(myProject);
}
public void generate(@NotNull final Processor<Usage> processor) {
Runnable runnable = new Runnable() {
public void run() {
myIndicator.setIndeterminate(true);
myIndicator.setText2(findBundleMessage("find.searching.for.string.in.file.occurrences.progress", 0));
final CountProcessor counter = new CountProcessor();
myScope.iterateContent(myProject, counter);
myIndicator.setIndeterminate(false);
myIndicator.setFraction(0);
myScope.iterateContent(myProject, new MyProcessor(processor, counter.getFileCount()));
}
};
ApplicationManager.getApplication().runReadAction(runnable);
}
private class MyProcessor extends BaseProcessor {
private final Processor<Usage> myProcessor;
private final int myTotalFileCount;
private int myFileCount;
private int myMatchCount;
public MyProcessor(Processor<Usage> processor, int fileCount) {
myProcessor = processor;
myTotalFileCount = fileCount;
}
protected void processXmlFile(VirtualFile t) {
myIndicator.setText(findBundleMessage("find.searching.for.string.in.file.progress", myExpression.expression, t.getPresentableUrl()));
final PsiFile psiFile = myManager.findFile(t);
if (psiFile instanceof XmlFile) {
final XmlFile t1 = (XmlFile)psiFile;
final XmlDocument document;
FileViewProvider fileViewProvider = t1.getViewProvider();
if (fileViewProvider instanceof TemplateLanguageFileViewProvider) {
final PsiFile root = fileViewProvider.getPsi(((TemplateLanguageFileViewProvider)fileViewProvider).getTemplateDataLanguage());
if (root instanceof XmlFile) {
document = ((XmlFile)root).getDocument();
} else {
document = null;
}
} else {
document = t1.getDocument();
}
if (document != null) {
process(document);
}
}
myIndicator.setFraction(++myFileCount / (double)myTotalFileCount);
}
private void process(XmlDocument t) {
try {
final XmlFile psiFile = (XmlFile)t.getContainingFile();
final XPath searchPath;
final Pattern pattern;
final Context context;
if (myMatchRecursively) {
searchPath = mySupport.createXPath(psiFile, "//*");
searchPath.setVariableContext(new CachedVariableContext(myExpression.variables, searchPath, t));
pattern = PatternParser.parse(myExpression.expression);
final ContextSupport support = new ContextSupport(searchPath.getNamespaceContext(), searchPath.getFunctionContext(), searchPath.getVariableContext(), searchPath.getNavigator());
context = new Context(support);
} else {
searchPath = mySupport.createXPath(psiFile, myExpression.expression, myExpression.namespaces);
searchPath.setVariableContext(new CachedVariableContext(myExpression.variables, searchPath, t));
pattern = null;
context = null;
}
final Object o = searchPath.evaluate(t);
if (o instanceof List) {
//noinspection unchecked
final List<PsiElement> list = ((List<PsiElement>)o);
for (PsiElement psiElement : list) {
myIndicator.checkCanceled();
if (myMatchRecursively) {
if (pattern.matches(psiElement, context)) {
matchFound();
myProcessor.process(new UsageInfo2UsageAdapter(new UsageInfo(psiElement)));
}
} else {
matchFound();
myProcessor.process(new UsageInfo2UsageAdapter(new UsageInfo(psiElement)));
}
}
} else if (Boolean.TRUE.equals(o)) {
matchFound();
myProcessor.process(new UsageInfo2UsageAdapter(new UsageInfo(psiFile)));
} else if (o instanceof Number) {
if (((Number)o).intValue() != 0) {
matchFound();
myProcessor.process(new UsageInfo2UsageAdapter(new UsageInfo(psiFile)));
}
} else if (o instanceof String) {
if (((String)o).length() > 0) {
matchFound();
myProcessor.process(new UsageInfo2UsageAdapter(new UsageInfo(psiFile)));
}
}
} catch (JaxenException e) {
Messages.showErrorDialog(myProject, "Error while evaluating XPath:\n" + e.getMessage(), "XPath Error");
} catch (SAXPathException e) {
Logger.getInstance(getClass().getName()).error(e);
}
}
private void matchFound() {
myIndicator.setText2(findBundleMessage("find.searching.for.string.in.file.occurrences.progress", ++myMatchCount));
}
}
private static String findBundleMessage(String s, Object... args) {
return FindBundle.message(s, args);
}
static class CountProcessor extends BaseProcessor {
private int myFileCount;
protected void processXmlFile(VirtualFile t) {
myFileCount++;
}
public int getFileCount() {
return myFileCount;
}
}
}