package com.chrisfolger.needsmoredojo.core.refactoring; import com.chrisfolger.needsmoredojo.core.amd.CompletionCallback; import com.chrisfolger.needsmoredojo.core.amd.objectmodel.DeclareResolver; import com.chrisfolger.needsmoredojo.core.amd.objectmodel.DeclareStatementItems; import com.chrisfolger.needsmoredojo.core.util.JSUtil; import com.intellij.lang.javascript.psi.*; import com.intellij.notification.Notification; import com.intellij.notification.NotificationType; import com.intellij.notification.Notifications; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.CommandProcessor; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class ClassToUtilConverter implements CompletionCallback { @Override public void run(Object[] result) { DeclareResolver util = new DeclareResolver(); final DeclareStatementItems utilItem = util.getDeclareStatementFromParsedStatement(result); if(utilItem == null) { Notifications.Bus.notify(new Notification("needsmoredojo", "Convert class module to util module", "Valid declare block was not found", NotificationType.WARNING)); return; } CommandProcessor.getInstance().executeCommand(utilItem.getDeclareContainingStatement().getProject(), new Runnable() { @Override public void run() { ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override public void run() { doRefactor(utilItem.getClassName(), utilItem.getDeclareContainingStatement(), utilItem.getExpressionsToMixin(), utilItem.getMethodsToConvert()); } }); } }, "Convert class module to util module", "Convert class module to util module"); } public void doRefactor(@Nullable JSLiteralExpression className, @NotNull JSElement originalReturnStatement, @NotNull JSExpression[] mixins, @NotNull JSProperty[] properties) { // insert new items before the return statement PsiElement parent = originalReturnStatement.getParent(); // build an array of mixins for the new declare statement StringBuilder mixinArray = new StringBuilder(); for (JSExpression mixin : mixins) { if (mixinArray.toString().equals("")) { mixinArray.append(mixin.getText()); } else { mixinArray.append(", " + mixin.getText()); } } String classPrefix = ""; if(className != null) { classPrefix = String.format("%s, ", className.getText()); } // create the declare statement and add it before the return statement String declareStatement = String.format("var util = declare(%s[%s], {});", classPrefix, mixinArray.toString()); JSUtil.addStatementBeforeElement(parent, originalReturnStatement, declareStatement); // convert each property to an assignment statement for (JSProperty property : properties) { String propertyStatement = String.format("util.%s = %s;", property.getName(), property.getValue().getText().replaceAll("this", "util")); // account for the case where the class property is in quotes, because it might be a special // property that would not be a valid javascript property unless it was in quotes. if(property.getNameIdentifier().getText().contains("'") || property.getNameIdentifier().getText().contains("\"")) { propertyStatement = String.format("util[%s] = %s;", property.getNameIdentifier().getText(), property.getValue().getText()); } JSUtil.addStatementBeforeElement(parent, originalReturnStatement, propertyStatement); } // add the final statement to return the util String newReturnStatement = "return util;"; JSUtil.addStatementBeforeElement(parent, originalReturnStatement, newReturnStatement); // delete the old return declare(...) block originalReturnStatement.delete(); } /** * This method returns a visitor that will take a dojo module and convert it to use a util pattern * instead of a class pattern * * @return the visitor */ public JSRecursiveElementVisitor getVisitorToConvertToUtilPattern() { return new JSRecursiveElementVisitor() { @Override public void visitJSCallExpression(JSCallExpression element) { if(!element.getMethodExpression().getText().equals("define")) { super.visitJSCallExpression(element); return; } // get the function JSFunction function = (JSFunction) element.getArguments()[1]; function.acceptChildren(new DeclareResolver().getVisitorToRetrieveDeclare(new ClassToUtilConverter())); return; } }; } public void convertToUtilPattern(PsiFile file) { // steps: // get the return declare statement // get all of the literal expressions file.acceptChildren(getVisitorToConvertToUtilPattern()); } }