From e641a6f01f3822d2b2e0758dfdafe3e3ed5d571c Mon Sep 17 00:00:00 2001 From: Eugene Kudelevsky Date: Tue, 24 Jul 2012 13:30:34 +0400 Subject: [PATCH] IDEA-89094 Validate names of android resources --- .../messages/AndroidBundle.properties | 5 +++- .../actions/CreateResourceFileDialog.java | 7 +++++ .../CreateTypedResourceFileAction.java | 19 +++++++++++- .../dom/resources/ResourceElement.java | 2 ++ .../dom/resources/ResourceNameConverter.java | 30 +++++++++++++++++++ .../android/util/AndroidResourceUtil.java | 14 +++++++++ .../testData/dom/resources/nameValidation.xml | 9 ++++++ .../dom/AndroidValueResourcesTest.java | 4 +++ 8 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 plugins/android/src/org/jetbrains/android/dom/resources/ResourceNameConverter.java create mode 100644 plugins/android/testData/dom/resources/nameValidation.xml diff --git a/plugins/android/resources/messages/AndroidBundle.properties b/plugins/android/resources/messages/AndroidBundle.properties index 98fd6cb89e9f..a991cb90d471 100644 --- a/plugins/android/resources/messages/AndroidBundle.properties +++ b/plugins/android/resources/messages/AndroidBundle.properties @@ -417,4 +417,7 @@ cannot.find.zip.align=The zipalign tool was not found in the SDK.\n\nPlease upda file.name.not.specified.error=File name is not specified root.element.not.specified.error=Root element is not specified directory.not.specified.error=Directory is not specified -android.manifest.merger.not.supported.error=Manifest merging is not supported. Please, reconfigure your manifest files \ No newline at end of file +android.manifest.merger.not.supported.error=Manifest merging is not supported. Please, reconfigure your manifest files +invalid.file.resource.name.error=Resource file name must contain only lowercase a-z, 0-9, or _ +invalid.file.resource.name.error1=Invalid resource file name +invalid.resource.name.error=Invalid resource name ''{0}'' \ No newline at end of file diff --git a/plugins/android/src/org/jetbrains/android/actions/CreateResourceFileDialog.java b/plugins/android/src/org/jetbrains/android/actions/CreateResourceFileDialog.java index ecd4adbde004..a4bd770f4830 100644 --- a/plugins/android/src/org/jetbrains/android/actions/CreateResourceFileDialog.java +++ b/plugins/android/src/org/jetbrains/android/actions/CreateResourceFileDialog.java @@ -34,6 +34,7 @@ import org.jetbrains.android.facet.AndroidFacet; import org.jetbrains.android.uipreview.DeviceConfiguratorPanel; import org.jetbrains.android.uipreview.InvalidOptionValueException; import org.jetbrains.android.util.AndroidBundle; +import org.jetbrains.android.util.AndroidResourceUtil; import org.jetbrains.android.util.AndroidUtils; import org.jetbrains.android.util.ModuleListCellRendererWrapper; import org.jetbrains.annotations.NotNull; @@ -252,6 +253,12 @@ public class CreateResourceFileDialog extends DialogWrapper { Messages.showErrorDialog(myPanel, AndroidBundle.message("directory.not.specified.error"), CommonBundle.getErrorTitle()); return; } + + final String errorMessage = AndroidResourceUtil.getInvalidResourceFileNameMessage(fileName); + if (errorMessage != null) { + Messages.showErrorDialog(myPanel, errorMessage, CommonBundle.getErrorTitle()); + return; + } myValidator = createValidator(subdirName); if (myValidator == null || myValidator.checkInput(fileName) && myValidator.canClose(fileName)) { super.doOKAction(); diff --git a/plugins/android/src/org/jetbrains/android/actions/CreateTypedResourceFileAction.java b/plugins/android/src/org/jetbrains/android/actions/CreateTypedResourceFileAction.java index 10074fea9763..d067378f1d2b 100644 --- a/plugins/android/src/org/jetbrains/android/actions/CreateTypedResourceFileAction.java +++ b/plugins/android/src/org/jetbrains/android/actions/CreateTypedResourceFileAction.java @@ -27,6 +27,7 @@ import com.intellij.openapi.editor.Editor; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.fileTypes.StdFileTypes; import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.InputValidatorEx; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Computable; import com.intellij.psi.PsiDirectory; @@ -78,7 +79,7 @@ public class CreateTypedResourceFileAction extends CreateElementActionBase { @NotNull @Override protected PsiElement[] invokeDialog(Project project, PsiDirectory directory) { - MyInputValidator validator = new MyInputValidator(project, directory); + MyInputValidator validator = new MyValidator(project, directory); Messages.showInputDialog(project, AndroidBundle.message("new.file.dialog.text"), AndroidBundle.message("new.typed.resource.dialog.title", myResourcePresentableName), Messages.getQuestionIcon(), "", validator); @@ -203,4 +204,20 @@ public class CreateTypedResourceFileAction extends CreateElementActionBase { } throw new IllegalArgumentException("Incorrect resource folder type"); } + + private class MyValidator extends MyInputValidator implements InputValidatorEx { + public MyValidator(Project project, PsiDirectory directory) { + super(project, directory); + } + + @Override + public boolean checkInput(String inputString) { + return getErrorText(inputString) == null; + } + + @Override + public String getErrorText(String inputString) { + return AndroidResourceUtil.getInvalidResourceFileNameMessage(inputString); + } + } } diff --git a/plugins/android/src/org/jetbrains/android/dom/resources/ResourceElement.java b/plugins/android/src/org/jetbrains/android/dom/resources/ResourceElement.java index d870c9971240..7d18b9f8b035 100644 --- a/plugins/android/src/org/jetbrains/android/dom/resources/ResourceElement.java +++ b/plugins/android/src/org/jetbrains/android/dom/resources/ResourceElement.java @@ -15,6 +15,7 @@ */ package org.jetbrains.android.dom.resources; +import com.intellij.util.xml.Convert; import com.intellij.util.xml.GenericAttributeValue; import com.intellij.util.xml.GenericDomValue; import com.intellij.util.xml.Required; @@ -25,5 +26,6 @@ import org.jetbrains.android.dom.AndroidDomElement; */ public interface ResourceElement extends GenericDomValue, AndroidDomElement { @Required + @Convert(ResourceNameConverter.class) GenericAttributeValue getName(); } diff --git a/plugins/android/src/org/jetbrains/android/dom/resources/ResourceNameConverter.java b/plugins/android/src/org/jetbrains/android/dom/resources/ResourceNameConverter.java new file mode 100644 index 000000000000..27970394b212 --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/dom/resources/ResourceNameConverter.java @@ -0,0 +1,30 @@ +package org.jetbrains.android.dom.resources; + +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.xml.ConvertContext; +import com.intellij.util.xml.Converter; +import org.jetbrains.android.util.AndroidBundle; +import org.jetbrains.android.util.AndroidResourceUtil; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.Nullable; + +/** + * @author Eugene.Kudelevsky + */ +public class ResourceNameConverter extends Converter { + @Override + public String fromString(@Nullable @NonNls String s, ConvertContext context) { + return s != null && StringUtil.isJavaIdentifier(AndroidResourceUtil.getFieldNameByResourceName(s)) + ? s : null; + } + + @Override + public String toString(@Nullable String s, ConvertContext context) { + return s; + } + + @Override + public String getErrorMessage(@Nullable String s, ConvertContext context) { + return AndroidBundle.message("invalid.resource.name.error", s); + } +} diff --git a/plugins/android/src/org/jetbrains/android/util/AndroidResourceUtil.java b/plugins/android/src/org/jetbrains/android/util/AndroidResourceUtil.java index f54e4cf1afd5..15ce866737bc 100644 --- a/plugins/android/src/org/jetbrains/android/util/AndroidResourceUtil.java +++ b/plugins/android/src/org/jetbrains/android/util/AndroidResourceUtil.java @@ -806,4 +806,18 @@ public class AndroidResourceUtil { public static String getFieldNameByResourceName(@NotNull String fieldName) { return fieldName.replace('.', '_').replace('-', '_').replace(':', '_'); } + + @Nullable + public static String getInvalidResourceFileNameMessage(@NotNull String fileName) { + for (int i = 0, n = fileName.length(); i < n; i++) { + char c = fileName.charAt(i); + if (!((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_' || c == '.')) { + return AndroidBundle.message("invalid.file.resource.name.error"); + } + } + if (!StringUtil.isJavaIdentifier(getFieldNameByResourceName(fileName))) { + return AndroidBundle.message("invalid.file.resource.name.error1"); + } + return null; + } } diff --git a/plugins/android/testData/dom/resources/nameValidation.xml b/plugins/android/testData/dom/resources/nameValidation.xml new file mode 100644 index 000000000000..acf5c477d4a6 --- /dev/null +++ b/plugins/android/testData/dom/resources/nameValidation.xml @@ -0,0 +1,9 @@ + + a + a + a + 1 + 1 + 1 + 1 + \ No newline at end of file diff --git a/plugins/android/testSrc/org/jetbrains/android/dom/AndroidValueResourcesTest.java b/plugins/android/testSrc/org/jetbrains/android/dom/AndroidValueResourcesTest.java index f504be57df66..0cba392db1f8 100644 --- a/plugins/android/testSrc/org/jetbrains/android/dom/AndroidValueResourcesTest.java +++ b/plugins/android/testSrc/org/jetbrains/android/dom/AndroidValueResourcesTest.java @@ -214,6 +214,10 @@ public class AndroidValueResourcesTest extends AndroidDomTest { doTestHighlighting(); } + public void testNameValidation() throws Throwable { + doTestHighlighting(); + } + public void testResourceReferenceAsValueCompletion1() throws Throwable { doTestCompletion(); }