blob: f436f5cf95cc0e89462eb40a96676a1204eefafa [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 org.jetbrains.plugins.groovy.extensions;
import com.intellij.psi.*;
import com.intellij.psi.util.PropertyUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;
import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentLabel;
import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyPsiManager;
import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyResolveResultImpl;
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames;
/**
* @author Sergey Evdokimov
*/
public class NamedArgumentDescriptor {
public static final NamedArgumentDescriptor SIMPLE_ON_TOP = new UnmodifiableDescriptor(Priority.ALWAYS_ON_TOP);
public static final NamedArgumentDescriptor SIMPLE_AS_LOCAL_VAR = new UnmodifiableDescriptor(Priority.AS_LOCAL_VARIABLE);
public static final NamedArgumentDescriptor SIMPLE_NORMAL = new UnmodifiableDescriptor(Priority.NORMAL);
public static final NamedArgumentDescriptor SIMPLE_UNLIKELY = new UnmodifiableDescriptor(Priority.UNLIKELY);
public static final StringTypeConditionWithPriority TYPE_STRING = new StringTypeConditionWithPriority(CommonClassNames.JAVA_LANG_STRING);
public static final StringTypeConditionWithPriority TYPE_CLOSURE = new StringTypeConditionWithPriority(GroovyCommonClassNames.GROOVY_LANG_CLOSURE);
public static final StringTypeConditionWithPriority TYPE_MAP = new StringTypeConditionWithPriority(CommonClassNames.JAVA_UTIL_MAP);
public static final StringTypeConditionWithPriority TYPE_LIST = new StringTypeConditionWithPriority(CommonClassNames.JAVA_UTIL_LIST);
public static final StringTypeConditionWithPriority TYPE_BOOL = new StringTypeConditionWithPriority(CommonClassNames.JAVA_LANG_BOOLEAN);
public static final StringTypeConditionWithPriority TYPE_CLASS = new StringTypeConditionWithPriority(CommonClassNames.JAVA_LANG_CLASS);
public static final StringTypeConditionWithPriority TYPE_INTEGER = new StringTypeConditionWithPriority(CommonClassNames.JAVA_LANG_INTEGER);
private final PsiElement myNavigationElement;
private final PsiSubstitutor mySubstitutor;
private Priority myPriority = Priority.ALWAYS_ON_TOP;
public NamedArgumentDescriptor() {
this(null);
}
public NamedArgumentDescriptor(@Nullable PsiElement navigationElement) {
this(navigationElement, PsiSubstitutor.EMPTY);
}
public NamedArgumentDescriptor(@Nullable PsiElement navigationElement, PsiSubstitutor substitutor) {
myNavigationElement = navigationElement;
mySubstitutor = substitutor;
}
@NotNull
public Priority getPriority() {
return myPriority;
}
public NamedArgumentDescriptor setPriority(@NotNull Priority priority) {
myPriority = priority;
return this;
}
public boolean checkType(@NotNull PsiType type, @NotNull GroovyPsiElement context) {
return true;
}
@Nullable
public PsiPolyVariantReference createReference(@NotNull GrArgumentLabel element) {
final PsiElement navigationElement = getNavigationElement();
if (navigationElement == null) return null;
return new NamedArgumentReference(element, navigationElement, mySubstitutor);
}
@Nullable
public PsiElement getNavigationElement() {
return myNavigationElement;
}
public static class NamedArgumentReference extends PsiPolyVariantReferenceBase<GrArgumentLabel> {
private final PsiElement myNavigationElement;
private final PsiSubstitutor mySubstitutor;
public NamedArgumentReference(GrArgumentLabel element, @NotNull PsiElement navigationElement, PsiSubstitutor substitutor) {
super(element);
myNavigationElement = navigationElement;
mySubstitutor = substitutor;
}
@Override
public PsiElement resolve() {
return myNavigationElement;
}
@Override
public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException {
if (element == myNavigationElement) return getElement();
return super.bindToElement(element);
}
@Override
public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException {
final PsiElement resolved = resolve();
if (resolved instanceof PsiMethod) {
final PsiMethod method = (PsiMethod) resolved;
final String oldName = getElement().getName();
if (!method.getName().equals(oldName)) { //was property reference to accessor
if (PropertyUtil.isSimplePropertySetter(method)) {
final String newPropertyName = PropertyUtil.getPropertyName(newElementName);
if (newPropertyName != null) {
newElementName = newPropertyName;
}
}
}
}
return super.handleElementRename(newElementName);
}
@NotNull
@Override
public Object[] getVariants() {
return ArrayUtil.EMPTY_OBJECT_ARRAY;
}
@NotNull
@Override
public GroovyResolveResult[] multiResolve(boolean incompleteCode) {
return new GroovyResolveResult[]{new GroovyResolveResultImpl(myNavigationElement, null, null, mySubstitutor, true, true)};
}
}
public enum Priority {
ALWAYS_ON_TOP,
AS_LOCAL_VARIABLE,
NORMAL,
UNLIKELY
}
private static class StringTypeConditionWithPriority extends StringTypeCondition {
private final StringTypeConditionWithPriority[] myInstances;
public StringTypeConditionWithPriority(String typeName) {
this(typeName, Priority.ALWAYS_ON_TOP, new StringTypeConditionWithPriority[Priority.values().length]);
}
private StringTypeConditionWithPriority(String typeName, Priority priority, StringTypeConditionWithPriority[] instances) {
super(typeName);
myInstances = instances;
super.setPriority(priority);
instances[priority.ordinal()] = this;
}
public StringTypeConditionWithPriority withPriority(Priority priority) {
StringTypeConditionWithPriority res = myInstances[priority.ordinal()];
if (res == null) {
res = new StringTypeConditionWithPriority(myTypeName, priority, myInstances);
}
return res;
}
@Override
public NamedArgumentDescriptor setPriority(@NotNull Priority priority) {
throw new UnsupportedOperationException("Use withPriority(priority)");
}
}
public static class StringTypeCondition extends NamedArgumentDescriptor {
protected final String myTypeName;
public StringTypeCondition(String typeName) {
this(typeName, null);
}
public StringTypeCondition(String typeName, @Nullable PsiElement navigationElement) {
super(navigationElement);
myTypeName = typeName;
}
@Override
public boolean checkType(@NotNull PsiType type, @NotNull GroovyPsiElement context) {
return GroovyPsiManager.isInheritorCached(type, myTypeName);
}
}
public static class StringArrayTypeCondition extends NamedArgumentDescriptor {
private final String[] myTypeNames;
public StringArrayTypeCondition(String... typeNames) {
this(null, typeNames);
}
public StringArrayTypeCondition(@Nullable PsiElement navigationElement, String... typeNames) {
super(navigationElement);
this.myTypeNames = typeNames;
}
@Override
public boolean checkType(@NotNull PsiType type, @NotNull GroovyPsiElement context) {
for (String typeName : myTypeNames) {
if (GroovyPsiManager.isInheritorCached(type, typeName)) {
return true;
}
}
return false;
}
}
public static class TypeCondition extends NamedArgumentDescriptor {
private final PsiType myType;
public TypeCondition(@NotNull PsiType type) {
this(type, null, PsiSubstitutor.EMPTY);
}
public TypeCondition(@NotNull PsiType type, @Nullable PsiElement navigationElement) {
this(type, navigationElement, PsiSubstitutor.EMPTY);
}
public TypeCondition(PsiType type, PsiElement navigationElement, PsiSubstitutor substitutor) {
super(navigationElement, substitutor);
myType = type;
}
@Override
public boolean checkType(@NotNull PsiType type, @NotNull GroovyPsiElement context) {
return TypesUtil.isAssignable(myType, type, context);
}
}
private static class UnmodifiableDescriptor extends NamedArgumentDescriptor {
public UnmodifiableDescriptor(Priority priority) {
super.setPriority(priority);
}
@Override
public NamedArgumentDescriptor setPriority(@NotNull Priority priority) {
throw new UnsupportedOperationException();
}
}
}