package com.google.jstestdriver.idea.util;
import com.google.common.collect.Lists;
import com.intellij.lang.ASTNode;
import com.intellij.lang.javascript.JSTokenTypes;
import com.intellij.lang.javascript.psi.*;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.tree.LeafPsiElement;
import com.intellij.psi.tree.IElementType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public class JsPsiUtils {
private static final JSProperty[] EMPTY_ARRAY = new JSProperty[] {};
private JsPsiUtils() {}
@Nullable
public static String extractStringValue(@Nullable JSExpression jsExpression) {
if (jsExpression == null) {
return null;
}
{
JSLiteralExpression jsLiteralExpression = CastUtils.tryCast(jsExpression, JSLiteralExpression.class);
if (jsLiteralExpression != null && jsLiteralExpression.isQuotedLiteral()) {
return StringUtil.stripQuotesAroundValue(StringUtil.notNullize(jsLiteralExpression.getText()));
}
}
{
JSBinaryExpression jsBinaryExpression = CastUtils.tryCast(jsExpression, JSBinaryExpression.class);
if (jsBinaryExpression != null) {
IElementType operationType = jsBinaryExpression.getOperationSign();
if (operationType == JSTokenTypes.PLUS) {
String lOperand = extractStringValue(jsBinaryExpression.getLOperand());
String rOperand = extractStringValue(jsBinaryExpression.getROperand());
if (lOperand != null && rOperand != null) {
return lOperand + rOperand;
}
}
}
}
{
JSReferenceExpression jsReferenceExpression = CastUtils.tryCast(jsExpression, JSReferenceExpression.class);
if (jsReferenceExpression != null) {
JSExpression initializer = extractInitExpression(jsReferenceExpression);
if (initializer != null) {
return extractStringValue(initializer);
}
}
}
return null;
}
@Nullable
private static JSExpression extractInitExpression(@NotNull JSReferenceExpression jsReferenceExpression) {
PsiElement resolved = resolveUniquely(jsReferenceExpression);
if (resolved == null) {
return null;
}
JSVariable jsVariable = CastUtils.tryCast(resolved, JSVariable.class);
if (jsVariable != null) {
return jsVariable.getInitializer();
}
JSDefinitionExpression jsDefinitionExpression = CastUtils.tryCast(resolved, JSDefinitionExpression.class);
if (jsDefinitionExpression != null) {
JSAssignmentExpression jsAssignmentExpression = CastUtils.tryCast(resolved.getParent(), JSAssignmentExpression.class);
if (jsAssignmentExpression != null) {
return jsAssignmentExpression.getROperand();
}
}
return null;
}
@Nullable
public static PsiElement resolveUniquely(@NotNull PsiPolyVariantReference psiPolyVariantReference) {
ResolveResult[] resolveResults = psiPolyVariantReference.multiResolve(false);
PsiElement candidate = null;
for (ResolveResult resolveResult : resolveResults) {
PsiElement resolvedElement = resolveResult.getElement();
if (resolvedElement != null && resolveResult.isValidResult()) {
if (candidate != null) {
return null;
}
candidate = resolvedElement;
}
}
return candidate;
}
@Nullable
public static JSObjectLiteralExpression extractObjectLiteralExpression(@Nullable JSExpression expression) {
if (expression == null) {
return null;
}
{
JSObjectLiteralExpression jsObjectLiteralExpression = CastUtils.tryCast(expression, JSObjectLiteralExpression.class);
if (jsObjectLiteralExpression != null) {
return jsObjectLiteralExpression;
}
}
{
JSReferenceExpression jsReferenceExpression = CastUtils.tryCast(expression, JSReferenceExpression.class);
if (jsReferenceExpression != null) {
JSExpression initializer = extractInitExpression(jsReferenceExpression);
if (initializer != null) {
return extractObjectLiteralExpression(initializer);
}
}
}
return null;
}
@Nullable
public static String extractNumberLiteral(@Nullable JSExpression jsExpression) {
if (jsExpression == null) {
return null;
}
{
JSLiteralExpression jsLiteralExpression = CastUtils.tryCast(jsExpression, JSLiteralExpression.class);
if (jsLiteralExpression != null && jsLiteralExpression.isNumericLiteral()) {
return jsLiteralExpression.getText();
}
}
{
JSReferenceExpression jsReferenceExpression = CastUtils.tryCast(jsExpression, JSReferenceExpression.class);
if (jsReferenceExpression != null) {
JSExpression initializer = extractInitExpression(jsReferenceExpression);
if (initializer != null) {
return extractNumberLiteral(initializer);
}
}
}
return null;
}
@Nullable
public static JSFunctionExpression extractFunctionExpression(@Nullable JSExpression expression) {
if (expression == null) {
return null;
}
{
JSFunctionExpression jsFunctionExpression = CastUtils.tryCast(expression, JSFunctionExpression.class);
if (jsFunctionExpression != null) {
return jsFunctionExpression;
}
}
{
JSReferenceExpression jsReferenceExpression = CastUtils.tryCast(expression, JSReferenceExpression.class);
if (jsReferenceExpression != null) {
JSExpression initializer = extractInitExpression(jsReferenceExpression);
if (initializer != null) {
return extractFunctionExpression(initializer);
}
}
}
return null;
}
@NotNull
public static List<JSElement> listJsElementsInExecutionOrder(@NotNull JSFile jsFile) {
List<JSElement> jsElements = Lists.newArrayList();
for (PsiElement psiElement : jsFile.getChildren()) {
JSElement jsElement = CastUtils.tryCast(psiElement, JSElement.class);
if (jsElement != null) {
collectJsElementsInExecutionOrder(jsElement, jsElements);
}
}
return jsElements;
}
private static void collectJsElementsInExecutionOrder(@NotNull JSElement jsElement, @NotNull List<JSElement> jsElements) {
JSExpressionStatement jsExpressionStatement = CastUtils.tryCast(jsElement, JSExpressionStatement.class);
if (jsExpressionStatement != null) {
JSFunctionExpression jsFunctionExpression = null;
{
JSCallExpression jsCallExpression = CastUtils.tryCast(jsExpressionStatement.getExpression(), JSCallExpression.class);
if (jsCallExpression != null) {
JSParenthesizedExpression jsParenthesizedExpression = CastUtils.tryCast(jsCallExpression.getMethodExpression(), JSParenthesizedExpression.class);
if (jsParenthesizedExpression != null) {
jsFunctionExpression = CastUtils.tryCast(jsParenthesizedExpression.getInnerExpression(), JSFunctionExpression.class);
}
}
}
{
JSParenthesizedExpression jsParenthesizedExpression = CastUtils.tryCast(jsExpressionStatement.getExpression(), JSParenthesizedExpression.class);
if (jsParenthesizedExpression != null) {
JSCallExpression jsCallExpression = CastUtils.tryCast(jsParenthesizedExpression.getInnerExpression(), JSCallExpression.class);
if (jsCallExpression != null) {
jsFunctionExpression = CastUtils.tryCast(jsCallExpression.getMethodExpression(), JSFunctionExpression.class);
}
}
}
if (jsFunctionExpression != null) {
JSSourceElement[] jsSourceElements = ObjectUtils.notNull(jsFunctionExpression.getBody(), JSSourceElement.EMPTY_ARRAY);
for (JSSourceElement jsSourceElement : jsSourceElements) {
if (jsSourceElement instanceof JSBlockStatement) {
JSBlockStatement jsBlockStatement = (JSBlockStatement) jsSourceElement;
for (JSStatement jsStatement : jsBlockStatement.getStatements()) {
collectJsElementsInExecutionOrder(jsStatement, jsElements);
}
} else {
collectJsElementsInExecutionOrder(jsSourceElement, jsElements);
}
}
return;
}
}
jsElements.add(jsElement);
}
public static boolean isStringElement(@Nullable JSExpression jsExpression) {
return extractStringValue(jsExpression) != null;
}
public static boolean isObjectElement(@Nullable JSExpression jsExpression) {
return extractObjectLiteralExpression(jsExpression) != null;
}
public static boolean isFunctionExpressionElement(@Nullable JSExpression jsExpression) {
return extractFunctionExpression(jsExpression) != null;
}
public static boolean isNumberElement(@Nullable JSExpression jsExpression) {
return extractNumberLiteral(jsExpression) != null;
}
@Nullable
public static JSCallExpression asCallExpressionStatement(JSElement element) {
JSExpressionStatement expressionStatement = CastUtils.tryCast(element, JSExpressionStatement.class);
if (expressionStatement != null) {
return CastUtils.tryCast(expressionStatement.getExpression(), JSCallExpression.class);
}
return null;
}
@NotNull
public static JSProperty[] getProperties(@NotNull JSObjectLiteralExpression objectLiteralExpression) {
JSProperty[] properties = objectLiteralExpression.getProperties();
if (properties == null) {
properties = EMPTY_ARRAY;
}
int cnt = 0;
for (JSProperty property : properties) {
if (property != null) {
cnt++;
}
}
if (cnt < properties.length) {
JSProperty[] a = new JSProperty[cnt];
int id = 0;
for (JSProperty property : properties) {
if (property != null) {
a[id] = property;
id++;
}
}
return a;
}
return properties;
}
public static boolean containsOffsetStrictly(@NotNull TextRange textRange, int offset) {
return textRange.getStartOffset() < offset && offset < textRange.getEndOffset();
}
@NotNull
public static JSExpression[] getArguments(@Nullable JSArgumentList argumentList) {
JSExpression[] expressions = null;
if (argumentList != null) {
expressions = argumentList.getArguments();
}
if (expressions == null) {
expressions = JSExpression.EMPTY_ARRAY;
}
return expressions;
}
@NotNull
public static JSExpression[] getArguments(@NotNull JSCallExpression jsCallExpression) {
return getArguments(jsCallExpression.getArgumentList());
}
@Nullable
public static PsiElement getPropertyNamePsiElement(@NotNull JSProperty property) {
return CastUtils.tryCast(property.getFirstChild(), LeafPsiElement.class);
}
@Nullable
public static String getPropertyName(@NotNull JSProperty property) {
PsiElement testMethodNameDeclaration = getPropertyNamePsiElement(property);
if (testMethodNameDeclaration == null) {
return null;
}
return StringUtil.stripQuotesAroundValue(testMethodNameDeclaration.getText());
}
public static boolean isElementOfType(@NotNull PsiElement psiElement, @NotNull IElementType type) {
if (psiElement instanceof ASTNode) {
ASTNode node = (ASTNode) psiElement;
return node.getElementType() == type;
}
return false;
}
public static boolean isElementOfType(@NotNull PsiElement psiElement,
@NotNull IElementType type1,
@NotNull IElementType type2) {
if (psiElement instanceof ASTNode) {
ASTNode node = (ASTNode)psiElement;
IElementType type = node.getElementType();
return type == type1 || type == type2;
}
return false;
}
public static boolean isElementOfType(@NotNull PsiElement psiElement,
@NotNull IElementType type1,
@NotNull IElementType type2,
@NotNull IElementType type3) {
if (psiElement instanceof ASTNode) {
ASTNode node = (ASTNode)psiElement;
IElementType type = node.getElementType();
return type == type1 || type == type2 || type == type3;
}
return false;
}
@Nullable
public static PsiElement getFunctionLeftBrace(@Nullable JSFunction function) {
if (function == null) {
return null;
}
JSSourceElement[] jsSourceElements = ObjectUtils.notNull(function.getBody(), JSSourceElement.EMPTY_ARRAY);
for (JSSourceElement jsSourceElement : jsSourceElements) {
if (jsSourceElement instanceof JSBlockStatement) {
JSBlockStatement jsBlockStatement = (JSBlockStatement) jsSourceElement;
return jsBlockStatement.getFirstChild();
}
}
return null;
}
@Nullable
public static Document getDocument(@NotNull PsiElement element) {
PsiFile psiFile = element.getContainingFile();
if (psiFile == null) {
return null;
}
return PsiDocumentManager.getInstance(element.getProject()).getDocument(psiFile);
}
}