/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.plugin.ij.lang.psi.util;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.impl.GeneratedMarkerVisitor;
import com.intellij.psi.impl.source.javadoc.PsiDocCommentImpl;
import com.intellij.psi.javadoc.PsiDocComment;
import com.intellij.psi.util.PsiMatcherImpl;
import com.intellij.psi.util.PsiMatchers;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.testFramework.LightVirtualFile;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.StringBuilderSpinAllocator;
import gw.lang.parser.GosuParserFactory;
import gw.lang.parser.IGosuParser;
import gw.lang.parser.ParserOptions;
import gw.lang.parser.exceptions.ParseResultsException;
import gw.lang.reflect.TypeSystem;
import gw.lang.reflect.gs.IGosuProgram;
import gw.lang.reflect.module.IModule;
import gw.plugin.ij.lang.parser.GosuElementTypes;
import gw.plugin.ij.lang.psi.IGosuFileBase;
import gw.plugin.ij.lang.psi.IGosuPsiElement;
import gw.plugin.ij.lang.psi.api.statements.IGosuUsesStatementList;
import gw.plugin.ij.lang.psi.api.statements.IGosuVariable;
import gw.plugin.ij.lang.psi.impl.AbstractGosuClassFileImpl;
import gw.plugin.ij.lang.psi.impl.GosuProgramFileImpl;
import gw.plugin.ij.lang.psi.impl.ModuleFileContext;
import gw.plugin.ij.lang.psi.impl.expressions.GosuIdentifierExpressionImpl;
import gw.plugin.ij.lang.psi.impl.expressions.GosuReferenceExpressionImpl;
import gw.plugin.ij.lang.psi.impl.expressions.GosuTypeLiteralImpl;
import gw.plugin.ij.lang.psi.impl.statements.GosuVariableImpl;
import gw.plugin.ij.lang.psi.impl.statements.params.GosuParameterImpl;
import gw.plugin.ij.lang.psi.impl.statements.typedef.GosuSyntheticClassDefinitionImpl;
import gw.plugin.ij.lang.psi.impl.statements.typedef.members.GosuMethodImpl;
import gw.plugin.ij.lang.psi.stubs.GosuFileStub;
import gw.plugin.ij.util.GosuModuleUtil;
import gw.plugin.ij.util.LightVirtualFileWithModule;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static com.intellij.openapi.util.text.StringUtil.join;
import static com.intellij.psi.util.PsiMatchers.hasClass;
import static gw.plugin.ij.util.ClassLord.purgeClassName;
public class GosuPsiParseUtil {
public static final String TRANSIENT_PROGRAM = "transient_program.gsp";
public static final String TRANSIENT_TEMPLATE = "transient_template.gst";
@Nullable
public static IGosuFileBase createVirtualProgramFile(@NotNull IGosuPsiElement psi, String strScript) {
AbstractGosuClassFileImpl file = (AbstractGosuClassFileImpl) psi.getContainingFile();
ASTNode child = file.getNode().findChildByType(GosuElementTypes.ELEM_TYPE_UsesStatementList);
if (child != null) {
strScript = child.getText() + "\n" + strScript;
}
child = file.getNode().findChildByType(GosuElementTypes.ELEM_TYPE_NamespaceStatement);
if (child != null) {
String text = child.getText();
text = text.replace("package", "uses") + ".*";
strScript = text + "\n" + strScript;
}
final LightVirtualFile virtualFile = LightVirtualFileWithModule.create(TRANSIENT_PROGRAM, strScript, GosuModuleUtil.findModuleForPsiElement(psi));
return (IGosuFileBase) PsiManager.getInstance(psi.getProject()).findFile(virtualFile);
}
public static PsiElement parseRelativeTypeLiteral(@NotNull String fullName, @NotNull PsiElement ctx) {
String purged = purgeClassName(fullName);
String qualifiedName = fullName.substring(purged.lastIndexOf(".") + 1);
String text = "uses " + fullName + " return null as " + qualifiedName;
LightVirtualFile virtualFile = LightVirtualFileWithModule.create(TRANSIENT_PROGRAM, text, GosuModuleUtil.findModuleForPsiElement(ctx));
IGosuFileBase psiFile = (IGosuFileBase) PsiManager.getInstance(ctx.getProject()).findFile(virtualFile);
return new PsiMatcherImpl(psiFile)
.descendant(PsiMatchers.hasClass(GosuSyntheticClassDefinitionImpl.class))
.descendant(new ElementTypeMatcher(GosuElementTypes.ELEM_TYPE_ReturnStatement))
.descendant(PsiMatchers.hasClass(GosuTypeLiteralImpl.class))
.getElement();
}
public static PsiElement parseTypeLiteral(@NotNull String fullName, @NotNull PsiElement ctx) {
return new PsiMatcherImpl(parseImport(fullName, ctx)).descendant(hasClass(GosuTypeLiteralImpl.class)).getElement();
}
public static PsiElement parseFullTypeLiteral(@NotNull String literalText, @NotNull PsiElement ctx, Iterable<String> imports) {
StringBuilder src = new StringBuilder();
for (String imp : imports) {
src.append("uses ").append(imp).append("\n");
}
src.append("var x : ").append(literalText);
IGosuFileBase psiFile = parse(src, ctx);
return new PsiMatcherImpl(psiFile)
.descendant(hasClass(PsiVariable.class))
.descendant(hasClass(GosuTypeLiteralImpl.class))
.getElement();
}
public static IGosuFileBase parse(CharSequence src, PsiElement ctx) {
return parse(src, ctx.getProject(), GosuModuleUtil.findModuleForPsiElement(ctx));
}
public static IGosuFileBase parse(CharSequence src, Project project) {
return parse(src, project, GosuModuleUtil.getGlobalModule(project));
}
public static IGosuFileBase parse(CharSequence src, Project project, IModule module) {
LightVirtualFile virtualFile = LightVirtualFileWithModule.create(TRANSIENT_PROGRAM, src, module);
return (IGosuFileBase) PsiManager.getInstance(project).findFile(virtualFile);
}
public static PsiElement parseDeclaration(String s, @NotNull PsiManager manager, @Nullable IModule module) {
if (module != null) {
TypeSystem.pushModule(module);
}
try {
LightVirtualFile fakeFile = LightVirtualFileWithModule.create(TRANSIENT_PROGRAM, s, module);
GosuFileStub gosuFileStub = new GosuFileStub(new GosuProgramFileImpl(new SingleRootFileViewProvider(manager, fakeFile, false)));
PsiElement[] children = gosuFileStub.getPsi().getChildren();
return children[0].getChildren()[2];
} finally {
if (module != null) {
TypeSystem.popModule(module);
}
}
}
public static PsiElement parseExpression(String expression, @NotNull PsiManager manager) {
return parseExpression(expression, manager, null);
}
public static PsiElement parseExpression(String expression, @NotNull PsiManager manager, @Nullable IModule module) {
if (module == null) {
module = GosuModuleUtil.getGlobalModule(manager.getProject());
}
TypeSystem.pushModule(module);
try {
final LightVirtualFile file = LightVirtualFileWithModule.create(TRANSIENT_PROGRAM, expression, module);
IGosuFileBase psiFile = (IGosuFileBase) PsiManager.getInstance(manager.getProject()).findFile(file);
return psiFile.getChildren()[0].getChildren()[2].getChildren()[0];
} finally {
TypeSystem.popModule(module);
}
}
public static PsiElement parseProgramm(String content, @NotNull PsiManager manager, @Nullable IModule module) {
return parseProgramm(content, null, manager, module);
}
public static PsiElement parseProgramm(String content, @Nullable PsiElement elemCtx, @NotNull PsiManager manager, @Nullable IModule module) {
if (module == null) {
module = GosuModuleUtil.getGlobalModule(manager.getProject());
}
String uses = getPackageAndUsesStatementsFrom(elemCtx);
TypeSystem.pushModule(module);
try {
final LightVirtualFile file = LightVirtualFileWithModule.create(TRANSIENT_PROGRAM, uses + content, module);
IGosuFileBase psiFile = (IGosuFileBase) PsiManager.getInstance(manager.getProject()).findFile(file);
return psiFile;
} finally {
TypeSystem.popModule(module);
}
}
private static String getPackageAndUsesStatementsFrom(PsiElement element) {
StringBuilder out = new StringBuilder();
PsiElement file = new PsiMatcherImpl(element)
.ancestor(hasClass(AbstractGosuClassFileImpl.class))
.getElement();
if (file instanceof AbstractGosuClassFileImpl) {
AbstractGosuClassFileImpl clazz = (AbstractGosuClassFileImpl) file;
String packageName = clazz.getPackageName();
IGosuUsesStatementList usesStatementList = clazz.findUsesStatementList();
if (packageName != null) {
out.append("package " + packageName + "\n");
}
if (usesStatementList != null) {
out.append(usesStatementList.getText() + "\n");
}
}
return out.toString();
}
@Nullable
public static PsiElement createModifierFromText(final String strModifier, @NotNull PsiManager manager) {
PsiElement psi = parseExpression(strModifier, manager);
return psi.getFirstChild();
}
public static PsiElement parseImport(String s, @NotNull PsiElement ctx) {
LightVirtualFile virtualFile = LightVirtualFileWithModule.create(TRANSIENT_PROGRAM, "uses " + s, GosuModuleUtil.findModuleForPsiElement(ctx));
IGosuFileBase psiFile = (IGosuFileBase) PsiManager.getInstance(ctx.getProject()).findFile(virtualFile);
return psiFile.getChildren()[0].getChildren()[2].getChildren()[0];
}
@NotNull
public static IGosuUsesStatementList parseUsesList(String importListAsText, @NotNull PsiElement ctx) {
LightVirtualFile virtualFile = LightVirtualFileWithModule.create(TRANSIENT_PROGRAM, importListAsText, GosuModuleUtil.findModuleForPsiElement(ctx));
IGosuFileBase psiFile = (IGosuFileBase) PsiManager.getInstance(ctx.getProject()).findFile(virtualFile);
IGosuUsesStatementList statementList = (IGosuUsesStatementList) psiFile.getChildren()[0].getChildren()[2];
GeneratedMarkerVisitor.markGenerated(statementList);
return statementList;
}
public static PsiElement parseTemplateImport(String s, @NotNull PsiElement ctx) {
LightVirtualFile virtualFile = LightVirtualFileWithModule.create(TRANSIENT_TEMPLATE, "<%uses " + s + "%>", GosuModuleUtil.findModuleForPsiElement(ctx));
IGosuFileBase psiFile = (IGosuFileBase) PsiManager.getInstance(ctx.getProject()).findFile(virtualFile);
return psiFile.getChildren()[0].getChildren()[2].getChildren()[0];
}
public static PsiElement parsePackageStatement(String strPackage, @NotNull PsiElement ctx) {
LightVirtualFile virtualFile = LightVirtualFileWithModule.create(TRANSIENT_PROGRAM, "package " + strPackage, GosuModuleUtil.findModuleForPsiElement(ctx));
IGosuFileBase psiFile = (IGosuFileBase) PsiManager.getInstance(ctx.getProject()).findFile(virtualFile);
PsiElement psiElement = psiFile.getChildren()[0].getChildren()[2];
GeneratedMarkerVisitor.markGenerated(psiElement);
return psiElement;
}
@Nullable
public static PsiElement createReferenceNameFromText(@NotNull GosuReferenceExpressionImpl referenceExpression, String refName) {
String strScript = "return " + refName;
IGosuFileBase file = createVirtualProgramFile(referenceExpression, strScript);
int iRefNameIndex = file.getText().indexOf(strScript) + "return ".length();
PsiElement psiRefName = file.findElementAt(iRefNameIndex);
if (psiRefName == null) {
throw new IllegalStateException("Did not find reference name psi");
}
return psiRefName;
}
@NotNull
public static GosuParameterImpl createParameter(String name, @NotNull PsiType argType, @NotNull PsiElement contextElement) {
GosuMethodImpl method = (GosuMethodImpl) parseDeclaration(
"construct(" + name + ": " + argType.getPresentableText() + ") {}",
contextElement.getManager(), GosuModuleUtil.findModuleForPsiElement(contextElement));
return (GosuParameterImpl) method.getParameters()[0];
}
public static PsiIdentifier parseIdentifier(String name, PsiElement ctx) {
LightVirtualFile virtualFile = LightVirtualFileWithModule.create(TRANSIENT_PROGRAM, "return " + name, GosuModuleUtil.findModuleForPsiElement(ctx));
IGosuFileBase psiFile = (IGosuFileBase) PsiManager.getInstance(ctx.getProject()).findFile(virtualFile);
return (PsiIdentifier) new PsiMatcherImpl(psiFile)
.descendant(PsiMatchers.hasClass(GosuSyntheticClassDefinitionImpl.class))
.descendant(new ElementTypeMatcher(GosuElementTypes.ELEM_TYPE_ReturnStatement))
.descendant(PsiMatchers.hasClass(GosuIdentifierExpressionImpl.class))
.getElement()
.getFirstChild();
}
public static PsiElement parseIdentifierOrTokenOrRelativeType(String name, PsiElement ctx) {
LightVirtualFile virtualFile = LightVirtualFileWithModule.create(TRANSIENT_PROGRAM, "return " + name, GosuModuleUtil.findModuleForPsiElement(ctx));
IGosuFileBase psiFile = (IGosuFileBase) PsiManager.getInstance(ctx.getProject()).findFile(virtualFile);
PsiElement element = new PsiMatcherImpl(psiFile)
.descendant(PsiMatchers.hasClass(GosuSyntheticClassDefinitionImpl.class))
.descendant(new ElementTypeMatcher(GosuElementTypes.ELEM_TYPE_ReturnStatement)).getElement();
return element.findElementAt("return ".length());
}
public static PsiDocComment parseJavadocComment(String text, PsiElement ctx) {
String code = "/**\n*" + text + "\n*/\nvar x: int";
LightVirtualFile virtualFile = LightVirtualFileWithModule.create(TRANSIENT_PROGRAM, code, GosuModuleUtil.findModuleForPsiElement(ctx));
IGosuFileBase psiFile = (IGosuFileBase) PsiManager.getInstance(ctx.getProject()).findFile(virtualFile);
PsiDocComment firstChild = (PsiDocComment) new PsiMatcherImpl(psiFile)
.descendant(PsiMatchers.hasClass(GosuSyntheticClassDefinitionImpl.class))
.descendant(PsiMatchers.hasClass(PsiDocCommentImpl.class))
.getElement();
return firstChild;
}
/**
* this method was copied from PsiElementFactoryImpl
*
* @param project
* @param name
* @param initializer
* @return
* @throws IncorrectOperationException
*/
@NotNull
public static IGosuVariable createLocalVariableDeclarationStatement(@NotNull Project project, @NotNull final String name,
boolean isFinal,
final PsiExpression initializer) throws IncorrectOperationException {
final PsiElement psiElement = createLocalVariableDeclarationAssignmentStatement(project, name, isFinal, initializer);
final GosuVariableImpl variable = PsiTreeUtil.findChildOfType(psiElement, GosuVariableImpl.class);
GeneratedMarkerVisitor.markGenerated(variable);
return (GosuVariableImpl) CodeStyleManager.getInstance(project).reformat(variable);
}
@NotNull
private static PsiElement createLocalVariableDeclarationAssignmentStatement(@NotNull Project project, @NotNull final String name,
boolean isFinal,
final PsiExpression initializer) throws IncorrectOperationException {
if (!JavaPsiFacade.getInstance(project).getNameHelper().isIdentifier(name)) {
throw new IncorrectOperationException("\"" + name + "\" is not an identifier.");
}
final StringBuilder builder = StringBuilderSpinAllocator.alloc();
if (initializer != null) {
PsiType type = initializer.getType();
if (initializer instanceof PsiArrayInitializerExpression) {
final PsiExpression[] initializers = ((PsiArrayInitializerExpression) initializer).getInitializers();
type = initializers.length == 0 ? type : initializers[0].getType();
}
if (type != null) {
builder.append("uses ").append(type.getCanonicalText()).append("\n");
}
}
builder.append("function foo(){").
append(isFinal ? "final " : "").
append("var ");
int i = 2;
while (i-- > 0) { //one iteration for declaration and one for assignment
builder.append(name).
append(" = ");
if (initializer != null) {
builder.append(initializer.getText());
} else {
builder.append("1");
}
builder.append(" ");
}
builder.append("}");
final String text = builder.toString();
StringBuilderSpinAllocator.dispose(builder);
final PsiElement psiElement = parseProgramm(text, PsiManager.getInstance(project), null);
return psiElement;
}
public static PsiAssignmentExpression createAssignmentStatement(@NotNull Project project, @NotNull final String name, final PsiExpression initializer) {
final PsiElement psiElement = createLocalVariableDeclarationAssignmentStatement(project, name, false, initializer);
final PsiAssignmentExpression assignmentStatement = PsiTreeUtil.findChildOfType(psiElement, PsiAssignmentExpression.class);
GeneratedMarkerVisitor.markGenerated(assignmentStatement);
return assignmentStatement;
}
public static PsiElement setName(PsiIdentifier identifier, String name) {
PsiIdentifier newIdentifier = parseIdentifier(name, identifier);
return identifier.replace(newIdentifier);
}
public static PsiMethod createConstructor(final String name, @NotNull PsiManager manager, @Nullable IModule module) {
final PsiElement program = parseProgramm(join("class ", name, " { construct () {} }"), manager, module);
final PsiMethod method = PsiTreeUtil.findChildOfType(program, GosuMethodImpl.class);
GeneratedMarkerVisitor.markGenerated(method);
return (PsiMethod) CodeStyleManager.getInstance(manager.getProject()).reformat(method);
}
public static PsiExpressionList createEmptyExpressionList(@NotNull PsiManager manager) {
PsiElement psiElement = parseProgramm("print(null)", manager, null);
PsiExpressionList exprList = PsiTreeUtil.findChildOfType(psiElement, PsiExpressionList.class);
exprList.getExpressions()[0].delete();
return exprList;
}
public static IGosuProgram parseProgram(@NotNull IGosuParser parser, @NotNull ParserOptions options, ModuleFileContext context, String contents) throws ParseResultsException {
final ParserOptions parserOptions = options
.withParser(parser)
.withTypeUsesMap(parser.getTypeUsesMap())
.withFileContext(context);
return GosuParserFactory.createProgramParser()
.parseExpressionOrProgram(contents, parser.getSymbolTable(), parserOptions)
.getProgram();
}
}