package edu.princeton.alumni.dwight00; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.actionSystem.DataConstants; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.actionSystem.EditorAction; import com.intellij.openapi.editor.actionSystem.EditorActionHandler; import com.intellij.openapi.editor.actionSystem.EditorWriteActionHandler; import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.codeEditor.CodeEditorManager; import com.intellij.util.IncorrectOperationException; /** * Created by IntelliJ IDEA. * User: dwight * Date: Dec 8, 2002 * Time: 1:17:21 PM * To change this template use Options | File Templates. */ // todo: should this be called on: method invocations? anywhere within a method? method definition only? public class ConvertMethodToInner extends EditorAction { public ConvertMethodToInner() { super(new ConvertMethodToInnerHandler()); } public static class ConvertMethodToInnerHandler extends EditorWriteActionHandler { private PsiMethod mTargetMethod; private PsiManager mPsiManager; private PsiElementFactory mElementFactory; public void executeWriteAction(Editor editor, DataContext dataContext) { Project project = (Project) dataContext.getData(DataConstants.PROJECT); CodeEditorManager.getInstance(project).commitAllToPsiFile(); PsiElement element = PsiUtils.getElement(dataContext); if (element == null || !(element instanceof PsiMethod) || ((PsiMethod) element).isConstructor()) return; mTargetMethod = (PsiMethod) element; PsiClass containingClass = mTargetMethod.getContainingClass(); mPsiManager = PsiManager.getInstance(project); mElementFactory = mPsiManager.getElementFactory(); try { PsiClass innerClass = createInnerClass(); replaceMethod(innerClass); containingClass.addAfter(mPsiManager.getCodeStyleManager().reformat(innerClass), mTargetMethod); } catch (IncorrectOperationException e1) { e1.printStackTrace(); //To change body of catch statement use Options | File Templates. throw new RuntimeException(e1); } } private void replaceMethod(PsiClass inInnerClass) throws IncorrectOperationException { PsiCodeBlock body = mTargetMethod.getBody(); StringBuffer myDelegation = new StringBuffer(); myDelegation.append("{"); System.err.println(mTargetMethod.getReturnType().getCanonicalText()); if (mTargetMethod.getReturnType() != null && !mTargetMethod.getReturnType().getCanonicalText().equals("void")) myDelegation.append("return "); myDelegation.append("new "); myDelegation.append(inInnerClass.getName()); myDelegation.append("("); PsiParameter[] parameters = mTargetMethod.getParameterList().getParameters(); for (int i = 0; i < parameters.length; i++) { PsiParameter parameter = parameters[i]; myDelegation.append(parameter.getName()); if (i < parameters.length-1) myDelegation.append(", "); } myDelegation.append(").execute();"); myDelegation.append("}"); PsiCodeBlock newBody = mElementFactory.createCodeBlockFromText(myDelegation.toString(), body); body.replace(mPsiManager.getCodeStyleManager().reformat(newBody)); } private PsiClass createInnerClass() throws IncorrectOperationException { // todo: maybe prompt dialog for execute-method's name? PsiClass innerClass = mElementFactory.createClass(generateClassName(mTargetMethod.getName())); PsiMethod executeMethod = mElementFactory.createMethod("execute", mTargetMethod.getReturnType()); copyMethodBody(mTargetMethod, executeMethod); copyThrowsClauses(mTargetMethod, executeMethod); makeParamsFields(mTargetMethod, innerClass); innerClass.add(executeMethod); return innerClass; } private void copyThrowsClauses(PsiMethod inTargetMethod, PsiMethod inExecuteMethod) throws IncorrectOperationException { PsiReferenceList throwsList = inTargetMethod.getThrowsList(); PsiReferenceElement[] referenceElements = throwsList.getReferenceElements(); for (int i = 0; i < referenceElements.length; i++) { PsiReferenceElement referenceElement = referenceElements[i]; inExecuteMethod.getThrowsList().add(referenceElement); } } private void makeParamsFields(PsiMethod inTargetMethod, PsiClass inInnerClass) throws IncorrectOperationException { // todo: use code-style to name parameters and fields correctly PsiParameterList parameterList = inTargetMethod.getParameterList(); PsiParameter[] parameters = parameterList.getParameters(); PsiMethod constructor = mElementFactory.createConstructor(); PsiParameterList ctorParamList = constructor.getParameterList(); PsiCodeBlock ctorBody = constructor.getBody(); for (int i = 0; i < parameters.length; i++) { PsiParameter parameter = parameters[i]; PsiField field = mElementFactory.createField(parameter.getName(), parameter.getType()); inInnerClass.add(field); PsiParameter ctorParameter = mElementFactory.createParameter(parameter.getName(), parameter.getType()); ctorParamList.add(ctorParameter); ctorBody.add(mElementFactory.createStatementFromText("this."+field.getName()+" = "+ctorParameter.getName()+";", ctorBody)); } inInnerClass.add(constructor); } private void copyMethodBody(PsiMethod inTargetMethod, PsiMethod inExecuteMethod) throws IncorrectOperationException { PsiCodeBlock originalBody = inTargetMethod.getBody(); PsiElement[] originalBodyElements = originalBody.getChildren(); PsiCodeBlock newMethodBody = inExecuteMethod.getBody(); for (int i = 1; i < originalBodyElements.length-1; i++) { PsiElement psiElement = originalBodyElements[i]; if (psiElement != originalBody.getLBrace() && psiElement != originalBody.getRBrace()) newMethodBody.add(psiElement); } } private String generateClassName(String inMethodName) { // todo: ensure class name not already in use, or supply dialog, or use live-template mechanism for editing String suffix = "Helper"; StringBuffer result = new StringBuffer(inMethodName.length()+suffix.length()); char firstChar = inMethodName.charAt(0); result.append(Character.toUpperCase(firstChar)); if (inMethodName.length() > 1) result.append(inMethodName.substring(1, inMethodName.length())); result.append(suffix); return result.toString(); } public boolean isEnabled(Editor editor, DataContext dataContext) { PsiElement element = PsiUtils.getElement(dataContext); return (element instanceof PsiMethod && !((PsiMethod) element).isConstructor()); } } }