You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2014/01/31 13:22:27 UTC
svn commit: r1563121 - in /sis/branches/JDK7/core/sis-referencing/src:
main/java/org/apache/sis/io/wkt/ test/java/org/apache/sis/io/wkt/
Author: desruisseaux
Date: Fri Jan 31 12:22:26 2014
New Revision: 1563121
URL: http://svn.apache.org/r1563121
Log:
WKT formatter needs to double the quote character if it appears inside the quoted text.
Modified:
sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/Formatter.java
sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/Symbols.java
sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/io/wkt/FormatterTest.java
sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/io/wkt/SymbolsTest.java
Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/Formatter.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/Formatter.java?rev=1563121&r1=1563120&r2=1563121&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/Formatter.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/Formatter.java [UTF-8] Fri Jan 31 12:22:26 2014
@@ -698,11 +698,28 @@ public class Formatter {
}
/**
- * Appends the given string as a quoted text.
+ * Appends the given string as a quoted text. If the given string contains the closing quote character,
+ * that character will be doubled. We check for the closing quote only because it is the character that
+ * the parser will look for determining the text end.
*/
private void quote(final String text) {
- buffer.appendCodePoint(symbols.getOpeningQuote(0)).append(text)
- .appendCodePoint(symbols.getClosingQuote(0));
+ final int base = buffer.appendCodePoint(symbols.getOpeningQuote(0)).length();
+ buffer.append(text);
+ closeQuote(base);
+ }
+
+ /**
+ * Double any closing quote character that may appear at or after the given index,
+ * then append the closing quote character.
+ */
+ private void closeQuote(int fromIndex) {
+ final String quote = symbols.getQuote();
+ while ((fromIndex = buffer.indexOf(quote, fromIndex)) >= 0) {
+ final int n = quote.length();
+ buffer.insert(fromIndex += n, quote);
+ fromIndex += n;
+ }
+ buffer.append(quote);
}
/**
@@ -767,7 +784,7 @@ public class Formatter {
appendSeparator(requestNewLine);
buffer.append("UNIT").appendCodePoint(symbols.getOpeningBracket(0));
setColor(ElementKind.UNIT);
- buffer.appendCodePoint(symbols.getOpeningQuote(0));
+ final int fromIndex = buffer.appendCodePoint(symbols.getOpeningQuote(0)).length();
if (NonSI.DEGREE_ANGLE.equals(unit)) {
buffer.append("degree");
} else if (SI.METRE.equals(unit)) {
@@ -775,7 +792,7 @@ public class Formatter {
} else {
unitFormat.format(unit, buffer, dummy);
}
- buffer.appendCodePoint(symbols.getClosingQuote(0));
+ closeQuote(fromIndex);
resetColor();
append(Units.toStandardUnit(unit));
buffer.appendCodePoint(symbols.getClosingBracket(0));
Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/Symbols.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/Symbols.java?rev=1563121&r1=1563120&r2=1563121&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/Symbols.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/Symbols.java [UTF-8] Fri Jan 31 12:22:26 2014
@@ -16,7 +16,10 @@
*/
package org.apache.sis.io.wkt;
+import java.util.Arrays;
import java.util.Locale;
+import java.io.IOException;
+import java.io.ObjectInputStream;
import java.io.Serializable;
import java.text.NumberFormat;
import org.apache.sis.util.Localized;
@@ -34,8 +37,8 @@ import static org.apache.sis.util.Argume
* <li>An English locale for {@linkplain java.text.DecimalFormatSymbols decimal format symbols}.</li>
* <li>Square brackets, as in {@code DATUM["WGS84"]}. An alternative allowed by the WKT
* specification is curly brackets as in {@code DATUM("WGS84")}.</li>
- * <li>English quotation mark ({@code '"'}). SIS also accepts {@code “…”} quotation marks
- * for more readable {@link String} constants in Java code.</li>
+ * <li>English quotation mark ({@code '"'}). SIS also accepts {@code '“'} opening quote and {@code '”'}
+ * closing quote for more readable {@link String} constants in Java code, but the later are not legal WKT.</li>
* <li>Coma separator followed by a space ({@code ", "}).</li>
* </ul>
*
@@ -43,7 +46,7 @@ import static org.apache.sis.util.Argume
* The {@link WKTFormat#getLocale()} property specifies the language to use when formatting
* {@link org.opengis.util.InternationalString} instances. This can be set to any value.
* On the contrary, the {@code Locale} property of this {@code Symbols} class controls
- * the decimal format symbols and is very rarely set to an other locale than an English one.
+ * the decimal format symbols and is very rarely set to an other locale than {@link Locale#ROOT}.
*
* @author Martin Desruisseaux (IRD)
* @since 0.4 (derived from geotk-2.1)
@@ -114,6 +117,16 @@ public class Symbols implements Localize
private int[] quotes;
/**
+ * The preferred closing quote character ({@code quotes[1]}) as a string.
+ * We use the closing quote because this is the character that the parser
+ * will look for determining the text end.
+ *
+ * @see #getQuote()
+ * @see #readObject(ObjectInputStream)
+ */
+ private transient String quote;
+
+ /**
* The character (as Unicode code point) used for opening ({@code openSequence})
* or closing ({@code closeSequence}) an array or enumeration.
*/
@@ -141,6 +154,7 @@ public class Symbols implements Localize
locale = symbols.locale;
brackets = symbols.brackets;
quotes = symbols.quotes;
+ quote = symbols.quote;
openSequence = symbols.openSequence;
closeSequence = symbols.closeSequence;
separator = symbols.separator;
@@ -151,9 +165,10 @@ public class Symbols implements Localize
* The given array is stored by reference - it is not cloned.
*/
private Symbols(final int[] brackets, final int[] quotes) {
- this.locale = Locale.US;
+ this.locale = Locale.ROOT;
this.brackets = brackets;
this.quotes = quotes;
+ this.quote = "\"";
this.openSequence = '{';
this.closeSequence = '}';
this.separator = ", ";
@@ -198,6 +213,15 @@ public class Symbols implements Localize
void checkWritePermission() throws UnsupportedOperationException {
throw new UnsupportedOperationException(Errors.format(Errors.Keys.UnmodifiableObject_1, "Symbols"));
}
+
+ /**
+ * Invoked on deserialization for replacing the deserialized instance by the constant instance.
+ */
+ Object readResolve() {
+ if (equals(SQUARE_BRACKETS)) return SQUARE_BRACKETS;
+ if (equals(CURLY_BRACKETS)) return CURLY_BRACKETS;
+ return this;
+ }
}
/**
@@ -215,9 +239,9 @@ public class Symbols implements Localize
}
/**
- * Returns the locale of {@linkplain java.text.DecimalFormatSymbols decimal format symbols}
- * or other symbols. This is usually an English locale. Note that this is not the same locale
- * than the {@link WKTFormat} one, which is used for choosing the language of international strings.
+ * Returns the locale of {@linkplain java.text.DecimalFormatSymbols decimal format symbols} or other symbols.
+ * The default value is {@link Locale#ROOT}. Note that this is not the same locale than the {@link WKTFormat}
+ * one, which is used for choosing the language of international strings.
*
* @return The symbols locale.
*/
@@ -228,6 +252,8 @@ public class Symbols implements Localize
/**
* Sets the locale of decimal format symbols or other symbols.
+ * Note that any non-English locale is likely to produce WKT that do not conform to ISO 19162.
+ * Such WKT should be used for human reading only, not for data export.
*
* @param locale The new symbols locale.
*/
@@ -348,6 +374,15 @@ public class Symbols implements Localize
}
/**
+ * Returns the preferred closing quote character as a string. This is the quote to double if it
+ * appears in a Unicode string to format. We check for the closing quote because this is the one
+ * that the parser will look for determining the text end.
+ */
+ final String getQuote() {
+ return quote;
+ }
+
+ /**
* Sets the opening and closing quotes to the given pairs.
* Each string shall contain exactly two code points (usually two characters).
* The first code point is taken as the opening quote, and the second code point as the closing quote.
@@ -365,6 +400,7 @@ public class Symbols implements Localize
public void setPairedQuotes(final String preferred, final String... alternatives) {
checkWritePermission();
quotes = toCodePoints(preferred, alternatives);
+ quote = preferred.substring(Character.charCount(quotes[0])).trim();
}
/**
@@ -372,7 +408,7 @@ public class Symbols implements Localize
* This method also verifies arguments validity.
*/
private static int[] toCodePoints(final String preferred, final String[] alternatives) {
- ensureNonNull("preferred", preferred);
+ ensureNonEmpty("preferred", preferred);
final int n = (alternatives != null) ? alternatives.length : 0;
final int[] array = new int[(n+1) * 2];
String name = "preferred";
@@ -574,4 +610,43 @@ public class Symbols implements Localize
}
return false;
}
+
+ /**
+ * Compares this {@code Symbols} with the given object for equality.
+ *
+ * @param other The object to compare with this {@code Symbols}.
+ * @return {@code true} if both objects are equal.
+ */
+ @Override
+ public boolean equals(final Object other) {
+ if (other instanceof Symbols) {
+ final Symbols that = (Symbols) other;
+ return Arrays.equals(brackets, that.brackets) &&
+ Arrays.equals(quotes, that.quotes) &&
+ // no need to compare 'quote' because it is computed from 'quotes'.
+ openSequence == that.openSequence &&
+ closeSequence == that.closeSequence &&
+ separator.equals(that.separator) &&
+ locale.equals(that.locale);
+ }
+ return false;
+ }
+
+ /**
+ * Returns a hash code value for this object.
+ *
+ * @return A hash code value.
+ */
+ @Override
+ public int hashCode() {
+ return Arrays.deepHashCode(new Object[] {brackets, quotes, openSequence, closeSequence, separator, locale});
+ }
+
+ /**
+ * Invoked on deserialization for recomputing the {@link #quote} field.
+ */
+ private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
+ in.defaultReadObject();
+ quote = String.valueOf(Character.toChars(quotes[1]));
+ }
}
Modified: sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/io/wkt/FormatterTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/io/wkt/FormatterTest.java?rev=1563121&r1=1563120&r2=1563121&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/io/wkt/FormatterTest.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/io/wkt/FormatterTest.java [UTF-8] Fri Jan 31 12:22:26 2014
@@ -16,13 +16,16 @@
*/
package org.apache.sis.io.wkt;
+import org.opengis.referencing.IdentifiedObject;
import org.opengis.referencing.operation.Matrix;
+import org.apache.sis.referencing.AbstractIdentifiedObject;
import org.apache.sis.referencing.operation.matrix.Matrix4;
import org.apache.sis.internal.util.X364;
import org.apache.sis.test.DependsOn;
import org.apache.sis.test.TestCase;
import org.junit.Test;
+import static java.util.Collections.singletonMap;
import static org.apache.sis.referencing.Assert.*;
@@ -46,6 +49,16 @@ public final strictfp class FormatterTes
}
/**
+ * Tests {@link Formatter#append(IdentifiedObject)} with a name that contains the quote character.
+ * We test that the closing quote character is doubled.
+ */
+ @Test
+ public void testAppendIdentifiedObject() {
+ assertWktEquals("IdentifiedObject[“My “object””.”]",
+ new AbstractIdentifiedObject(singletonMap(IdentifiedObject.NAME_KEY, "My “object”.")));
+ }
+
+ /**
* Tests {@link Formatter#append(Matrix)}.
*/
@Test
Modified: sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/io/wkt/SymbolsTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/io/wkt/SymbolsTest.java?rev=1563121&r1=1563120&r2=1563121&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/io/wkt/SymbolsTest.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/io/wkt/SymbolsTest.java [UTF-8] Fri Jan 31 12:22:26 2014
@@ -16,11 +16,12 @@
*/
package org.apache.sis.io.wkt;
+import java.util.Locale;
import org.apache.sis.test.TestCase;
import org.apache.sis.util.StringBuilders;
import org.junit.Test;
-import static org.junit.Assert.*;
+import static org.apache.sis.test.Assert.*;
/**
@@ -59,4 +60,47 @@ public final strictfp class SymbolsTest
assertFalse(wkt.contentEquals(buffer));
assertEquals(message, expected, Symbols.DEFAULT.containsAxis(buffer));
}
+
+ /**
+ * Ensures that the static constants are immutable.
+ */
+ @Test
+ public void testImmutability() {
+ try {
+ Symbols.SQUARE_BRACKETS.setPairedBrackets("()", "[]");
+ fail("Constant shall be immutable.");
+ } catch (UnsupportedOperationException e) {
+ // This is the expected exception.
+ final String message = e.getMessage();
+ assertTrue(message, message.contains("Symbols"));
+ }
+ try {
+ Symbols.CURLY_BRACKETS.setLocale(Locale.FRENCH);
+ fail("Constant shall be immutable.");
+ } catch (UnsupportedOperationException e) {
+ // This is the expected exception.
+ final String message = e.getMessage();
+ assertTrue(message, message.contains("Symbols"));
+ }
+ }
+
+ /**
+ * Tests {@link Symbols} serialization.
+ */
+ @Test
+ public void testSerialization() {
+ assertSame(Symbols.SQUARE_BRACKETS, assertSerializedEquals(Symbols.SQUARE_BRACKETS));
+ assertSame(Symbols.CURLY_BRACKETS, assertSerializedEquals(Symbols.CURLY_BRACKETS));
+ /*
+ * Test with a new instance using a closing quote symbol different than the opening one.
+ * This is necessary in order to ensure that the symbol is recomputed correctly.
+ */
+ final Symbols symbols = new Symbols(Symbols.CURLY_BRACKETS);
+ assertEquals("quote", "\"", symbols.getQuote());
+ symbols.setPairedQuotes("“”", "\"\"");
+ assertEquals("quote", "”", symbols.getQuote());
+ final Symbols c = assertSerializedEquals(symbols);
+ assertNotSame(symbols, c); // Expect a new instance.
+ assertEquals("quote", "”", c.getQuote()); // Verify the recomputed value.
+ }
}