blob: d40be21e178600eff81f11dfca667fa59a26e59b [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* 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 com.intellij.uiDesigner.binding;
import com.intellij.lang.properties.IProperty;
import com.intellij.lang.properties.psi.PropertiesFile;
import com.intellij.lang.properties.psi.Property;
import com.intellij.openapi.application.AccessToken;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.NullableComputable;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.impl.cache.CacheManager;
import com.intellij.psi.impl.search.PsiSearchHelperImpl;
import com.intellij.psi.search.*;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.CommonProcessors;
import com.intellij.util.Processor;
import com.intellij.util.QueryExecutor;
import com.intellij.util.text.CharArrayUtil;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.List;
/**
* @author max
*/
public class FormReferencesSearcher implements QueryExecutor<PsiReference, ReferencesSearch.SearchParameters> {
@Override
public boolean execute(@NotNull final ReferencesSearch.SearchParameters p, @NotNull final Processor<PsiReference> consumer) {
if (!scopeCanContainForms(p.getScope())) return true;
final PsiElement refElement = p.getElementToSearch();
final PsiFile psiFile = ApplicationManager.getApplication().runReadAction(new Computable<PsiFile>() {
@Override
public PsiFile compute() {
if (!refElement.isValid()) return null;
return refElement.getContainingFile();
}
});
if (psiFile == null) return true;
final VirtualFile virtualFile = psiFile.getVirtualFile();
if (virtualFile == null) return true;
Project project = ApplicationManager.getApplication().runReadAction(new Computable<Project>() {
@Override
public Project compute() {
return psiFile.getProject();
}
});
Module module = ProjectRootManager.getInstance(project).getFileIndex().getModuleForFile(virtualFile);
if (module == null) return true;
final GlobalSearchScope scope = GlobalSearchScope.moduleWithDependenciesScope(module);
final LocalSearchScope filterScope = p.getScope() instanceof LocalSearchScope
? (LocalSearchScope) p.getScope()
: null;
PsiManager psiManager = PsiManager.getInstance(project);
if (refElement instanceof PsiPackage) {
//no need to do anything
//if (!UIFormUtil.processReferencesInUIForms(consumer, (PsiPackage)refElement, scope)) return false;
}
else if (refElement instanceof PsiClass) {
if (!processReferencesInUIForms(consumer, psiManager,(PsiClass)refElement, scope, filterScope)) return false;
}
else if (refElement instanceof PsiEnumConstant) {
if (!processReferencesInUIForms(consumer, psiManager, (PsiEnumConstant)refElement, scope, filterScope)) return false;
}
else if (refElement instanceof PsiField) {
if (!processReferencesInUIForms(consumer, psiManager, (PsiField)refElement, scope, filterScope)) return false;
}
else if (refElement instanceof IProperty) {
if (!processReferencesInUIForms(consumer, psiManager, (Property)refElement, scope, filterScope)) return false;
}
else if (refElement instanceof PropertiesFile) {
if (!processReferencesInUIForms(consumer, psiManager, (PropertiesFile)refElement, scope, filterScope)) return false;
}
return true;
}
private static boolean scopeCanContainForms(SearchScope scope) {
if (!(scope instanceof LocalSearchScope)) return true;
LocalSearchScope localSearchScope = (LocalSearchScope) scope;
final PsiElement[] elements = localSearchScope.getScope();
for (final PsiElement element : elements) {
if (element instanceof PsiDirectory) return true;
boolean isForm = ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() {
@Override
public Boolean compute() {
PsiFile file;
if (element instanceof PsiFile) {
file = (PsiFile)element;
}
else {
if (!element.isValid()) return false;
file = element.getContainingFile();
}
return file.getFileType() == StdFileTypes.GUI_DESIGNER_FORM;
}
});
if (isForm) return true;
}
return false;
}
private static boolean processReferencesInUIForms(Processor<PsiReference> processor,
PsiManager psiManager, final PsiClass aClass,
GlobalSearchScope scope, final LocalSearchScope filterScope) {
String className = getQualifiedName(aClass);
return className == null || processReferencesInUIFormsInner(className, aClass, processor, scope, psiManager, filterScope);
}
public static String getQualifiedName(final PsiClass aClass) {
return ApplicationManager.getApplication().runReadAction(new Computable<String>() {
@Override
public String compute() {
if (!aClass.isValid()) return null;
return aClass.getQualifiedName();
}
});
}
private static boolean processReferencesInUIForms(Processor<PsiReference> processor,
PsiManager psiManager, final PsiEnumConstant enumConstant,
GlobalSearchScope scope, final LocalSearchScope filterScope) {
String className = ApplicationManager.getApplication().runReadAction(new Computable<String>() {
@Override
public String compute() {
return enumConstant.getName();
}
});
return className == null || processReferencesInUIFormsInner(className, enumConstant, processor, scope, psiManager, filterScope);
}
private static boolean processReferencesInUIFormsInner(String name,
PsiElement element,
Processor<PsiReference> processor,
GlobalSearchScope scope1,
PsiManager manager,
final LocalSearchScope filterScope) {
GlobalSearchScope scope = GlobalSearchScope.projectScope(manager.getProject()).intersectWith(scope1);
List<PsiFile> files = FormClassIndex.findFormsBoundToClass(manager.getProject(), name, scope);
return processReferencesInFiles(files, manager, name, element, filterScope, processor);
}
private static boolean processReferencesInUIForms(Processor<PsiReference> processor,
PsiManager psiManager,
PsiField field,
GlobalSearchScope scope1,
LocalSearchScope filterScope) {
GlobalSearchScope scope = GlobalSearchScope.projectScope(psiManager.getProject()).intersectWith(scope1);
final AccessToken token = ReadAction.start();
PsiClass containingClass = field.getContainingClass();
if (containingClass == null) return true;
String fieldName;
try {
fieldName = field.getName();
}
finally {
token.finish();
}
final List<PsiFile> files = FormClassIndex.findFormsBoundToClass(psiManager.getProject(), containingClass, scope);
return processReferencesInFiles(files, psiManager, fieldName, field, filterScope, processor);
}
private static boolean processReferences(final Processor<PsiReference> processor,
final PsiFile file,
String name,
final PsiElement element,
final LocalSearchScope filterScope) {
CharSequence chars = ApplicationManager.getApplication().runReadAction(new NullableComputable<CharSequence>() {
@Override
public CharSequence compute() {
if (filterScope != null) {
boolean isInScope = false;
for(PsiElement filterElement: filterScope.getScope()) {
if (PsiTreeUtil.isAncestor(filterElement, file, false)) {
isInScope = true;
break;
}
}
if (!isInScope) return null;
}
return file.getViewProvider().getContents();
}});
if (chars == null) return true;
int index = 0;
final int offset = name.lastIndexOf('.');
while(true){
index = CharArrayUtil.indexOf(chars, name, index);
if (index < 0) break;
final int finalIndex = index;
final Boolean searchDone = ApplicationManager.getApplication().runReadAction(new NullableComputable<Boolean>() {
@Override
public Boolean compute() {
final PsiReference ref = file.findReferenceAt(finalIndex + offset + 1);
if (ref != null && ref.isReferenceTo(element)) {
return processor.process(ref);
}
return true;
}
});
if (!searchDone.booleanValue()) return false;
index++;
}
return true;
}
private static boolean processReferencesInUIForms(final Processor<PsiReference> processor,
PsiManager psiManager,
final Property property,
final GlobalSearchScope globalSearchScope,
final LocalSearchScope filterScope) {
final Project project = psiManager.getProject();
final GlobalSearchScope scope = GlobalSearchScope.projectScope(project).intersectWith(globalSearchScope);
String name = ApplicationManager.getApplication().runReadAction(new Computable<String>() {
@Override
public String compute() {
return property.getName();
}
});
if (name == null) return true;
psiManager.startBatchFilesProcessingMode();
try {
CommonProcessors.CollectProcessor<VirtualFile> collector = new CommonProcessors.CollectProcessor<VirtualFile>() {
@Override
protected boolean accept(VirtualFile virtualFile) {
return virtualFile.getFileType() == StdFileTypes.GUI_DESIGNER_FORM;
}
};
((PsiSearchHelperImpl)PsiSearchHelper.SERVICE.getInstance(project)).processFilesWithText(
scope, UsageSearchContext.IN_PLAIN_TEXT, true, name, collector
);
for (final VirtualFile vfile:collector.getResults()) {
ProgressManager.checkCanceled();
PsiFile file = ApplicationManager.getApplication().runReadAction(new Computable<PsiFile>() {
@Override
public PsiFile compute() {
return PsiManager.getInstance(project).findFile(vfile);
}
});
if (!processReferences(processor, file, name, property, filterScope)) return false;
}
}
finally {
psiManager.finishBatchFilesProcessingMode();
}
return true;
}
private static boolean processReferencesInUIForms(final Processor<PsiReference> processor,
PsiManager psiManager,
final PropertiesFile propFile,
final GlobalSearchScope globalSearchScope,
final LocalSearchScope filterScope) {
final Project project = psiManager.getProject();
GlobalSearchScope scope = GlobalSearchScope.projectScope(project).intersectWith(globalSearchScope);
final String baseName = ApplicationManager.getApplication().runReadAction(new Computable<String>() {
@Override
public String compute() {
return propFile.getResourceBundle().getBaseName();
}
});
PsiFile containingFile = ApplicationManager.getApplication().runReadAction(new Computable<PsiFile>() {
@Override
public PsiFile compute() {
return propFile.getContainingFile();
}
});
List<PsiFile> files = Arrays.asList(CacheManager.SERVICE.getInstance(project).getFilesWithWord(baseName, UsageSearchContext.IN_PLAIN_TEXT, scope, true));
return processReferencesInFiles(files, psiManager, baseName, containingFile, filterScope, processor);
}
private static boolean processReferencesInFiles(List<PsiFile> files,
PsiManager psiManager, String baseName,
PsiElement element,
LocalSearchScope filterScope,
Processor<PsiReference> processor) {
psiManager.startBatchFilesProcessingMode();
try {
for (PsiFile file : files) {
ProgressManager.checkCanceled();
if (file.getFileType() != StdFileTypes.GUI_DESIGNER_FORM) continue;
if (!processReferences(processor, file, baseName, element, filterScope)) return false;
}
}
finally {
psiManager.finishBatchFilesProcessingMode();
}
return true;
}
}