From 0551f18acb01a9d79a5473d3fd2057d2bd02df12 Mon Sep 17 00:00:00 2001 From: "Denis.Zhdanov" Date: Thu, 13 Oct 2011 17:10:14 +0400 Subject: [PATCH] IDEA-54829 When editing a resource bundle, IntelliJ generates spurious double backslashes Corrected property values (un)escaping processing --- .../editor/ResourceBundleEditor.java | 28 +++----- .../properties/editor/ResourceBundleUtil.java | 68 +++++++++++++++++++ .../editor/ResourceBundleUtilTest.java | 58 ++++++++++++++++ 3 files changed, 135 insertions(+), 19 deletions(-) create mode 100644 plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleUtil.java create mode 100644 plugins/properties/testSrc/com/intellij/lang/properties/editor/ResourceBundleUtilTest.java diff --git a/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleEditor.java b/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleEditor.java index a52722f458d1..825b166550b3 100644 --- a/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleEditor.java +++ b/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleEditor.java @@ -318,7 +318,14 @@ public class ResourceBundleEditor extends UserDataHolderBase implements FileEdit if (editor == null) continue; reinitSettings(editor); IProperty property = propertiesFile.findPropertyByKey(propertyName); - final String value = property == null ? "" : property.getValue(); + final String value; + if (property == null) { + value = ""; + } + else { + String rawValue = property.getValue(); + value = rawValue == null ? "" : ResourceBundleUtil.fromPropertyValueToValueEditor(rawValue); + } final Document document = editor.getDocument(); CommandProcessor.getInstance().executeCommand(null, new Runnable() { public void run() { @@ -362,7 +369,7 @@ public class ResourceBundleEditor extends UserDataHolderBase implements FileEdit else { myBackSlashPressed.remove(propertiesFile); } - String value = getPropertyValueFromText(text); + String value = ResourceBundleUtil.fromValueEditorToPropertyValue(text); IProperty property = propertiesFile.findPropertyByKey(propertyName); try { if (property == null) { @@ -377,23 +384,6 @@ public class ResourceBundleEditor extends UserDataHolderBase implements FileEdit } } - private static String getPropertyValueFromText(final String text) { - StringBuilder value = new StringBuilder(); - for (int i=0; i propertiesFiles = myResourceBundle.getPropertiesFiles(myProject); for (final PropertiesFile propertiesFile : propertiesFiles) { diff --git a/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleUtil.java b/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleUtil.java new file mode 100644 index 000000000000..e36cadf3f21b --- /dev/null +++ b/plugins/properties/src/com/intellij/lang/properties/editor/ResourceBundleUtil.java @@ -0,0 +1,68 @@ +package com.intellij.lang.properties.editor; + +import gnu.trove.TIntHashSet; +import org.jetbrains.annotations.NotNull; + +import java.io.Reader; +import java.util.Properties; + +/** + * @author Denis Zhdanov + * @since 10/5/11 2:35 PM + */ +public class ResourceBundleUtil { + + private static final TIntHashSet SYMBOLS_TO_ESCAPE = new TIntHashSet(new int[]{'#', '!', '=', ':'}); + private static final char ESCAPE_SYMBOL = '\\'; + + private ResourceBundleUtil() { + } + + /** + * Allows to map given 'raw' property value text to the 'user-friendly' text to show at the resource bundle editor. + *

+ * Note: please refer to {@link Properties#load(Reader)} contract for the property value escape rules. + * + * @param text 'raw' property value text + * @return 'user-friendly' text to show at the resource bundle editor + */ + @NotNull + public static String fromPropertyValueToValueEditor(@NotNull String text) { + StringBuilder buffer = new StringBuilder(); + boolean escaped = false; + for (int i = 0; i < text.length(); i++) { + char c = text.charAt(i); + if (c == ESCAPE_SYMBOL && !escaped) { + escaped = true; + continue; + } + buffer.append(c); + escaped = false; + } + return buffer.toString(); + } + + /** + * Perform reverse operation to {@link #fromPropertyValueToValueEditor(String)}. + * + * @param text 'user-friendly' text shown to the user at the resource bundle editor + * @return 'raw' value to store at the *.properties file + */ + @NotNull + public static String fromValueEditorToPropertyValue(@NotNull String text) { + StringBuilder buffer = new StringBuilder(); + for (int i = 0; i < text.length(); i++) { + char c = text.charAt(i); + + if ((i == 0 && (c == ' ' || c == '\t')) // Leading white space + || c == '\n' // Multi-line value + || c == ESCAPE_SYMBOL // Escaped 'escape' symbol + || SYMBOLS_TO_ESCAPE.contains(c)) // Special symbol + { + buffer.append(ESCAPE_SYMBOL); + } + buffer.append(c); + } + return buffer.toString(); + } +} diff --git a/plugins/properties/testSrc/com/intellij/lang/properties/editor/ResourceBundleUtilTest.java b/plugins/properties/testSrc/com/intellij/lang/properties/editor/ResourceBundleUtilTest.java new file mode 100644 index 000000000000..ec0c65efd02e --- /dev/null +++ b/plugins/properties/testSrc/com/intellij/lang/properties/editor/ResourceBundleUtilTest.java @@ -0,0 +1,58 @@ +package com.intellij.lang.properties.editor; + +import gnu.trove.TIntHashSet; +import org.jetbrains.annotations.NotNull; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * @author Denis Zhdanov + * @since 10/5/11 2:37 PM + */ +public class ResourceBundleUtilTest { + + /** + * Holds pairs like ('property value'; 'value editor text'). + */ + private static final String[][] TEST_DATA = { + // Common. + { "", "" }, + { "as-is", "as-is" }, + { "with escaped escape symbol - \\\\", "with escaped escape symbol - \\" }, + + // Special symbols. + { "special symbols - \\# and \\! and \\= and \\:", "special symbols - # and ! and = and :" }, + + // White spaces. + { "trailing white space ", "trailing white space " }, + { "trailing white spaces ", "trailing white spaces " }, + { "trailing tab\t", "trailing tab\t" }, + { "trailing tabs\t\t\t", "trailing tabs\t\t\t" }, + { "\\\tstarting from tab", "\tstarting from tab" }, + { "\\\t\t\tstarting from tabs", "\t\t\tstarting from tabs" }, + { "\\ starting from white space", " starting from white space" }, + { "\\ starting from white spaces", " starting from white spaces" }, + { "\\ \t starting from white spaces and tabs", " \t starting from white spaces and tabs" }, + { "first line \\\nsecond line", "first line \nsecond line" }, + + // All together. + { "\\\t text with \\\nspecial symbols\\:\\\n\\#", "\t text with \nspecial symbols:\n#" } + }; + + @Test + public void checkAllTestData() { + for (String[] entry : TEST_DATA) { + assertEquals( + "Expected property value differs from the one converted from value editor text", + entry[0], + ResourceBundleUtil.fromValueEditorToPropertyValue(entry[1]) + ); + assertEquals( + "Expected value editor text differs from the one converted from property value", + entry[1], + ResourceBundleUtil.fromPropertyValueToValueEditor(entry[0]) + ); + } + } +}