| package org.jetbrains.android.dom; |
| |
| import com.intellij.codeInsight.FileModificationService; |
| import com.intellij.codeInsight.intention.AbstractIntentionAction; |
| import com.intellij.codeInsight.intention.HighPriorityAction; |
| import com.intellij.ide.util.ClassFilter; |
| import com.intellij.ide.util.TreeClassChooser; |
| import com.intellij.ide.util.TreeClassChooserFactory; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.editor.Editor; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.ui.Messages; |
| import com.intellij.openapi.util.Ref; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.psi.*; |
| import com.intellij.psi.codeStyle.CodeStyleManager; |
| import com.intellij.psi.codeStyle.JavaCodeStyleManager; |
| import com.intellij.psi.search.GlobalSearchScope; |
| import com.intellij.psi.search.searches.ClassInheritorsSearch; |
| import com.intellij.psi.util.PsiTreeUtil; |
| import com.intellij.psi.util.PsiUtilBase; |
| import com.intellij.psi.xml.XmlAttribute; |
| import com.intellij.psi.xml.XmlAttributeValue; |
| import com.intellij.psi.xml.XmlFile; |
| import com.intellij.util.IncorrectOperationException; |
| import com.intellij.util.Processor; |
| import com.intellij.util.PsiNavigateUtil; |
| import com.intellij.util.xml.DomManager; |
| import com.intellij.util.xml.GenericAttributeValue; |
| import org.jetbrains.android.dom.converters.OnClickConverter; |
| import org.jetbrains.android.facet.AndroidFacet; |
| import org.jetbrains.android.util.AndroidBundle; |
| import org.jetbrains.android.util.AndroidUtils; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| /** |
| * @author Eugene.Kudelevsky |
| */ |
| public class AndroidCreateOnClickHandlerAction extends AbstractIntentionAction implements HighPriorityAction { |
| @NotNull |
| @Override |
| public String getText() { |
| return AndroidBundle.message("create.on.click.handler.intention.text"); |
| } |
| |
| @Override |
| public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { |
| if (editor == null || !(file instanceof XmlFile)) { |
| return false; |
| } |
| final AndroidFacet facet = AndroidFacet.getInstance(file); |
| |
| if (facet == null) { |
| return false; |
| } |
| final XmlAttributeValue attrValue = getXmlAttributeValue(file, editor); |
| |
| if (attrValue == null) { |
| return false; |
| } |
| final PsiElement parent = attrValue.getParent(); |
| |
| if (!(parent instanceof XmlAttribute)) { |
| return false; |
| } |
| final GenericAttributeValue domValue = DomManager.getDomManager(project).getDomElement((XmlAttribute)parent); |
| |
| if (domValue == null || !(domValue.getConverter() instanceof OnClickConverter)) { |
| return false; |
| } |
| final String methodName = attrValue.getValue(); |
| return methodName != null && StringUtil.isJavaIdentifier(methodName); |
| } |
| |
| @Nullable |
| private static XmlAttributeValue getXmlAttributeValue(PsiFile file, Editor editor) { |
| final int offset = editor.getCaretModel().getOffset(); |
| final PsiElement element = file.findElementAt(offset); |
| return element != null ? PsiTreeUtil.getParentOfType(element, XmlAttributeValue.class) : null; |
| } |
| |
| @Override |
| public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { |
| final AndroidFacet facet = AndroidFacet.getInstance(file); |
| assert facet != null; |
| |
| final XmlAttributeValue attrValue = getXmlAttributeValue(file, editor); |
| assert attrValue != null; |
| final String methodName = attrValue.getValue(); |
| assert methodName != null; |
| final GenericAttributeValue domValue = DomManager.getDomManager(project).getDomElement((XmlAttribute)attrValue.getParent()); |
| assert domValue != null; |
| final OnClickConverter converter = (OnClickConverter)domValue.getConverter(); |
| |
| final PsiClass activityBaseClass = JavaPsiFacade.getInstance(project).findClass( |
| AndroidUtils.ACTIVITY_BASE_CLASS_NAME, facet.getModule().getModuleWithDependenciesAndLibrariesScope(false)); |
| |
| if (activityBaseClass == null) { |
| return; |
| } |
| final GlobalSearchScope scope = facet.getModule().getModuleScope(false); |
| final PsiClass selectedClass; |
| |
| if (ApplicationManager.getApplication().isUnitTestMode()) { |
| final Ref<PsiClass> selClassRef = Ref.create(); |
| |
| ClassInheritorsSearch.search(activityBaseClass, scope, true, true, false).forEach(new Processor<PsiClass>() { |
| @Override |
| public boolean process(PsiClass psiClass) { |
| if (!psiClass.isInterface() && !psiClass.hasModifierProperty(PsiModifier.ABSTRACT)) { |
| selClassRef.set(psiClass); |
| return false; |
| } |
| return true; |
| } |
| }); |
| selectedClass = selClassRef.get(); |
| } |
| else { |
| final TreeClassChooser chooser = TreeClassChooserFactory.getInstance(project).createInheritanceClassChooser( |
| "Choose Activity to Create the Method", scope, activityBaseClass, null, new ClassFilter() { |
| @Override |
| public boolean isAccepted(PsiClass aClass) { |
| return !converter.findHandlerMethod(aClass, methodName); |
| } |
| }); |
| chooser.showDialog(); |
| selectedClass = chooser.getSelected(); |
| } |
| |
| if (selectedClass != null) { |
| addHandlerMethodAndNavigate(project, selectedClass, methodName, converter.getDefaultMethodParameterType(selectedClass)); |
| } |
| } |
| |
| @NotNull |
| private static String suggestVarName(@NotNull String type) { |
| for (int i = type.length() - 1; i >= 0; i--) { |
| final char c = type.charAt(i); |
| |
| if (Character.isUpperCase(c)) { |
| return type.substring(i).toLowerCase(); |
| } |
| } |
| return "o"; |
| } |
| |
| @Nullable |
| public static PsiMethod addHandlerMethod(@NotNull Project project, |
| @NotNull PsiClass psiClass, |
| @NotNull String methodName, |
| @NotNull String methodParamType) { |
| final PsiFile file = psiClass.getContainingFile(); |
| |
| if (file == null || !FileModificationService.getInstance().prepareFileForWrite(file)) { |
| return null; |
| } |
| final PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory(); |
| final String varName = suggestVarName(methodParamType); |
| PsiMethod method = (PsiMethod)psiClass.add(factory.createMethodFromText( |
| "public void " + methodName + "(" + methodParamType + " " + varName + ") {}", psiClass)); |
| |
| PsiMethod method1 = (PsiMethod)CodeStyleManager.getInstance(project).reformat(method); |
| method1 = (PsiMethod)JavaCodeStyleManager.getInstance(project).shortenClassReferences(method1); |
| return (PsiMethod)method.replace(method1); |
| } |
| |
| public static void addHandlerMethodAndNavigate(@NotNull final Project project, |
| @NotNull final PsiClass psiClass, |
| @NotNull final String methodName, |
| @NotNull final String methodParamType) { |
| if (!AndroidUtils.isIdentifier(methodName)) { |
| Messages.showErrorDialog(project, String.format("%1$s is not a valid Java identifier/method name.", methodName), "Invalid Name"); |
| return; |
| } |
| |
| ApplicationManager.getApplication().runWriteAction(new Runnable() { |
| @Override |
| public void run() { |
| final PsiMethod method = addHandlerMethod(project, psiClass, methodName, methodParamType); |
| |
| if (method == null) { |
| return; |
| } |
| if (!ApplicationManager.getApplication().isUnitTestMode()) { |
| PsiNavigateUtil.navigate(method); |
| } |
| final PsiFile javaFile = method.getContainingFile(); |
| |
| if (javaFile == null) { |
| return; |
| } |
| final Editor javaEditor = PsiUtilBase.findEditor(method); |
| |
| if (javaEditor == null) { |
| return; |
| } |
| final PsiCodeBlock body = method.getBody(); |
| |
| if (body != null) { |
| final PsiJavaToken lBrace = body.getLBrace(); |
| |
| if (lBrace != null) { |
| javaEditor.getCaretModel().moveToOffset(lBrace.getTextRange().getEndOffset()); |
| } |
| } |
| } |
| }); |
| } |
| } |