/* * Copyright 2013 Guidewire Software, Inc. */ package gw.plugin.ij.actions; import com.intellij.ide.IdeView; import com.intellij.ide.PasteProvider; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.actionSystem.DataKeys; import com.intellij.openapi.application.Result; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; import com.intellij.openapi.fileEditor.OpenFileDescriptor; import com.intellij.openapi.ide.CopyPasteManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vfs.newvfs.impl.VirtualDirectoryImpl; import com.intellij.psi.*; import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.util.PsiMatcherImpl; import com.intellij.psi.util.PsiMatchers; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.IncorrectOperationException; import gw.internal.gosu.parser.expressions.TypeLiteral; import gw.lang.parser.IGosuParser; import gw.lang.parser.IParsedElement; import gw.lang.parser.expressions.ITypeLiteralExpression; import gw.lang.reflect.IErrorType; import gw.lang.reflect.IType; import gw.lang.reflect.TypeSystem; import gw.lang.reflect.gs.IGosuClass; import gw.lang.reflect.module.IModule; import gw.plugin.ij.lang.GosuTokenImpl; import gw.plugin.ij.lang.psi.IGosuFileBase; import gw.plugin.ij.lang.psi.api.IGosuPackageDefinition; import gw.plugin.ij.lang.psi.api.statements.IGosuField; import gw.plugin.ij.lang.psi.api.statements.typedef.IGosuMethod; import gw.plugin.ij.lang.psi.impl.AbstractGosuClassFileImpl; import gw.plugin.ij.lang.psi.util.GosuPsiParseUtil; import gw.plugin.ij.util.ClassLord; import org.jetbrains.annotations.NotNull; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.util.*; import static com.google.common.collect.Sets.newHashSet; import static com.intellij.psi.util.PsiMatchers.hasClass; import static gw.plugin.ij.lang.psi.util.GosuPsiParseUtil.parsePackageStatement; public class GosuFilePasteProvider implements PasteProvider { private static final Logger LOG = Logger.getInstance(GosuFilePasteProvider.class); public void performPaste(@NotNull DataContext dataContext) { try { doPaste(dataContext); } catch (Exception e) { LOG.error("cannot paste the gosu content", e); } } private void doPaste(DataContext dataContext) { final Project project = DataKeys.PROJECT.getData(dataContext); final IdeView ideView = DataKeys.IDE_VIEW.getData(dataContext); final PsiDirectory targetDir = ideView.getOrChooseDirectory(); if (targetDir == null) { return; } if (project == null) { return; } final String text = getTextFromClipboardContent(project); if (text == null) { return; } IGosuFileBase file = GosuPsiParseUtil.parse(text, project); final Guesser guesser = new Guesser(); file.accept(guesser); if (!guesser.isGosu) { return; } final String unitName; Collection<PsiClass> classes = PsiTreeUtil.findChildrenOfType(file, PsiClass.class); //we have at least two classes in the psi: transient and real class. if (classes.size() < 2) { guesser.isProgram = true; } String ext; if (guesser.isProgram) { unitName = ensureFileName("program", targetDir, ext = "gsp", 0); } else { Iterator<PsiClass> it = classes.iterator(); it.next(); PsiClass mainClass = it.next(); String mainClassName = mainClass.getName(); unitName = ensureFileName(mainClassName, targetDir, ext = "gs", 0); } IGosuPackageDefinition pckg = PsiTreeUtil.findChildOfType(file, IGosuPackageDefinition.class); final String oldPackageName; if (pckg != null) { oldPackageName = pckg.getPackageName(); } else { oldPackageName = null; } final String fileName = unitName + "." + ext; new WriteCommandAction(project, "Paste gosu file '" + fileName + "'") { @Override protected void run(Result result) throws Throwable { PsiFile newFile; try { newFile = targetDir.createFile(fileName); } catch (IncorrectOperationException e) { return; } final Document document = PsiDocumentManager.getInstance(project).getDocument(newFile); document.setText(text); PsiDocumentManager.getInstance(project).commitDocument(document); if (newFile instanceof AbstractGosuClassFileImpl || !guesser.isProgram) { try { updatePackageStatement((AbstractGosuClassFileImpl) newFile, oldPackageName, targetDir); } catch (Exception e) { LOG.info("Unable to fix imports and add missing imports for the pasting gosu file. " + e.getMessage()); } } PsiClass mainClass = PsiTreeUtil.findChildOfType(newFile, PsiClass.class); if (mainClass != null && !unitName.equals(mainClass.getName())) { mainClass.setName(unitName); } new OpenFileDescriptor(project, newFile.getVirtualFile()).navigate(true); } }.execute(); } private static void updatePackageStatement(final AbstractGosuClassFileImpl gosuFile, String oldPackageName, final PsiDirectory targetDir) { final PsiPackage aPackage = JavaDirectoryService.getInstance().getPackage(targetDir); if (aPackage == null) { return; } final String newPackg = aPackage.getQualifiedName(); IGosuPackageDefinition pckgDef = PsiTreeUtil.findChildOfType(gosuFile, IGosuPackageDefinition.class); if (pckgDef != null) { pckgDef.replace(parsePackageStatement(newPackg, gosuFile)); } else { PsiElement firstChild = gosuFile.getFirstChild(); if (firstChild != null) { gosuFile.addBefore(parsePackageStatement(newPackg, gosuFile), firstChild); } } IParsedElement pe = gosuFile.getParsedElement(); IGosuClass gosuClass = pe.getGosuClass(); IGosuParser parser = gosuClass.getParser(); pe = gosuClass.getClassStatement(); IModule module = gosuClass.getTypeLoader().getModule(); TypeSystem.pushModule(module); ArrayList<String> imports = new ArrayList<>();; try { List<ITypeLiteralExpression> typeLitExprs = new ArrayList<>(); pe.getContainedParsedElementsByType(ITypeLiteralExpression.class, typeLitExprs); for (ITypeLiteralExpression expr : typeLitExprs) { String className = expr.getLocation().getTextFromTokens(); className = ClassLord.purgeClassName(className); if (!resolveRelativeTypeInParser(className, parser)) { String checkName = oldPackageName + "." + className; IType typeCheck = TypeSystem.getByFullNameIfValid(checkName); if (typeCheck != null) { imports.add(checkName); } } } } finally { TypeSystem.popModule(module); } for (String imp : imports) { ClassLord.doImport(imp, gosuFile); } } public static boolean resolveRelativeTypeInParser(String strRelativeName, IGosuParser parser) { IType type = parser.resolveTypeLiteral(strRelativeName).getType().getType(); return !(type instanceof IErrorType); } private String ensureFileName(String fileName, PsiDirectory dir, String ext, int attempt) { String tryName = fileName; if (attempt > 0) { tryName += "_" + attempt; } PsiFile file = dir.findFile(tryName + "." + ext); if (file != null) { return ensureFileName(fileName, dir, ext, ++attempt); } else { return tryName; } } public boolean isPastePossible(@NotNull DataContext dataContext) { return true; } public boolean isPasteEnabled(@NotNull DataContext dataContext) { Project project = DataKeys.PROJECT.getData(dataContext); if (project == null) { return false; } String text = getTextFromClipboardContent(project); if (text == null) { return false; } try { IGosuFileBase file = GosuPsiParseUtil.parse(text, project); Guesser guesser = new Guesser(); file.accept(guesser); return guesser.isGosu; } catch (Exception e) { //not gosu e.printStackTrace(); } return false; } private static String getTextFromClipboardContent(final Project project) { Transferable content = CopyPasteManager.getInstance().getContents(); if (content != null) { String text = null; try { text = (String) content.getTransferData(DataFlavor.stringFlavor); } catch (Exception e) { // ignore; } return text; } return null; } static class Guesser extends PsiRecursiveElementVisitor { boolean isGosu = false; boolean isProgram = true; final Class[] gosuDeterminants = {IGosuMethod.class, IGosuField.class}; final Set<String> classDeterminants = newHashSet("class", "enum", "interface"); public void visitElement(PsiElement element) { for (Class<?> c : gosuDeterminants) { if (c.isInstance(element)) { isGosu = true; } } if (element instanceof GosuTokenImpl && classDeterminants.contains(element.getText())) { isProgram = false; } super.visitElement(element); } } }