blob: d917c154c6d548f28cb231a120dd584b01de6f52 [file] [log] [blame]
/*
* Copyright 2000-2009 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.util.xml.converters;
import com.intellij.codeInsight.CodeInsightBundle;
import com.intellij.ide.IdeBundle;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.*;
import com.intellij.util.CommonProcessors;
import com.intellij.util.Function;
import com.intellij.util.Processor;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.xml.ConvertContext;
import com.intellij.util.xml.DomElement;
import com.intellij.util.xml.GenericDomValue;
import com.intellij.util.xml.ResolvingConverter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
/**
* @author peter
*/
public abstract class AbstractMethodResolveConverter<ParentType extends DomElement> extends ResolvingConverter<PsiMethod> {
public static final String ALL_METHODS = "*";
private final Class<ParentType> myDomMethodClass;
protected AbstractMethodResolveConverter(final Class<ParentType> domMethodClass) {
myDomMethodClass = domMethodClass;
}
@NotNull
protected abstract Collection<PsiClass> getPsiClasses(final ParentType parent, final ConvertContext context);
@Nullable
protected abstract AbstractMethodParams getMethodParams(@NotNull ParentType parent);
public void bindReference(final GenericDomValue<PsiMethod> genericValue, final ConvertContext context, final PsiElement element) {
assert element instanceof PsiMethod : "PsiMethod expected";
final PsiMethod psiMethod = (PsiMethod)element;
final ParentType parent = getParent(context);
final AbstractMethodParams methodParams = getMethodParams(parent);
genericValue.setStringValue(psiMethod.getName());
if (methodParams != null) {
methodParams.undefine();
for (PsiParameter parameter : psiMethod.getParameterList().getParameters()) {
methodParams.addMethodParam().setValue(parameter.getType());
}
}
}
public String getErrorMessage(final String s, final ConvertContext context) {
final ParentType parent = getParent(context);
return CodeInsightBundle
.message("error.cannot.resolve.0.1", IdeBundle.message("element.method"), getReferenceCanonicalText(s, getMethodParams(parent)));
}
@NotNull
protected final ParentType getParent(final ConvertContext context) {
ParentType parent = context.getInvocationElement().getParentOfType(myDomMethodClass, true);
assert parent != null: "Can't get parent of type " + myDomMethodClass + " for " + context.getInvocationElement();
return parent;
}
public boolean isReferenceTo(@NotNull final PsiElement element, final String stringValue, final PsiMethod resolveResult,
final ConvertContext context) {
if (super.isReferenceTo(element, stringValue, resolveResult, context)) return true;
final Ref<Boolean> result = new Ref<Boolean>(Boolean.FALSE);
processMethods(context, new Processor<PsiMethod>() {
public boolean process(final PsiMethod method) {
if (method.equals(element)) {
result.set(Boolean.TRUE);
return false;
}
return true;
}
}, new Function<PsiClass, PsiMethod[]>() {
public PsiMethod[] fun(final PsiClass s) {
return s.findMethodsByName(stringValue, true);
}
});
return result.get();
}
@SuppressWarnings({"WeakerAccess"})
protected void processMethods(final ConvertContext context, Processor<PsiMethod> processor, Function<PsiClass, PsiMethod[]> methodGetter) {
for (PsiClass psiClass : getPsiClasses(getParent(context), context)) {
if (psiClass != null) {
for (PsiMethod psiMethod : methodGetter.fun(psiClass)) {
if (!processor.process(psiMethod)) {
return;
}
}
}
}
}
@NotNull
public Collection<? extends PsiMethod> getVariants(final ConvertContext context) {
LinkedHashSet<PsiMethod> methodList = new LinkedHashSet<PsiMethod>();
Processor<PsiMethod> processor = CommonProcessors.notNullProcessor(new CommonProcessors.CollectProcessor<PsiMethod>(methodList));
processMethods(context, processor, new Function<PsiClass, PsiMethod[]>() {
public PsiMethod[] fun(final PsiClass s) {
final List<PsiMethod> list = ContainerUtil.findAll(getVariants(s), new Condition<PsiMethod>() {
public boolean value(final PsiMethod object) {
return acceptMethod(object, context);
}
});
return list.toArray(new PsiMethod[list.size()]);
}
});
return methodList;
}
protected Collection<PsiMethod> getVariants(final PsiClass s) {
return Arrays.asList(s.getAllMethods());
}
protected boolean acceptMethod(final PsiMethod psiMethod, final ConvertContext context) {
return methodSuits(psiMethod);
}
public static boolean methodSuits(final PsiMethod psiMethod) {
if (psiMethod.isConstructor()) return false;
return psiMethod.getContainingClass().isInterface() || (!psiMethod.hasModifierProperty(PsiModifier.FINAL) && !psiMethod.hasModifierProperty(PsiModifier.STATIC));
}
public Set<String> getAdditionalVariants() {
return Collections.singleton(ALL_METHODS);
}
public PsiMethod fromString(final String methodName, final ConvertContext context) {
final CommonProcessors.FindFirstProcessor<PsiMethod> processor = new CommonProcessors.FindFirstProcessor<PsiMethod>();
processMethods(context, processor, new Function<PsiClass, PsiMethod[]>() {
public PsiMethod[] fun(final PsiClass s) {
final PsiMethod method = findMethod(s, methodName, getMethodParams(getParent(context)));
if (method != null && acceptMethod(method, context)) {
return new PsiMethod[]{method};
}
return PsiMethod.EMPTY_ARRAY;
}
});
if (processor.isFound()) return processor.getFoundValue();
processMethods(context, processor, new Function<PsiClass, PsiMethod[]>() {
public PsiMethod[] fun(final PsiClass s) {
return s.findMethodsByName(methodName, true);
}
});
return processor.getFoundValue();
}
public String toString(final PsiMethod method, final ConvertContext context) {
return method.getName();
}
public static String getReferenceCanonicalText(final String name, @Nullable final AbstractMethodParams methodParams) {
StringBuilder sb = new StringBuilder(name);
if (methodParams == null) {
sb.append("()");
}
else if (methodParams.getXmlTag() != null) {
sb.append("(");
final List<GenericDomValue<PsiType>> list = methodParams.getMethodParams();
boolean first = true;
for (GenericDomValue<PsiType> value : list) {
if (first) first = false;
else sb.append(", ");
sb.append(value.getStringValue());
}
sb.append(")");
}
return sb.toString();
}
@Nullable
public static PsiMethod findMethod(final PsiClass psiClass, final String methodName, @Nullable final AbstractMethodParams methodParameters) {
if (psiClass == null || methodName == null) return null;
return ContainerUtil.find(psiClass.findMethodsByName(methodName, true), new Condition<PsiMethod>() {
public boolean value(final PsiMethod object) {
return methodParamsMatchSignature(methodParameters, object);
}
});
}
public static boolean methodParamsMatchSignature(@Nullable final AbstractMethodParams params, final PsiMethod psiMethod) {
if (params != null && params.getXmlTag() == null) return true;
PsiParameter[] parameters = psiMethod.getParameterList().getParameters();
if (params == null) return parameters.length == 0;
List<GenericDomValue<PsiType>> methodParams = params.getMethodParams();
if (methodParams.size() != parameters.length) return false;
for (int i = 0; i < parameters.length; i++) {
if (!Comparing.equal(parameters[i].getType(), methodParams.get(i).getValue())) {
return false;
}
}
return true;
}
}