blob: 9375561d92e19a12b83b80a5e09290d045a5d287 [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.psi.impl.search;
import com.intellij.find.findUsages.FindUsagesOptions;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.psi.*;
import com.intellij.psi.search.searches.MethodReferencesSearch;
import com.intellij.psi.util.PsiFormatUtil;
import com.intellij.psi.util.PsiFormatUtilBase;
import com.intellij.usageView.UsageInfo;
import com.intellij.util.Processor;
import com.intellij.util.containers.HashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Set;
/**
* Author: msk
*/
public class ThrowSearchUtil {
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.search.ThrowSearchUtil");
private ThrowSearchUtil() {
}
public static class Root {
@NotNull private final PsiElement myElement;
@NotNull private final PsiType myType;
private final boolean isExact;
public Root(@NotNull PsiElement root, @NotNull PsiType type, final boolean exact) {
myElement = root;
myType = type;
isExact = exact;
}
@Override
public String toString() {
return PsiFormatUtil.formatType(myType, PsiFormatUtilBase.SHOW_FQ_CLASS_NAMES, PsiSubstitutor.EMPTY);
}
}
/**
* @return true, if we should continue processing
*/
private static boolean processExn(@NotNull PsiParameter aCatch, @NotNull Processor<UsageInfo> processor, @NotNull Root root) {
final PsiType type = aCatch.getType();
if (type.isAssignableFrom(root.myType)) {
processor.process(new UsageInfo(aCatch));
return false;
}
if (!root.isExact && root.myType.isAssignableFrom(type)) {
processor.process(new UsageInfo(aCatch));
return true;
}
return true;
}
private static boolean scanCatches(@NotNull PsiElement elem,
@NotNull Processor<UsageInfo> processor,
@NotNull Root root,
@NotNull FindUsagesOptions options,
@NotNull Set<PsiMethod> processed) {
while (elem != null) {
final PsiElement parent = elem.getParent();
if (elem instanceof PsiMethod) {
final PsiMethod deepestSuperMethod = ((PsiMethod)elem).findDeepestSuperMethod();
final PsiMethod method = deepestSuperMethod != null ? deepestSuperMethod : (PsiMethod)elem;
if (!processed.contains(method)) {
processed.add(method);
final PsiReference[] refs = MethodReferencesSearch.search(method, options.searchScope, true).toArray(PsiReference.EMPTY_ARRAY);
for (int i = 0; i != refs.length; ++i) {
if (!scanCatches(refs[i].getElement(), processor, root, options, processed)) return false;
}
}
return true;
}
if (elem instanceof PsiTryStatement) {
final PsiTryStatement aTry = (PsiTryStatement)elem;
final PsiParameter[] catches = aTry.getCatchBlockParameters();
for (int i = 0; i != catches.length; ++i) {
if (!processExn(catches[i], processor, root)) {
return false;
}
}
}
else if (parent instanceof PsiTryStatement) {
final PsiTryStatement tryStmt = (PsiTryStatement)parent;
if (elem != tryStmt.getTryBlock()) {
elem = parent.getParent();
continue;
}
}
elem = parent;
}
return true;
}
public static boolean addThrowUsages(@NotNull Processor<UsageInfo> processor, @NotNull Root root, @NotNull FindUsagesOptions options) {
Set<PsiMethod> processed = new HashSet<PsiMethod>();
return scanCatches(root.myElement, processor, root, options, processed);
}
/**
* @return is type of exn exactly known
*/
private static boolean isExactExnType(final PsiExpression exn) {
return exn instanceof PsiNewExpression;
}
@Nullable
public static Root[] getSearchRoots(final PsiElement element) {
if (element instanceof PsiThrowStatement) {
final PsiThrowStatement aThrow = (PsiThrowStatement)element;
final PsiExpression exn = aThrow.getException();
return new Root[]{new Root(aThrow.getParent(), exn.getType(), isExactExnType(exn))};
}
if (element instanceof PsiKeyword) {
final PsiKeyword kwd = (PsiKeyword)element;
if (PsiKeyword.THROWS.equals(kwd.getText())) {
final PsiElement parent = kwd.getParent();
if (parent != null && parent.getParent() instanceof PsiMethod) {
final PsiMethod method = (PsiMethod)parent.getParent();
final PsiReferenceList throwsList = method.getThrowsList();
final PsiClassType[] exns = throwsList.getReferencedTypes();
final Root[] roots = new Root[exns.length];
for (int i = 0; i != roots.length; ++i) {
final PsiClassType exn = exns[i];
roots[i] = new Root(method, exn, false); // TODO: test for final
}
return roots;
}
}
}
return null;
}
public static boolean isSearchable(final PsiElement element) {
return getSearchRoots(element) != null;
}
public static String getSearchableTypeName(final PsiElement e) {
if (e instanceof PsiThrowStatement) {
final PsiThrowStatement aThrow = (PsiThrowStatement)e;
final PsiType type = aThrow.getException().getType();
return PsiFormatUtil.formatType(type, PsiFormatUtilBase.SHOW_FQ_CLASS_NAMES, PsiSubstitutor.EMPTY);
}
if (e instanceof PsiKeyword && PsiKeyword.THROWS.equals(e.getText())) {
return e.getParent().getText();
}
LOG.error("invalid searchable element");
return e.getText();
}
}