diff --git a/plugins/android/resources/messages/AndroidBundle.properties b/plugins/android/resources/messages/AndroidBundle.properties index ebfbde4a815e..6fe79781fabd 100644 --- a/plugins/android/resources/messages/AndroidBundle.properties +++ b/plugins/android/resources/messages/AndroidBundle.properties @@ -412,4 +412,5 @@ deployment.target.settings.min.sdk.info.message=Only compatible AVDs are shown. android.compilation.warning.circular.app.dependency=Generated fields in {0}.R class in module ''{1}'' won''t be final, because of circular dependency on module ''{2}'' cannot.find.zip.align=The zipalign tool was not found in the SDK.\n\nPlease update to the latest SDK and re-export your application\nor run zipalign manually.\n\nAligning applications allows Android to use application resources\nmore efficiently. file.name.not.specified.error=File name is not specified -root.element.not.specified.error=Root element is not specified \ No newline at end of file +root.element.not.specified.error=Root element is not specified +directory.not.specified.error=Directory is not specified \ No newline at end of file diff --git a/plugins/android/src/META-INF/plugin.xml b/plugins/android/src/META-INF/plugin.xml index 3315888cb0df..4700bd2e5eec 100644 --- a/plugins/android/src/META-INF/plugin.xml +++ b/plugins/android/src/META-INF/plugin.xml @@ -219,6 +219,8 @@ + + diff --git a/plugins/android/src/org/jetbrains/android/AndroidGotoDeclarationHandler.java b/plugins/android/src/org/jetbrains/android/AndroidGotoDeclarationHandler.java index d73c45ffb5ab..9b23a4cc2c3a 100644 --- a/plugins/android/src/org/jetbrains/android/AndroidGotoDeclarationHandler.java +++ b/plugins/android/src/org/jetbrains/android/AndroidGotoDeclarationHandler.java @@ -19,7 +19,11 @@ import com.intellij.codeInsight.navigation.actions.GotoDeclarationHandler; import com.intellij.navigation.NavigationItem; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.editor.Editor; -import com.intellij.psi.*; +import com.intellij.openapi.util.Pair; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiIdentifier; +import com.intellij.psi.PsiReferenceExpression; import com.intellij.psi.meta.PsiMetaOwner; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.xml.XmlAttributeValue; @@ -27,7 +31,6 @@ import org.jetbrains.android.dom.wrappers.FileResourceElementWrapper; import org.jetbrains.android.dom.wrappers.ValueResourceElementWrapper; import org.jetbrains.android.facet.AndroidFacet; import org.jetbrains.android.util.AndroidResourceUtil; -import org.jetbrains.android.util.AndroidUtils; import java.util.List; @@ -51,47 +54,22 @@ public class AndroidGotoDeclarationHandler implements GotoDeclarationHandler { return null; } - final String resFieldName = refExp.getReferenceName(); - if (resFieldName == null || resFieldName.length() == 0) { - return null; - } - - PsiExpression qExp = refExp.getQualifierExpression(); - if (!(qExp instanceof PsiReferenceExpression)) { - return null; - } - final PsiReferenceExpression resClassReference = (PsiReferenceExpression)qExp; - - final String resClassName = resClassReference.getReferenceName(); - if (resClassName == null || resClassName.length() == 0) { - return null; - } - - qExp = resClassReference.getQualifierExpression(); - if (!(qExp instanceof PsiReferenceExpression)) { - return null; - } - - final PsiElement resolvedElement = ((PsiReferenceExpression)qExp).resolve(); - if (!(resolvedElement instanceof PsiClass) || - !AndroidUtils.R_CLASS_NAME.equals(((PsiClass)resolvedElement).getName())) { - return null; - } - - final PsiFile containingFile = resolvedElement.getContainingFile(); - if (containingFile == null || !AndroidResourceUtil.isRJavaFile(facet, containingFile)) { + final Pair pair = AndroidResourceUtil.getReferredResourceField(facet, refExp); + if (pair == null) { return null; } + final String resClassName = pair.getFirst(); + final String resFieldName = pair.getSecond(); final List resourceList = facet.getLocalResourceManager().findResourcesByFieldName(resClassName, resFieldName); final PsiElement[] resources = resourceList.toArray(new PsiElement[resourceList.size()]); final PsiElement[] wrappedResources = new PsiElement[resources.length]; - + for (int i = 0; i < resources.length; i++) { final PsiElement resource = resources[i]; - - if (resource instanceof XmlAttributeValue && - resource instanceof PsiMetaOwner && + + if (resource instanceof XmlAttributeValue && + resource instanceof PsiMetaOwner && resource instanceof NavigationItem) { wrappedResources[i] = new ValueResourceElementWrapper((XmlAttributeValue)resource); } diff --git a/plugins/android/src/org/jetbrains/android/actions/AndroidCreateLayoutFileAction.java b/plugins/android/src/org/jetbrains/android/actions/AndroidCreateLayoutFileAction.java index 77de2e06e5b0..3e11642979a6 100644 --- a/plugins/android/src/org/jetbrains/android/actions/AndroidCreateLayoutFileAction.java +++ b/plugins/android/src/org/jetbrains/android/actions/AndroidCreateLayoutFileAction.java @@ -16,6 +16,7 @@ package org.jetbrains.android.actions; +import com.android.resources.ResourceFolderType; import com.android.resources.ResourceType; import com.intellij.CommonBundle; import com.intellij.facet.ProjectFacetManager; @@ -57,7 +58,7 @@ public class AndroidCreateLayoutFileAction extends CreateTypedResourceFileAction private String myLastRootComponentName; public AndroidCreateLayoutFileAction() { - super("Layout", "layout", "LinearLayout", false, false); + super("Layout", ResourceFolderType.LAYOUT, false, false); } @NotNull @@ -75,7 +76,7 @@ public class AndroidCreateLayoutFileAction extends CreateTypedResourceFileAction @Override protected PsiElement[] create(String newName, PsiDirectory directory) throws Exception { assert myLastRootComponentName != null; - return doCreate(newName, directory, myLastRootComponentName, false); + return doCreateAndNavigate(newName, directory, myLastRootComponentName, false); } @Override diff --git a/plugins/android/src/org/jetbrains/android/actions/CreateResourceDialog.java b/plugins/android/src/org/jetbrains/android/actions/CreateResourceDialog.java index 13239b7c5a48..68b7e5c6187a 100644 --- a/plugins/android/src/org/jetbrains/android/actions/CreateResourceDialog.java +++ b/plugins/android/src/org/jetbrains/android/actions/CreateResourceDialog.java @@ -73,15 +73,19 @@ public class CreateResourceDialog extends DialogWrapper { private final Map myResType2ActionMap = new HashMap(); private final DeviceConfiguratorPanel myDeviceConfiguratorPanel; private final AndroidFacet myFacet; + private final ResourceType myPredefinedResourceType; public CreateResourceDialog(@NotNull AndroidFacet facet, Collection actions, @Nullable ResourceType predefinedResourceType, @Nullable String predefinedFileName, + boolean chooseFileName, @NotNull Module module, boolean chooseModule) { super(facet.getModule().getProject()); myFacet = facet; + myPredefinedResourceType = predefinedResourceType; + myResTypeLabel.setLabelFor(myResourceTypeCombo); myResourceTypeCombo.registerUpDownHint(myFileNameField); myUpDownHint.setIcon(PlatformIcons.UP_DOWN_ARROWS); @@ -137,7 +141,7 @@ public class CreateResourceDialog extends DialogWrapper { @Override public void actionPerformed(ActionEvent e) { myDeviceConfiguratorPanel.applyEditors(); - updateRootElementCombo(); + updateRootElementTextField(); } }); @@ -153,8 +157,10 @@ public class CreateResourceDialog extends DialogWrapper { } if (predefinedFileName != null) { - myFileNameField.setVisible(false); - myFileNameLabel.setVisible(false); + if (!chooseFileName) { + myFileNameField.setVisible(false); + myFileNameLabel.setVisible(false); + } myFileNameField.setText(predefinedFileName); } @@ -183,11 +189,13 @@ public class CreateResourceDialog extends DialogWrapper { myDeviceConfiguratorPanel.updateAll(); myDeviceConfiguratorWrapper.add(myDeviceConfiguratorPanel, BorderLayout.CENTER); setOKActionEnabled(myDirectoryNameTextField.getText().length() > 0); - updateRootElementCombo(); + updateRootElementTextField(); init(); + + setTitle(AndroidBundle.message("new.resource.dialog.title")); } - private void updateRootElementCombo() { + private void updateRootElementTextField() { final CreateTypedResourceFileAction action = getSelectedAction(); if (action != null) { @@ -195,12 +203,19 @@ public class CreateResourceDialog extends DialogWrapper { myRootElementField = new TextFieldWithAutoCompletion( myFacet.getModule().getProject(), new TextFieldWithAutoCompletion.StringsCompletionProvider(allowedTagNames, null), true); myRootElementField.setEnabled(allowedTagNames.size() > 1); - myRootElementField.setText(!action.isChooseTagName() ? action.getDefaultRootTag() : ""); + myRootElementField.setText(!action.isChooseTagName() && myPredefinedResourceType != ResourceType.LAYOUT + ? action.getDefaultRootTag() + : ""); myRootElementFieldWrapper.removeAll(); myRootElementFieldWrapper.add(myRootElementField, BorderLayout.CENTER); } } + @NotNull + public String getFileName() { + return myFileNameField.getText().trim(); + } + private static boolean containsElement(@NotNull ListModel model, @NotNull Object objectToFind) { for (int i = 0, n = model.getSize(); i < n; i++) { if (objectToFind.equals(model.getElementAt(i))) { @@ -232,7 +247,10 @@ public class CreateResourceDialog extends DialogWrapper { } final String subdirName = getSubdirName(); - assert subdirName.length() > 0; + if (subdirName.length() == 0) { + Messages.showErrorDialog(myPanel, AndroidBundle.message("directory.not.specified.error"), CommonBundle.getErrorTitle()); + return; + } myValidator = createValidator(subdirName); if (myValidator == null || myValidator.checkInput(fileName) && myValidator.canClose(fileName)) { super.doOKAction(); @@ -266,7 +284,7 @@ public class CreateResourceDialog extends DialogWrapper { @Override public JComponent getPreferredFocusedComponent() { - if (myFileNameField.isVisible()) { + if (myFileNameField.getText().length() == 0) { return myFileNameField; } else if (myResourceTypeCombo.isVisible()) { @@ -275,7 +293,10 @@ public class CreateResourceDialog extends DialogWrapper { else if (myModuleCombo.isVisible()) { return myModuleCombo; } - return myDeviceConfiguratorPanel.getAvailableQualifiersList(); + else if (myRootElementFieldWrapper.isVisible()) { + return myRootElementField; + } + return myDirectoryNameTextField; } public CreateTypedResourceFileAction getSelectedAction() { diff --git a/plugins/android/src/org/jetbrains/android/actions/CreateResourceFileAction.java b/plugins/android/src/org/jetbrains/android/actions/CreateResourceFileAction.java index ce3d080544ff..b8a3218f0e99 100644 --- a/plugins/android/src/org/jetbrains/android/actions/CreateResourceFileAction.java +++ b/plugins/android/src/org/jetbrains/android/actions/CreateResourceFileAction.java @@ -30,6 +30,7 @@ import com.intellij.openapi.fileTypes.StdFileTypes; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.InputValidator; +import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.VirtualFile; @@ -90,39 +91,48 @@ public class CreateResourceFileAction extends CreateElementActionBase { }); } - // must be invoked in a write action - public static PsiElement[] createResourceFile(final Project project, - @NotNull AndroidFacet facet, + @NotNull + public static PsiElement[] createFileResource(@NotNull AndroidFacet facet, @NotNull final ResourceType resType, @NotNull String resName, - boolean chooseDirectory) { + boolean chooseResName) { final CreateResourceFileAction action = getInstance(); - String subdirName = resType.getName(); - VirtualFile resourceDir = facet.getLocalResourceManager().getResourceDir(); - - if (chooseDirectory) { - final MyDialog dialog = new MyDialog(facet, action.mySubactions.values(), resType, resName, action, facet.getModule(), true); - dialog.setTitle(AndroidBundle.message("new.resource.dialog.title")); - dialog.show(); - if (!dialog.isOK()) { - return PsiElement.EMPTY_ARRAY; - } - subdirName = dialog.getSubdirName(); - final AndroidFacet selectedFacet = AndroidFacet.getInstance(dialog.getSelectedModule()); - LOG.assertTrue(selectedFacet != null); - resourceDir = selectedFacet.getLocalResourceManager().getResourceDir(); + final MyDialog dialog = + new MyDialog(facet, action.mySubactions.values(), resType, resName, chooseResName, action, facet.getModule(), true); + dialog.show(); + if (!dialog.isOK()) { + return PsiElement.EMPTY_ARRAY; } - if (resourceDir != null) { - final PsiDirectory psiResDir = PsiManager.getInstance(project).findDirectory(resourceDir); - if (psiResDir != null) { - CreateElementActionBase.MyInputValidator validator = action.createValidator(project, psiResDir, subdirName); - if (validator.checkInput(resName) && validator.canClose(resName)) { - return validator.getCreatedElements(); - } - } + if (chooseResName) { + resName = dialog.getFileName(); } - return PsiElement.EMPTY_ARRAY; + final String subdirName = dialog.getSubdirName(); + final AndroidFacet selectedFacet = AndroidFacet.getInstance(dialog.getSelectedModule()); + LOG.assertTrue(selectedFacet != null); + + final VirtualFile resourceDir = selectedFacet.getLocalResourceManager().getResourceDir(); + final Project project = facet.getModule().getProject(); + final PsiDirectory psiResDir = resourceDir != null ? PsiManager.getInstance(project).findDirectory(resourceDir) : null; + + if (psiResDir == null) { + Messages.showErrorDialog(project, "Cannot find resource directory for module " + selectedFacet.getModule().getName(), + CommonBundle.getErrorTitle()); + return PsiElement.EMPTY_ARRAY; + } + final String finalResName = resName; + + final PsiElement[] elements = ApplicationManager.getApplication().runWriteAction(new Computable() { + @Nullable + @Override + public PsiElement[] compute() { + MyInputValidator validator = action.createValidator(project, psiResDir, subdirName); + return validator.checkInput(finalResName) && validator.canClose(finalResName) + ? validator.getCreatedElements() + : null; + } + }); + return elements != null ? elements : PsiElement.EMPTY_ARRAY; } @NotNull @@ -131,13 +141,13 @@ public class CreateResourceFileAction extends CreateElementActionBase { final AndroidFacet facet = AndroidFacet.getInstance(directory); LOG.assertTrue(facet != null); - MyDialog dialog = new MyDialog(facet, mySubactions.values(), null, null, CreateResourceFileAction.this, facet.getModule(), false) { + final MyDialog dialog = + new MyDialog(facet, mySubactions.values(), null, null, true, CreateResourceFileAction.this, facet.getModule(), false) { @Override protected InputValidator createValidator(@NotNull String subdirName) { return CreateResourceFileAction.this.createValidator(project, directory, subdirName); } }; - dialog.setTitle(AndroidBundle.message("new.resource.dialog.title")); dialog.show(); return PsiElement.EMPTY_ARRAY; } @@ -159,8 +169,11 @@ public class CreateResourceFileAction extends CreateElementActionBase { @Override protected PsiElement[] create(String newName, PsiDirectory directory) throws Exception { CreateTypedResourceFileAction action = getActionByDir(directory); + if (action == null) { + throw new IllegalArgumentException("Incorrect directory"); + } if (myRootElement != null && myRootElement.length() > 0) { - return action.doCreate(newName, directory, myRootElement, false); + return action.doCreateAndNavigate(newName, directory, myRootElement, false); } return action.create(newName, directory); } @@ -204,10 +217,11 @@ public class CreateResourceFileAction extends CreateElementActionBase { Collection actions, @Nullable ResourceType predefinedResourceType, @Nullable String predefinedFileName, + boolean chooseFileName, @NotNull CreateResourceFileAction action, @NotNull Module module, boolean chooseModule) { - super(facet, actions, predefinedResourceType, predefinedFileName, module, chooseModule); + super(facet, actions, predefinedResourceType, predefinedFileName, chooseFileName, module, chooseModule); myAction = action; } diff --git a/plugins/android/src/org/jetbrains/android/actions/CreateResourceFileActionGroup.java b/plugins/android/src/org/jetbrains/android/actions/CreateResourceFileActionGroup.java index 5024481ba13b..22c21899cf1a 100644 --- a/plugins/android/src/org/jetbrains/android/actions/CreateResourceFileActionGroup.java +++ b/plugins/android/src/org/jetbrains/android/actions/CreateResourceFileActionGroup.java @@ -16,6 +16,7 @@ package org.jetbrains.android.actions; +import com.android.resources.ResourceFolderType; import com.intellij.openapi.actionSystem.DefaultActionGroup; import org.jetbrains.android.dom.animation.AndroidAnimationUtils; import org.jetbrains.android.dom.animator.AndroidAnimatorUtil; @@ -40,7 +41,7 @@ public class CreateResourceFileActionGroup extends DefaultActionGroup { CreateResourceFileAction a = new CreateResourceFileAction(); a.add(new AndroidCreateLayoutFileAction()); - a.add(new CreateTypedResourceFileAction("XML", "xml", "PreferenceScreen", false, true) { + a.add(new CreateTypedResourceFileAction("XML", ResourceFolderType.XML, false, true) { @NotNull @Override public List getAllowedTagNames(@NotNull AndroidFacet facet) { @@ -48,7 +49,7 @@ public class CreateResourceFileActionGroup extends DefaultActionGroup { } }); - a.add(new CreateTypedResourceFileAction("Drawable", "drawable", "selector", false, true) { + a.add(new CreateTypedResourceFileAction("Drawable", ResourceFolderType.DRAWABLE, false, true) { @NotNull @Override public List getAllowedTagNames(@NotNull AndroidFacet facet) { @@ -56,11 +57,11 @@ public class CreateResourceFileActionGroup extends DefaultActionGroup { } }); - a.add(new CreateTypedResourceFileAction("Color", "color", "selector", false, false)); - a.add(new CreateTypedResourceFileAction("Values", "values", "resources", true, false)); - a.add(new CreateTypedResourceFileAction("Menu", "menu", "menu", false, false)); + a.add(new CreateTypedResourceFileAction("Color", ResourceFolderType.COLOR, false, false)); + a.add(new CreateTypedResourceFileAction("Values", ResourceFolderType.VALUES, true, false)); + a.add(new CreateTypedResourceFileAction("Menu", ResourceFolderType.MENU, false, false)); - a.add(new CreateTypedResourceFileAction("Animation", "anim", "set", false, true) { + a.add(new CreateTypedResourceFileAction("Animation", ResourceFolderType.ANIM, false, true) { @NotNull @Override public List getAllowedTagNames(@NotNull AndroidFacet facet) { @@ -68,7 +69,7 @@ public class CreateResourceFileActionGroup extends DefaultActionGroup { } }); - a.add(new CreateTypedResourceFileAction("Animator", "animator", "set", false, true) { + a.add(new CreateTypedResourceFileAction("Animator", ResourceFolderType.ANIMATOR, false, true) { @NotNull @Override public List getAllowedTagNames(@NotNull AndroidFacet facet) { diff --git a/plugins/android/src/org/jetbrains/android/actions/CreateTypedResourceFileAction.java b/plugins/android/src/org/jetbrains/android/actions/CreateTypedResourceFileAction.java index 5c32c6de9de6..f59644879537 100644 --- a/plugins/android/src/org/jetbrains/android/actions/CreateTypedResourceFileAction.java +++ b/plugins/android/src/org/jetbrains/android/actions/CreateTypedResourceFileAction.java @@ -16,11 +16,9 @@ package org.jetbrains.android.actions; +import com.android.resources.ResourceFolderType; import com.intellij.CommonBundle; import com.intellij.ide.actions.CreateElementActionBase; -import com.intellij.ide.fileTemplates.FileTemplate; -import com.intellij.ide.fileTemplates.FileTemplateManager; -import com.intellij.ide.fileTemplates.FileTemplateUtil; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.actionSystem.LangDataKeys; import com.intellij.openapi.application.ApplicationManager; @@ -38,7 +36,6 @@ import com.intellij.psi.xml.XmlFile; import com.intellij.psi.xml.XmlTag; import com.intellij.util.PsiNavigateUtil; import com.intellij.xml.refactoring.XmlTagInplaceRenamer; -import org.jetbrains.android.AndroidFileTemplateProvider; import org.jetbrains.android.facet.AndroidFacet; import org.jetbrains.android.util.AndroidBundle; import org.jetbrains.android.util.AndroidResourceUtil; @@ -48,36 +45,33 @@ import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Properties; /** * @author Eugene.Kudelevsky */ public class CreateTypedResourceFileAction extends CreateElementActionBase { - static final String ROOT_TAG_PROPERTY = "ROOT_TAG"; - private final String myResourceType; + private final ResourceFolderType myResourceType; private final String myResourcePresentableName; protected final String myDefaultRootTag; private final boolean myValuesResourceFile; private final boolean myChooseTagName; public CreateTypedResourceFileAction(@NotNull String resourcePresentableName, - @NotNull String resourceType, - @NotNull String defaultRootTag, + @NotNull ResourceFolderType resourceFolderType, boolean valuesResourceFile, boolean chooseTagName) { super(AndroidBundle.message("new.typed.resource.action.title", resourcePresentableName), AndroidBundle.message("new.typed.resource.action.description", resourcePresentableName), StdFileTypes.XML.getIcon()); - myResourceType = resourceType; + myResourceType = resourceFolderType; myResourcePresentableName = resourcePresentableName; - myDefaultRootTag = defaultRootTag; + myDefaultRootTag = getDefaultRootTabByResourceType(resourceFolderType); myValuesResourceFile = valuesResourceFile; myChooseTagName = chooseTagName; } public String getResourceType() { - return myResourceType; + return myResourceType.getName(); } @NotNull @@ -93,20 +87,12 @@ public class CreateTypedResourceFileAction extends CreateElementActionBase { @NotNull @Override protected PsiElement[] create(String newName, PsiDirectory directory) throws Exception { - return doCreate(newName, directory, myDefaultRootTag, myChooseTagName); + return doCreateAndNavigate(newName, directory, myDefaultRootTag, myChooseTagName); } - PsiElement[] doCreate(String newName, PsiDirectory directory, String rootTagName, boolean chooseTagName) throws Exception { - FileTemplateManager manager = FileTemplateManager.getInstance(); - String templateName = getTemplateName(); - FileTemplate template = manager.getJ2eeTemplate(templateName); - Properties properties = new Properties(); - if (!myValuesResourceFile) { - properties.setProperty(ROOT_TAG_PROPERTY, rootTagName); - } - PsiElement createdElement = FileTemplateUtil.createFromTemplate(template, newName, properties, directory); - assert createdElement instanceof XmlFile; - final XmlFile file = (XmlFile)createdElement; + PsiElement[] doCreateAndNavigate(String newName, PsiDirectory directory, String rootTagName, boolean chooseTagName) throws Exception { + final XmlFile file = AndroidResourceUtil + .createFileResource(newName, directory, rootTagName, myResourceType.getName(), myValuesResourceFile); doNavigate(file); if (chooseTagName) { @@ -124,26 +110,16 @@ public class CreateTypedResourceFileAction extends CreateElementActionBase { } } } - return new PsiElement[]{createdElement}; + return new PsiElement[]{file}; } protected void doNavigate(XmlFile file) { PsiNavigateUtil.navigate(file); } - private String getTemplateName() { - if (myValuesResourceFile) { - return AndroidFileTemplateProvider.VALUE_RESOURCE_FILE_TEMPLATE; - } - if ("layout".equals(myResourceType)) { - return AndroidFileTemplateProvider.LAYOUT_RESOURCE_FILE_TEMPLATE; - } - return AndroidFileTemplateProvider.RESOURCE_FILE_TEMPLATE; - } - @Override protected boolean isAvailable(DataContext context) { - return super.isAvailable(context) && doIsAvailable(context, myResourceType); + return super.isAvailable(context) && doIsAvailable(context, myResourceType.getName()); } public boolean isChooseTagName() { @@ -202,4 +178,28 @@ public class CreateTypedResourceFileAction extends CreateElementActionBase { public String toString() { return myResourcePresentableName; } + + @NotNull + public static String getDefaultRootTabByResourceType(@NotNull ResourceFolderType resourceType) { + switch (resourceType) { + case XML: + return "PreferenceScreen"; + case DRAWABLE: + return "selector"; + case COLOR: + return "selector"; + case VALUES: + return "resources"; + case MENU: + return "menu"; + case ANIM: + return "set"; + case ANIMATOR: + return "set"; + case LAYOUT: + return "LinearLayout"; + default: + } + throw new IllegalArgumentException("Incorrect resource folder type"); + } } diff --git a/plugins/android/src/org/jetbrains/android/actions/CreateXmlResourceDialog.java b/plugins/android/src/org/jetbrains/android/actions/CreateXmlResourceDialog.java index ab2e43d7f575..227d9075738b 100644 --- a/plugins/android/src/org/jetbrains/android/actions/CreateXmlResourceDialog.java +++ b/plugins/android/src/org/jetbrains/android/actions/CreateXmlResourceDialog.java @@ -83,13 +83,16 @@ public class CreateXmlResourceDialog extends DialogWrapper { public CreateXmlResourceDialog(@NotNull Module module, @NotNull ResourceType resourceType, @Nullable String predefinedName, - @Nullable String predefinedValue) { + @Nullable String predefinedValue, + boolean chooseName) { super(module.getProject()); myResourceType = resourceType; if (predefinedName != null && predefinedName.length() > 0) { - myNameLabel.setVisible(false); - myNameField.setVisible(false); + if (!chooseName) { + myNameLabel.setVisible(false); + myNameField.setVisible(false); + } myNameField.setText(predefinedName); } @@ -390,7 +393,7 @@ public class CreateXmlResourceDialog extends DialogWrapper { @Override public JComponent getPreferredFocusedComponent() { - return myNameField.isVisible() ? myNameField : myValueField; + return myNameField.getText().length() == 0 ? myNameField : myValueField; } @Override diff --git a/plugins/android/src/org/jetbrains/android/dom/converters/ResourceReferenceConverter.java b/plugins/android/src/org/jetbrains/android/dom/converters/ResourceReferenceConverter.java index 2c97811d6614..af899538c7a6 100644 --- a/plugins/android/src/org/jetbrains/android/dom/converters/ResourceReferenceConverter.java +++ b/plugins/android/src/org/jetbrains/android/dom/converters/ResourceReferenceConverter.java @@ -15,38 +15,26 @@ */ package org.jetbrains.android.dom.converters; -import com.android.AndroidConstants; import com.android.resources.ResourceType; -import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.codeInspection.LocalQuickFix; -import com.intellij.codeInspection.ProblemDescriptor; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.command.undo.UndoUtil; import com.intellij.openapi.components.ServiceManager; -import com.intellij.openapi.editor.Editor; import com.intellij.openapi.module.Module; -import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; import com.intellij.psi.PsiReference; import com.intellij.psi.xml.XmlAttribute; import com.intellij.psi.xml.XmlElement; import com.intellij.psi.xml.XmlTag; -import com.intellij.util.IncorrectOperationException; import com.intellij.util.xml.*; -import org.jetbrains.android.actions.CreateXmlResourceDialog; import org.jetbrains.android.dom.AdditionalConverter; import org.jetbrains.android.dom.AndroidResourceType; -import org.jetbrains.android.dom.resources.Item; -import org.jetbrains.android.dom.resources.ResourceElement; import org.jetbrains.android.dom.resources.ResourceValue; import org.jetbrains.android.facet.AndroidFacet; +import org.jetbrains.android.inspections.CreateFileResourceQuickFix; +import org.jetbrains.android.inspections.CreateValueResourceQuickFix; import org.jetbrains.android.resourceManagers.LocalResourceManager; import org.jetbrains.android.resourceManagers.ResourceManager; -import org.jetbrains.android.util.AndroidBundle; import org.jetbrains.android.util.AndroidCommonUtils; import org.jetbrains.android.util.AndroidResourceUtil; import org.jetbrains.annotations.NonNls; @@ -61,11 +49,6 @@ import static org.jetbrains.android.util.AndroidUtils.SYSTEM_RESOURCE_PACKAGE; * @author yole */ public class ResourceReferenceConverter extends ResolvingConverter implements CustomReferenceConverter { - private static final Set XML_FILE_RESOURCE_TYPES = EnumSet.of(ResourceType.ANIM, ResourceType.ANIMATOR, - ResourceType.INTERPOLATOR, ResourceType.LAYOUT, - ResourceType.MENU, ResourceType.XML, ResourceType.COLOR, - ResourceType.DRAWABLE); - private final List myResourceTypes; private ResolvingConverter myAdditionalConverter; private boolean myAdditionalConverterSoft = false; @@ -313,10 +296,10 @@ public class ResourceReferenceConverter extends ResolvingConverter fixes = new ArrayList(); if (AndroidResourceUtil.VALUE_RESOURCE_TYPES.contains(resType)) { - fixes.add(new MyCreateValueResourceQuickFix(facet, resType, resourceName, context.getFile())); + fixes.add(new CreateValueResourceQuickFix(facet, resType, resourceName, context.getFile(), false)); } - if (XML_FILE_RESOURCE_TYPES.contains(resType)) { - fixes.add(new MyCreateFileResourceQuickFix(facet, resType, resourceName, context.getFile())); + if (AndroidResourceUtil.XML_FILE_RESOURCE_TYPES.contains(resType)) { + fixes.add(new CreateFileResourceQuickFix(facet, resType, resourceName, context.getFile(), false)); } return fixes.toArray(new LocalQuickFix[fixes.size()]); } @@ -350,163 +333,4 @@ public class ResourceReferenceConverter extends ResolvingConverter list = manager.findValueResources(myResourceType.getName(), myResourceName); - if (list.size() == 1) { - ResourceElement element = list.get(0); - XmlTag tag = element.getXmlTag(); - tag.getValue().setText(""); - } - } - } - UndoUtil.markPsiFileForUndo(myFile); - } - } - - public static class MyCreateFileResourceQuickFix implements LocalQuickFix, IntentionAction { - private final AndroidFacet myFacet; - private final ResourceType myResourceType; - private final String myResourceName; - private final PsiFile myFile; - - public MyCreateFileResourceQuickFix(@NotNull AndroidFacet facet, - @NotNull ResourceType resourceType, - @NotNull String resourceName, - @NotNull PsiFile file) { - myFacet = facet; - myResourceType = resourceType; - myResourceName = resourceName; - myFile = file; - } - - @NotNull - public String getName() { - return AndroidBundle.message("create.file.resource.quickfix.name", myResourceName, - '\'' + myResourceType.getName() + "' directory"); - } - - @NotNull - @Override - public String getText() { - return AndroidBundle.message("create.file.resource.intention.name", myResourceType, myResourceName + ".xml"); - } - - @NotNull - public String getFamilyName() { - return AndroidBundle.message("quick.fixes.family"); - } - - @Override - public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { - return true; - } - - @Override - public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { - // simplified resource creating for batch mode - myFacet.getLocalResourceManager().addResourceFileAndNavigate(myResourceName, myResourceType, true); - UndoUtil.markPsiFileForUndo(myFile); - } - - @Override - public boolean startInWriteAction() { - return true; - } - - public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { - // simplified resource creating for batch mode - myFacet.getLocalResourceManager().addResourceFileAndNavigate(myResourceName, myResourceType, false); - UndoUtil.markPsiFileForUndo(myFile); - } - } } diff --git a/plugins/android/src/org/jetbrains/android/inspections/AndroidQuickFixProvider.java b/plugins/android/src/org/jetbrains/android/inspections/AndroidQuickFixProvider.java new file mode 100644 index 000000000000..39ec81858ca8 --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/inspections/AndroidQuickFixProvider.java @@ -0,0 +1,74 @@ +package org.jetbrains.android.inspections; + +import com.android.resources.ResourceType; +import com.intellij.codeInsight.daemon.QuickFixActionRegistrar; +import com.intellij.codeInsight.quickfix.UnresolvedReferenceQuickFixProvider; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleUtil; +import com.intellij.openapi.util.Pair; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiReferenceExpression; +import org.jetbrains.android.dom.manifest.Manifest; +import org.jetbrains.android.facet.AndroidFacet; +import org.jetbrains.android.util.AndroidResourceUtil; +import org.jetbrains.annotations.NotNull; + +/** + * @author Eugene.Kudelevsky + */ +public class AndroidQuickFixProvider extends UnresolvedReferenceQuickFixProvider { + @Override + public void registerFixes(PsiReferenceExpression exp, QuickFixActionRegistrar registrar) { + final Module contextModule = ModuleUtil.findModuleForPsiElement(exp); + if (contextModule == null) { + return; + } + + final AndroidFacet facet = AndroidFacet.getInstance(contextModule); + if (facet == null) { + return; + } + + final Manifest manifest = facet.getManifest(); + if (manifest == null) { + return; + } + + final String aPackage = manifest.getPackage().getValue(); + if (aPackage == null) { + return; + } + + final PsiFile contextFile = exp.getContainingFile(); + if (contextFile == null) { + return; + } + + final Pair pair = AndroidResourceUtil.getReferredResourceField(facet, exp); + if (pair == null) { + return; + } + final String resClassName = pair.getFirst(); + final String resFieldName = pair.getSecond(); + + final ResourceType resourceType = ResourceType.getEnum(resClassName); + if (resourceType == ResourceType.STYLEABLE || resourceType == ResourceType.ATTR) { + // todo: support + return; + } + + if (AndroidResourceUtil.VALUE_RESOURCE_TYPES.contains(resourceType)) { + registrar + .register(new CreateValueResourceQuickFix(facet, resourceType, resFieldName, contextFile, true)); + } + if (AndroidResourceUtil.XML_FILE_RESOURCE_TYPES.contains(resourceType)) { + registrar.register(new CreateFileResourceQuickFix(facet, resourceType, resFieldName, contextFile, true)); + } + } + + @NotNull + @Override + public Class getReferenceClass() { + return PsiReferenceExpression.class; + } +} diff --git a/plugins/android/src/org/jetbrains/android/inspections/CreateFileResourceQuickFix.java b/plugins/android/src/org/jetbrains/android/inspections/CreateFileResourceQuickFix.java new file mode 100644 index 000000000000..03b559ddf87f --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/inspections/CreateFileResourceQuickFix.java @@ -0,0 +1,117 @@ +package org.jetbrains.android.inspections; + +import com.android.resources.ResourceFolderType; +import com.android.resources.ResourceType; +import com.intellij.codeInsight.intention.HighPriorityAction; +import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.codeInspection.LocalQuickFix; +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.command.undo.UndoUtil; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Computable; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiDirectory; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiManager; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.android.actions.CreateResourceFileAction; +import org.jetbrains.android.actions.CreateTypedResourceFileAction; +import org.jetbrains.android.facet.AndroidFacet; +import org.jetbrains.android.util.AndroidBundle; +import org.jetbrains.android.util.AndroidResourceUtil; +import org.jetbrains.annotations.NotNull; + +/** +* @author Eugene.Kudelevsky +*/ +public class CreateFileResourceQuickFix implements LocalQuickFix, IntentionAction, HighPriorityAction { + private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.inspections.CreateFileResourceQuickFix"); + + private final AndroidFacet myFacet; + private final ResourceType myResourceType; + private final String myResourceName; + private final PsiFile myFile; + private final boolean myChooseResName; + + public CreateFileResourceQuickFix(@NotNull AndroidFacet facet, + @NotNull ResourceType resourceType, + @NotNull String resourceName, + @NotNull PsiFile file, + boolean chooseResName) { + myFacet = facet; + myResourceType = resourceType; + myResourceName = resourceName; + myFile = file; + myChooseResName = chooseResName; + } + + @NotNull + public String getName() { + return AndroidBundle.message("create.file.resource.quickfix.name", myResourceName, + '\'' + myResourceType.getName() + "' directory"); + } + + @NotNull + @Override + public String getText() { + return AndroidBundle.message("create.file.resource.intention.name", myResourceType, myResourceName + ".xml"); + } + + @NotNull + public String getFamilyName() { + return AndroidBundle.message("quick.fixes.family"); + } + + @Override + public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { + return true; + } + + @Override + public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { + final PsiElement[] createdElements = + CreateResourceFileAction.createFileResource(myFacet, myResourceType, myResourceName + ".xml", myChooseResName); + if (createdElements.length > 0) { + UndoUtil.markPsiFileForUndo(myFile); + } + } + + @Override + public boolean startInWriteAction() { + return false; + } + + public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { + final VirtualFile resourceDir = myFacet.getLocalResourceManager().getResourceDir(); + if (resourceDir == null) { + return; + } + final PsiDirectory psiResDir = PsiManager.getInstance(project).findDirectory(resourceDir); + if (psiResDir == null) { + return; + } + final String resDirName = myResourceType.getName(); + PsiDirectory resSubdir = psiResDir.findSubdirectory(resDirName); + + if (resSubdir == null) { + resSubdir = ApplicationManager.getApplication().runWriteAction(new Computable() { + public PsiDirectory compute() { + return psiResDir.createSubdirectory(resDirName); + } + }); + } + + try { + AndroidResourceUtil.createFileResource(myResourceName, resSubdir, CreateTypedResourceFileAction.getDefaultRootTabByResourceType( + ResourceFolderType.getFolderType(resDirName)), resDirName, false); + UndoUtil.markPsiFileForUndo(myFile); + } + catch (Exception e) { + LOG.error(e); + } + } +} diff --git a/plugins/android/src/org/jetbrains/android/inspections/CreateValueResourceQuickFix.java b/plugins/android/src/org/jetbrains/android/inspections/CreateValueResourceQuickFix.java new file mode 100644 index 000000000000..2a4b02d71415 --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/inspections/CreateValueResourceQuickFix.java @@ -0,0 +1,126 @@ +package org.jetbrains.android.inspections; + +import com.android.AndroidConstants; +import com.android.resources.ResourceType; +import com.intellij.codeInsight.intention.HighPriorityAction; +import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.codeInspection.LocalQuickFix; +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.command.undo.UndoUtil; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.PsiFile; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.android.actions.CreateXmlResourceDialog; +import org.jetbrains.android.facet.AndroidFacet; +import org.jetbrains.android.util.AndroidBundle; +import org.jetbrains.android.util.AndroidResourceUtil; +import org.jetbrains.annotations.NotNull; + +import java.util.Collections; +import java.util.List; + +/** +* @author Eugene.Kudelevsky +*/ +public class CreateValueResourceQuickFix implements LocalQuickFix, IntentionAction, HighPriorityAction { + private final AndroidFacet myFacet; + private final ResourceType myResourceType; + private final String myResourceName; + private final PsiFile myFile; + private final boolean myChooseName; + + public CreateValueResourceQuickFix(@NotNull AndroidFacet facet, + @NotNull ResourceType resourceType, + @NotNull String resourceName, + @NotNull PsiFile file, + boolean chooseName) { + myFacet = facet; + myResourceType = resourceType; + myResourceName = resourceName; + myFile = file; + myChooseName = chooseName; + } + + @NotNull + public String getName() { + return AndroidBundle.message("create.value.resource.quickfix.name", myResourceName, + AndroidResourceUtil.getDefaultResourceFileName(myResourceType.getName())); + } + + @NotNull + @Override + public String getText() { + return AndroidBundle.message("create.value.resource.intention.name", myResourceType, myResourceName); + } + + @NotNull + public String getFamilyName() { + return AndroidBundle.message("quick.fixes.family"); + } + + @Override + public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { + return true; + } + + @Override + public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { + doInvoke(); + } + + protected boolean doInvoke() { + if (ApplicationManager.getApplication().isUnitTestMode()) { + final String fileName = AndroidResourceUtil.getDefaultResourceFileName(myResourceType.getName()); + assert fileName != null; + + if (!AndroidResourceUtil.createValueResource(myFacet.getModule(), myResourceName, myResourceType, fileName, + Collections.singletonList(AndroidConstants.FD_RES_VALUES), "a")) { + return false; + } + } + else { + final CreateXmlResourceDialog dialog = new CreateXmlResourceDialog(myFacet.getModule(), myResourceType, myResourceName, null, + myChooseName); + dialog.setTitle("New " + StringUtil.capitalize(myResourceType.getDisplayName()) + " Value Resource"); + dialog.show(); + + if (!dialog.isOK()) { + return false; + } + + final Module moduleToPlaceResource = dialog.getModule(); + if (moduleToPlaceResource == null) { + return false; + } + final String fileName = dialog.getFileName(); + final List dirNames = dialog.getDirNames(); + final String resValue = dialog.getValue(); + final String resName = dialog.getResourceName(); + if (!AndroidResourceUtil.createValueResource(moduleToPlaceResource, resName, myResourceType, fileName, dirNames, resValue)) { + return false; + } + } + PsiDocumentManager.getInstance(myFile.getProject()).commitAllDocuments(); + UndoUtil.markPsiFileForUndo(myFile); + ApplicationManager.getApplication().invokeLater(new Runnable() { + public void run() { + ApplicationManager.getApplication().saveAll(); + } + }); + return true; + } + + @Override + public boolean startInWriteAction() { + return false; + } + + public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { + // todo: implement local fix + } +} diff --git a/plugins/android/src/org/jetbrains/android/intentions/AndroidAddStringResourceAction.java b/plugins/android/src/org/jetbrains/android/intentions/AndroidAddStringResourceAction.java index 007223e7ce29..bb62b105fa65 100644 --- a/plugins/android/src/org/jetbrains/android/intentions/AndroidAddStringResourceAction.java +++ b/plugins/android/src/org/jetbrains/android/intentions/AndroidAddStringResourceAction.java @@ -35,10 +35,8 @@ import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.TextRange; import com.intellij.psi.*; import com.intellij.psi.codeStyle.JavaCodeStyleManager; -import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.InheritanceUtil; import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.psi.util.PsiUtil; import com.intellij.psi.xml.XmlAttribute; import com.intellij.psi.xml.XmlAttributeValue; import com.intellij.psi.xml.XmlFile; @@ -180,7 +178,7 @@ public class AndroidAddStringResourceAction extends AbstractIntentionAction impl } if (resName == null) { - final CreateXmlResourceDialog dialog = new CreateXmlResourceDialog(facet.getModule(), ResourceType.STRING, null, value); + final CreateXmlResourceDialog dialog = new CreateXmlResourceDialog(facet.getModule(), ResourceType.STRING, null, value, false); dialog.setTitle("Extract String Resource"); dialog.show(); @@ -244,7 +242,7 @@ public class AndroidAddStringResourceAction extends AbstractIntentionAction impl ApplicationManager.getApplication().invokeLater(new Runnable() { @Override public void run() { - createStubResourceField(module, aPackage, resType, rJavaFieldName); + AndroidResourceUtil.createStubResourceField(module, aPackage, resType, rJavaFieldName); } }); @@ -292,37 +290,6 @@ public class AndroidAddStringResourceAction extends AbstractIntentionAction impl }); } - private static void createStubResourceField(final Module module, - final String aPackage, - final String resType, - final String fieldName) { - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - final Project project = module.getProject(); - final PsiClass[] classes = - JavaPsiFacade.getInstance(project).findClasses(aPackage + ".R", GlobalSearchScope.moduleScope(module)); - if (classes.length == 1) { - final PsiClass aClass = classes[0]; - final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project); - - PsiClass resTypeClass = aClass.findInnerClassByName(resType, false); - - if (resTypeClass == null) { - resTypeClass = (PsiClass)aClass.add(factory.createClass(resType)); - } - else if (resTypeClass.findFieldByName(fieldName, false) != null) { - return; - } - final PsiField psiField = (PsiField)resTypeClass.add(factory.createField(fieldName, PsiType.INT)); - PsiUtil.setModifierProperty(psiField, PsiModifier.PUBLIC, true); - PsiUtil.setModifierProperty(psiField, PsiModifier.STATIC, true); - PsiUtil.setModifierProperty(psiField, PsiModifier.FINAL, true); - } - } - }); - } - @Nullable private static String getPackage(@NotNull AndroidFacet facet) { Manifest manifest = facet.getManifest(); diff --git a/plugins/android/src/org/jetbrains/android/newProject/AndroidModuleBuilder.java b/plugins/android/src/org/jetbrains/android/newProject/AndroidModuleBuilder.java index 9be9195012cf..7f2cdc9a7183 100644 --- a/plugins/android/src/org/jetbrains/android/newProject/AndroidModuleBuilder.java +++ b/plugins/android/src/org/jetbrains/android/newProject/AndroidModuleBuilder.java @@ -17,6 +17,7 @@ package org.jetbrains.android.newProject; import com.android.AndroidConstants; +import com.android.resources.ResourceType; import com.android.sdklib.IAndroidTarget; import com.android.sdklib.SdkConstants; import com.intellij.CommonBundle; @@ -78,6 +79,7 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Properties; @@ -425,7 +427,10 @@ public class AndroidModuleBuilder extends JavaModuleBuilder { final String normalizedAppName = AndroidResourceUtil.normalizeXmlResourceValue(myApplicationName.replace("\\", "\\\\")); if (appNameResElement == null) { - manager.addValueResource("string", appNameResource, normalizedAppName); + final String fileName = AndroidResourceUtil.getDefaultResourceFileName(ResourceType.STRING.getName()); + assert fileName != null; + AndroidResourceUtil.createValueResource(facet.getModule(), appNameResource, ResourceType.STRING, fileName, Collections + .singletonList(AndroidConstants.FD_RES_VALUES), normalizedAppName); } else { appNameResElement.setStringValue(normalizedAppName); diff --git a/plugins/android/src/org/jetbrains/android/resourceManagers/LocalResourceManager.java b/plugins/android/src/org/jetbrains/android/resourceManagers/LocalResourceManager.java index f4067bb3593f..28159de08edd 100644 --- a/plugins/android/src/org/jetbrains/android/resourceManagers/LocalResourceManager.java +++ b/plugins/android/src/org/jetbrains/android/resourceManagers/LocalResourceManager.java @@ -22,10 +22,8 @@ import com.intellij.CommonBundle; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.Module; -import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Pair; -import com.intellij.openapi.vfs.ReadonlyStatusHandler; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiElement; @@ -33,13 +31,11 @@ import com.intellij.psi.PsiField; import com.intellij.psi.PsiFile; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.xml.XmlFile; -import com.intellij.util.IncorrectOperationException; import com.intellij.util.containers.HashMap; import com.intellij.util.containers.HashSet; import com.intellij.util.indexing.FileBasedIndex; import org.jetbrains.android.AndroidFileTemplateProvider; import org.jetbrains.android.AndroidValueResourcesIndex; -import org.jetbrains.android.actions.CreateResourceFileAction; import org.jetbrains.android.dom.attrs.AttributeDefinitions; import org.jetbrains.android.dom.resources.Attr; import org.jetbrains.android.dom.resources.DeclareStyleable; @@ -57,8 +53,6 @@ import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.util.*; -import static org.jetbrains.android.util.AndroidUtils.loadDomElement; - /** * @author Eugene.Kudelevsky */ @@ -263,50 +257,6 @@ public class LocalResourceManager extends ResourceManager { return result; } - // must be invoked in a write action - @Nullable - public VirtualFile addResourceFileAndNavigate(@NotNull final String fileOrResourceName, - @NotNull ResourceType resType, - boolean chooseDirectory) { - Project project = myModule.getProject(); - PsiElement[] createdElements = - CreateResourceFileAction.createResourceFile(project, myFacet, resType, fileOrResourceName, chooseDirectory); - if (createdElements.length == 0) return null; - assert createdElements.length == 1; - PsiElement element = createdElements[0]; - assert element instanceof PsiFile; - return ((PsiFile)element).getVirtualFile(); - } - - // must be invoked in a write action - @Nullable - public ResourceElement addValueResource(@NotNull final String type, @NotNull final String name, @Nullable final String value) { - String resourceFileName = AndroidResourceUtil.getDefaultResourceFileName(type); - if (resourceFileName == null) { - throw new IllegalArgumentException("Incorrect resource type"); - } - VirtualFile resFile = findOrCreateResourceFile(resourceFileName); - if (resFile == null || - !ReadonlyStatusHandler.ensureFilesWritable(myModule.getProject(), resFile)) { - return null; - } - final Resources resources = loadDomElement(myModule, resFile, Resources.class); - if (resources == null) { - if (ApplicationManager.getApplication().isUnitTestMode()) { - throw new IncorrectOperationException("invalid strings.xml"); - } - Messages.showErrorDialog(myModule.getProject(), AndroidBundle.message("not.resource.file.error", resourceFileName), - CommonBundle.getErrorTitle()); - return null; - } - ResourceElement element = AndroidResourceUtil.addValueResource(type, resources); - element.getName().setValue(name); - if (value != null) { - element.setStringValue(value); - } - return element; - } - @Nullable private VirtualFile findOrCreateChildDir(@NotNull final VirtualFile dir, @NotNull final String name) { try { diff --git a/plugins/android/src/org/jetbrains/android/util/AndroidResourceUtil.java b/plugins/android/src/org/jetbrains/android/util/AndroidResourceUtil.java index dcbdc39a17a1..0fdc4a277520 100644 --- a/plugins/android/src/org/jetbrains/android/util/AndroidResourceUtil.java +++ b/plugins/android/src/org/jetbrains/android/util/AndroidResourceUtil.java @@ -21,6 +21,9 @@ import com.android.resources.ResourceType; import com.android.sdklib.SdkConstants; import com.intellij.CommonBundle; import com.intellij.ide.actions.CreateElementActionBase; +import com.intellij.ide.fileTemplates.FileTemplate; +import com.intellij.ide.fileTemplates.FileTemplateManager; +import com.intellij.ide.fileTemplates.FileTemplateUtil; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.Module; @@ -30,13 +33,17 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ModulePackageIndex; import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.ReadonlyStatusHandler; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.util.PsiUtil; import com.intellij.psi.xml.XmlAttribute; import com.intellij.psi.xml.XmlAttributeValue; +import com.intellij.psi.xml.XmlFile; import com.intellij.psi.xml.XmlTag; import com.intellij.util.ArrayUtil; import com.intellij.util.IncorrectOperationException; @@ -68,6 +75,11 @@ public class AndroidResourceUtil { ResourceType.ID, ResourceType.BOOL, ResourceType.INTEGER); public static final Set REFERRABLE_RESOURCE_TYPES = EnumSet.noneOf(ResourceType.class); + public static final Set XML_FILE_RESOURCE_TYPES = EnumSet.of(ResourceType.ANIM, ResourceType.ANIMATOR, + ResourceType.INTERPOLATOR, ResourceType.LAYOUT, + ResourceType.MENU, ResourceType.XML, ResourceType.COLOR, + ResourceType.DRAWABLE); + static final String ROOT_TAG_PROPERTY = "ROOT_TAG"; private AndroidResourceUtil() { } @@ -606,11 +618,11 @@ public class AndroidResourceUtil { } private static boolean addValueResource(@NotNull AndroidFacet facet, - @NotNull String resourceName, - @NotNull ResourceType resourceType, + @NotNull final String resourceName, + @NotNull final ResourceType resourceType, @NotNull String fileName, @NotNull List dirNames, - @NotNull String value) throws Exception { + @NotNull final String value) throws Exception { if (dirNames.size() == 0) { return false; } @@ -638,14 +650,19 @@ public class AndroidResourceUtil { resourcesElements[i] = resources; } - for (Resources resources : resourcesElements) { - final ResourceElement element = addValueResource(resourceType.getName(), resources); - element.getName().setValue(resourceName); + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + for (Resources resources : resourcesElements) { + final ResourceElement element = addValueResource(resourceType.getName(), resources); + element.getName().setValue(resourceName); - if (value.length() > 0) { - element.setStringValue(value); + if (value.length() > 0) { + element.setStringValue(value); + } + } } - } + }); return true; } @@ -691,4 +708,99 @@ public class AndroidResourceUtil { Messages.showErrorDialog(project, message, CommonBundle.getErrorTitle()); } } + + @Nullable + public static Pair getReferredResourceField(@NotNull AndroidFacet facet, @NotNull PsiReferenceExpression exp) { + final String resFieldName = exp.getReferenceName(); + if (resFieldName == null || resFieldName.length() == 0) { + return null; + } + + PsiExpression qExp = exp.getQualifierExpression(); + if (!(qExp instanceof PsiReferenceExpression)) { + return null; + } + final PsiReferenceExpression resClassReference = (PsiReferenceExpression)qExp; + + final String resClassName = resClassReference.getReferenceName(); + if (resClassName == null || resClassName.length() == 0) { + return null; + } + + qExp = resClassReference.getQualifierExpression(); + if (!(qExp instanceof PsiReferenceExpression)) { + return null; + } + + final PsiElement resolvedElement = ((PsiReferenceExpression)qExp).resolve(); + if (!(resolvedElement instanceof PsiClass) || + !AndroidUtils.R_CLASS_NAME.equals(((PsiClass)resolvedElement).getName())) { + return null; + } + + final PsiFile containingFile = resolvedElement.getContainingFile(); + if (containingFile == null || !isRJavaFile(facet, containingFile)) { + return null; + } + return new Pair(resClassName, resFieldName); + } + + public static void createStubResourceField(@NotNull final Module module, + @NotNull final String aPackage, + @NotNull final String resClassName, + @NotNull final String resFieldName) { + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + final Project project = module.getProject(); + final PsiClass[] classes = + JavaPsiFacade.getInstance(project).findClasses(aPackage + ".R", GlobalSearchScope.moduleScope(module)); + if (classes.length == 1) { + final PsiClass aClass = classes[0]; + final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project); + + PsiClass resTypeClass = aClass.findInnerClassByName(resClassName, false); + + if (resTypeClass == null) { + resTypeClass = (PsiClass)aClass.add(factory.createClass(resClassName)); + } + else if (resTypeClass.findFieldByName(resFieldName, false) != null) { + return; + } + final PsiField psiField = (PsiField)resTypeClass.add(factory.createField(resFieldName, PsiType.INT)); + PsiUtil.setModifierProperty(psiField, PsiModifier.PUBLIC, true); + PsiUtil.setModifierProperty(psiField, PsiModifier.STATIC, true); + PsiUtil.setModifierProperty(psiField, PsiModifier.FINAL, true); + } + } + }); + } + + @NotNull + public static XmlFile createFileResource(@NotNull String fileName, + @NotNull PsiDirectory resSubdir, + @NotNull String rootTagName, + @NotNull String resourceType, + boolean valuesResourceFile) throws Exception { + FileTemplateManager manager = FileTemplateManager.getInstance(); + String templateName = getTemplateName(resourceType, valuesResourceFile); + FileTemplate template = manager.getJ2eeTemplate(templateName); + Properties properties = new Properties(); + if (!valuesResourceFile) { + properties.setProperty(ROOT_TAG_PROPERTY, rootTagName); + } + PsiElement createdElement = FileTemplateUtil.createFromTemplate(template, fileName, properties, resSubdir); + assert createdElement instanceof XmlFile; + return (XmlFile)createdElement; + } + + private static String getTemplateName(String resourceType, boolean valuesResourceFile) { + if (valuesResourceFile) { + return AndroidFileTemplateProvider.VALUE_RESOURCE_FILE_TEMPLATE; + } + if ("layout".equals(resourceType)) { + return AndroidFileTemplateProvider.LAYOUT_RESOURCE_FILE_TEMPLATE; + } + return AndroidFileTemplateProvider.RESOURCE_FILE_TEMPLATE; + } } diff --git a/plugins/android/testSrc/org/jetbrains/android/dom/AndroidLayoutDomTest.java b/plugins/android/testSrc/org/jetbrains/android/dom/AndroidLayoutDomTest.java index de12c53a9e0b..d859ac793252 100644 --- a/plugins/android/testSrc/org/jetbrains/android/dom/AndroidLayoutDomTest.java +++ b/plugins/android/testSrc/org/jetbrains/android/dom/AndroidLayoutDomTest.java @@ -4,13 +4,12 @@ import com.android.sdklib.SdkConstants; import com.intellij.codeInsight.TargetElementUtilBase; import com.intellij.codeInsight.daemon.impl.HighlightInfo; import com.intellij.codeInsight.intention.IntentionAction; -import com.intellij.codeInspection.actions.CleanupInspectionIntention; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; -import org.jetbrains.android.dom.converters.ResourceReferenceConverter; +import org.jetbrains.android.inspections.CreateValueResourceQuickFix; import java.io.IOException; import java.util.ArrayList; @@ -351,7 +350,7 @@ public class AndroidLayoutDomTest extends AndroidDomTest { if (ranges != null) { for (Pair pair : ranges) { final IntentionAction action = pair.getFirst().getAction(); - if (action instanceof ResourceReferenceConverter.MyCreateValueResourceQuickFix) { + if (action instanceof CreateValueResourceQuickFix) { actions.add(action); } } @@ -368,44 +367,6 @@ public class AndroidLayoutDomTest extends AndroidDomTest { myFixture.checkResultByFile("res/values/drawables.xml", testFolder + '/' + getTestName(true) + "_drawable_after.xml", true); } - public void testCreateResourceFromUsageCleanUp() throws Throwable { - final VirtualFile virtualFile = copyFileToProject(getTestName(true) + ".xml"); - myFixture.configureFromExistingVirtualFile(virtualFile); - final List infos = myFixture.doHighlighting(); - - final List actions = new ArrayList(); - - for (HighlightInfo info : infos) { - final List> ranges = info.quickFixActionRanges; - - if (ranges != null) { - for (Pair pair : ranges) { - final HighlightInfo.IntentionActionDescriptor descriptor = pair.getFirst(); - if (descriptor.getAction() instanceof ResourceReferenceConverter.MyCreateValueResourceQuickFix) { - final List options = descriptor.getOptions(myFixture.getFile(), myFixture.getEditor()); - - if (options != null) { - for (IntentionAction option : options) { - if (option instanceof CleanupInspectionIntention) { - actions.add(option); - } - } - } - } - } - } - } - assertEquals(1, actions.size()); - - new WriteCommandAction.Simple(getProject()) { - @Override - protected void run() throws Throwable { - actions.get(0).invoke(getProject(), myFixture.getEditor(), myFixture.getFile()); - } - }.execute(); - myFixture.checkResultByFile("res/values/drawables.xml", testFolder + '/' + getTestName(true) + "_drawable_after.xml", true); - } - public void testXsdFile1() throws Throwable { final VirtualFile virtualFile = copyFileToProject("XsdFile.xsd", "res/raw/XsdFile.xsd"); myFixture.configureFromExistingVirtualFile(virtualFile); diff --git a/plugins/android/testSrc/org/jetbrains/android/dom/AndroidValueResourcesTest.java b/plugins/android/testSrc/org/jetbrains/android/dom/AndroidValueResourcesTest.java index 444486328e12..93c97fe11840 100644 --- a/plugins/android/testSrc/org/jetbrains/android/dom/AndroidValueResourcesTest.java +++ b/plugins/android/testSrc/org/jetbrains/android/dom/AndroidValueResourcesTest.java @@ -27,7 +27,7 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiReference; import com.intellij.psi.xml.XmlAttributeValue; -import org.jetbrains.android.dom.converters.ResourceReferenceConverter; +import org.jetbrains.android.inspections.CreateValueResourceQuickFix; import java.util.ArrayList; import java.util.List; @@ -199,7 +199,7 @@ public class AndroidValueResourcesTest extends AndroidDomTest { if (ranges != null) { for (Pair pair : ranges) { final IntentionAction action = pair.getFirst().getAction(); - if (action instanceof ResourceReferenceConverter.MyCreateValueResourceQuickFix) { + if (action instanceof CreateValueResourceQuickFix) { actions.add(action); } }