package com.intellij.javascript.flex;
import com.intellij.lang.javascript.psi.*;
import com.intellij.lang.javascript.psi.ecmal4.JSClass;
import com.intellij.lang.javascript.psi.impl.JSLiteralExpressionImpl;
import com.intellij.lang.javascript.psi.resolve.JSResolveUtil;
import com.intellij.lang.javascript.psi.util.JSUtils;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
import com.intellij.psi.PsiReferenceProvider;
import com.intellij.util.ProcessingContext;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
import java.util.*;
/**
* @author yole
*/
public class FlexPropertyReferenceProvider extends PsiReferenceProvider {
private static final Set<String> ourMethodsWithPropertyReferences = new THashSet<>(
Arrays.asList("findResourceBundleWithResource", "getString", "getObject", "getClass", "getStringArray", "getNumber", "getInt",
"getUint", "getBoolean"));
@NotNull
@Override
public PsiReference[] getReferencesByElement(@NotNull PsiElement element, @NotNull ProcessingContext context) {
PsiElement parent = element.getParent();
JSReferenceExpression invokedMethod = JSUtils.getMethodNameIfInsideCall(parent);
List<PsiReference> result = new ArrayList<>();
if (invokedMethod != null) {
String invokedMethodName;
boolean justResourceBundleRef = false;
PsiElement qualifier;
if ((ourMethodsWithPropertyReferences.contains(invokedMethodName = (invokedMethod.getReferencedName())) ||
(justResourceBundleRef = "getResourceBundle".equals(invokedMethodName))) &&
((qualifier = invokedMethod.getQualifier()) instanceof JSReferenceExpression ||
(qualifier instanceof JSCallExpression &&
((JSCallExpression)qualifier).getMethodExpression() instanceof JSReferenceExpression))) {
final JSExpression[] args = ((JSArgumentList)parent).getArguments();
boolean propertyRef = false;
boolean bundleRef = false;
if (justResourceBundleRef) {
bundleRef = args.length > 1 && args[1] == element;
}
else {
propertyRef = args.length > 1 && args[1] == element;
bundleRef = args.length > 0 && args[0] == element;
if (bundleRef && args.length == 1) { // ResourceBundle.getString deprecated, without bundle name
bundleRef = false;
propertyRef = true;
}
}
boolean isSoft = true;
if (propertyRef || bundleRef) {
PsiElement resolved = invokedMethod.resolve();
if (resolved instanceof JSFunction) {
PsiElement parentClass = JSResolveUtil.findParent(resolved);
if (parentClass instanceof JSClass) {
String name = ((JSClass)parentClass).getName();
isSoft = name == null || (name.indexOf("ResourceManager") == -1 && name.indexOf("ResourceBundle") == -1);
}
}
}
if (propertyRef) {
FlexPropertiesSupport.PropertyReferenceInfoProvider<JSLiteralExpressionImpl> provider =
isSoft ? ourSoftPropertyInfoProvider : ourPropertyInfoProvider;
if (args.length > 1 && !isSoft) {
JSExpression bundleExpression = args[0];
if (bundleExpression instanceof JSReferenceExpression) {
PsiElement resolved = ((JSReferenceExpression)bundleExpression).resolve();
if (resolved instanceof JSVariable) {
bundleExpression = ((JSVariable)resolved).getInitializer();
}
}
if (bundleExpression instanceof JSLiteralExpression) {
final Object expressionValue = ((JSLiteralExpression)bundleExpression).getValue();
if (expressionValue instanceof String) {
provider = new FlexPropertiesSupport.PropertyReferenceInfoProvider<JSLiteralExpressionImpl>() {
public TextRange getReferenceRange(JSLiteralExpressionImpl element) {
return getValueRange(element);
}
public String getBundleName(JSLiteralExpressionImpl element) {
return (String)expressionValue;
}
public boolean isSoft(JSLiteralExpressionImpl element) {
return false;
}
};
}
}
}
Collections.addAll(result, FlexPropertiesSupport.getPropertyReferences((JSLiteralExpressionImpl)element, provider));
}
else if (bundleRef) {
PsiReference[] reference = FlexPropertiesSupport.getResourceBundleReference((JSLiteralExpressionImpl)element,
isSoft
? ourSoftBundleInfoProvider
: ourBundleInfoProvider);
Collections.addAll(result, reference);
}
}
}
return result.toArray(new PsiReference[result.size()]);
}
private static final FlexPropertiesSupport.PropertyReferenceInfoProvider<JSLiteralExpressionImpl> ourPropertyInfoProvider =
new FlexPropertiesSupport.PropertyReferenceInfoProvider<JSLiteralExpressionImpl>() {
public TextRange getReferenceRange(JSLiteralExpressionImpl element) {
return getValueRange(element);
}
public String getBundleName(JSLiteralExpressionImpl element) {
return null;
}
public boolean isSoft(JSLiteralExpressionImpl element) {
return false;
}
};
private static final FlexPropertiesSupport.PropertyReferenceInfoProvider<JSLiteralExpressionImpl> ourSoftPropertyInfoProvider =
new FlexPropertiesSupport.PropertyReferenceInfoProvider<JSLiteralExpressionImpl>() {
public TextRange getReferenceRange(JSLiteralExpressionImpl element) {
return getValueRange(element);
}
public String getBundleName(JSLiteralExpressionImpl element) {
return null;
}
public boolean isSoft(JSLiteralExpressionImpl element) {
return true;
}
};
private static final FlexPropertiesSupport.BundleReferenceInfoProvider<JSLiteralExpressionImpl> ourBundleInfoProvider =
new FlexPropertiesSupport.BundleReferenceInfoProvider<JSLiteralExpressionImpl>() {
public TextRange getReferenceRange(JSLiteralExpressionImpl element) {
return getValueRange(element);
}
public boolean isSoft(JSLiteralExpressionImpl element) {
return false;
}
};
private static final FlexPropertiesSupport.BundleReferenceInfoProvider<JSLiteralExpressionImpl> ourSoftBundleInfoProvider =
new FlexPropertiesSupport.BundleReferenceInfoProvider<JSLiteralExpressionImpl>() {
public TextRange getReferenceRange(JSLiteralExpressionImpl element) {
return getValueRange(element);
}
public boolean isSoft(JSLiteralExpressionImpl element) {
return true;
}
};
private static TextRange getValueRange(JSLiteralExpressionImpl element) {
int textLength = element.getTextLength();
if (textLength < 2) return null;
return new TextRange(1, textLength - 1);
}
}