sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1789729 [7/9] - in /sis/branches/JDK9: ./ application/sis-console/ application/sis-console/src/main/java/org/apache/sis/console/ application/sis-console/src/test/java/org/apache/sis/console/ core/ core/sis-feature/src/main/java/org/apache/...
Date Fri, 31 Mar 2017 18:49:18 GMT
Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/measure/SystemUnit.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/measure/SystemUnit.java?rev=1789729&r1=1789728&r2=1789729&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/measure/SystemUnit.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/measure/SystemUnit.java [UTF-8] Fri Mar 31 18:49:16 2017
@@ -28,6 +28,7 @@ import javax.measure.UnconvertibleExcept
 import javax.measure.IncommensurableException;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.ObjectConverters;
+import org.apache.sis.util.ComparisonMode;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.internal.converter.SurjectiveConverter;
 import org.apache.sis.math.Fraction;
@@ -481,17 +482,18 @@ final class SystemUnit<Q extends Quantit
     }
 
     /**
-     * Compares this unit with the given object for equality.
+     * Compares this unit with the given object for equality,
+     * optionally ignoring metadata and rounding errors.
      *
-     * @param  other  the other object to compares with this unit, or {@code null}.
-     * @return {@code true} if the given object is equals to this unit.
+     * @param  other  the other object to compare with this unit, or {@code null}.
+     * @return {@code true} if the given object is equal to this unit.
      */
     @Override
-    public boolean equals(final Object other) {
+    public boolean equals(final Object other, final ComparisonMode mode) {
         if (other == this) {
             return true;
         }
-        if (super.equals(other)) {
+        if (super.equals(other, mode)) {
             final SystemUnit<?> that = (SystemUnit<?>) other;
             return Objects.equals(quantity, that.quantity) && dimension.equals(that.dimension);
         }

Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/measure/UnitFormat.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/measure/UnitFormat.java?rev=1789729&r1=1789728&r2=1789729&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/measure/UnitFormat.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/measure/UnitFormat.java [UTF-8] Fri Mar 31 18:49:16 2017
@@ -282,7 +282,7 @@ public class UnitFormat extends Format i
      * Mapping from long localized and unlocalized names to unit instances.
      * This map is used only for parsing and created when first needed.
      *
-     * @see #nameToUnit()
+     * @see #fromName(String)
      */
     private transient volatile Map<String,Unit<?>> nameToUnit;
 
@@ -291,7 +291,7 @@ public class UnitFormat extends Format i
      * if the user create many {@code UnitFormat} instances. Note that we do not cache {@link #symbolToName} because
      * {@link ResourceBundle} already provides its own caching mechanism.
      *
-     * @see #nameToUnit()
+     * @see #fromName(String)
      */
     private static final WeakValueHashMap<Locale, Map<String,Unit<?>>> SHARED = new WeakValueHashMap<>(Locale.class);
 
@@ -383,8 +383,9 @@ public class UnitFormat extends Format i
      *
      * <div class="section">Restriction on character set</div>
      * Current implementation accepts only {@linkplain Character#isLetter(int) letters},
-     * {@linkplain Characters#isSubScript(int) subscripts}, {@linkplain Character#isWhitespace(int) whitespaces}
-     * and the degree sign (°),
+     * {@linkplain Characters#isSubScript(int) subscripts}, {@linkplain Character#isSpaceChar(int) spaces}
+     * (including non-breaking spaces but <strong>not</strong> CR/LF characters), the degree sign (°) and
+     * a few other characters like underscore,
      * but the set of legal characters may be expanded in future Apache SIS versions.
      * However the following restrictions are likely to remain:
      *
@@ -405,7 +406,7 @@ public class UnitFormat extends Format i
         ArgumentChecks.ensureNonEmpty("label", label);
         for (int i=0; i < label.length();) {
             final int c = label.codePointAt(i);
-            if (!AbstractUnit.isSymbolChar(c) && !Character.isWhitespace(c)) {
+            if (!AbstractUnit.isSymbolChar(c) && !Character.isSpaceChar(c)) {       // NOT Character.isWhitespace(int)
                 throw new IllegalArgumentException(Errors.format(Errors.Keys.IllegalArgumentValue_2, "label", label));
             }
             i += Character.charCount(c);
@@ -444,16 +445,36 @@ public class UnitFormat extends Format i
     }
 
     /**
-     * Returns the mapping from long localized and unlocalized names to unit instances.
-     * This mapping is somewhat the converse of {@link #symbolToName()}, but includes
+     * Returns the unit instance for the given long (un)localized or name.
+     * This method is somewhat the converse of {@link #symbolToName()}, but recognizes also
      * international and American spelling of unit names in addition of localized names.
      * The intend is to recognize "meter" as well as "metre".
      *
      * <p>While we said that {@code UnitFormat} is not thread safe, we make an exception for this method
      * for allowing the singleton {@link #INSTANCE} to parse symbols in a multi-threads environment.</p>
      */
-    @SuppressWarnings("ReturnOfCollectionOrArrayField")
-    private Map<String,Unit<?>> nameToUnit() {
+    @SuppressWarnings("fallthrough")
+    private Unit<?> fromName(String uom) {
+        /*
+         * Before to search in resource bundles, check for degrees units. The "deg" unit can be both angular
+         * and Celsius degrees. We try to resolve this ambiguity by looking for the "C" suffix. We perform a
+         * special case for the degrees units because SI symbols are case-sentive and unit names in resource
+         * bundles are case-insensitive, but the "deg" case is a mix of both.
+         */
+        if (uom.regionMatches(true, 0, "deg", 0, 3)) {
+            final int length = uom.length();
+            switch (length) {
+                case 3: return Units.DEGREE;                    // Exactly "deg"  (ignoring case)
+                case 5: final char c = uom.charAt(3);
+                        if (c != '_' && !Character.isSpaceChar(c)) break;
+                        // else fallthrough
+                case 4: switch (uom.charAt(length - 1)) {
+                            case 'K':                           // Unicode U+212A
+                            case 'K': return Units.KELVIN;      // Exactly "degK" (ignoring case except for 'K')
+                            case 'C': return Units.CELSIUS;
+                        }
+            }
+        }
         Map<String,Unit<?>> map = nameToUnit;
         if (map == null) {
             map = SHARED.get(locale);
@@ -489,7 +510,17 @@ public class UnitFormat extends Format i
             }
             nameToUnit = map;
         }
-        return map;
+        /*
+         * The 'nameToUnit' map contains plural forms (declared in UnitAliases.properties),
+         * but we make a special case for "degrees", "metres" and "meters" because they
+         * appear in numerous places.
+         */
+        uom = uom.replace('_', ' ').toLowerCase(locale);
+        uom = CharSequences.replace(CharSequences.replace(CharSequences.replace(CharSequences.toASCII(uom),
+                "meters",  "meter"),
+                "metres",  "metre"),
+                "degrees", "degree").toString();
+        return map.get(uom);
     }
 
     /**
@@ -775,18 +806,33 @@ public class UnitFormat extends Format i
      * Returns {@code true} if the given character is a digit in the sense of the {@code UnitFormat} parser.
      * Note that "digit" is taken here in a much more restrictive way than {@link Character#isDigit(int)}.
      */
-    private static boolean isDigit(final int c) {
+    private static boolean isDigit(final char c) {
         return c >= '0' && c <= '9';
     }
 
     /**
      * Returns {@code true} if the given character is the sign of a number according the {@code UnitFormat} parser.
      */
-    private static boolean isSign(final int c) {
+    private static boolean isSign(final char c) {
         return c == '+' || c == '-';
     }
 
     /**
+     * Returns {@code true} if the given character sequence contains at least one digit.
+     * This is a hack for allowing to recognize units like "100 feet" (in principle not
+     * legal, but seen in practice). This verification has some value if digits are not
+     * allowed as unit label or symbol.
+     */
+    private static boolean hasDigit(final CharSequence symbol, int lower, final int upper) {
+        while (lower < upper) {
+            if (isDigit(symbol.charAt(lower++))) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
      * Parses the given text as an instance of {@code Unit}.
      * If the parse completes without reading the entire length of the text, an exception is thrown.
      *
@@ -810,7 +856,7 @@ public class UnitFormat extends Format i
         final ParsePosition position = new ParsePosition(0);
         final Unit<?> unit = parse(symbols, position);
         final int length = symbols.length();
-        final int unrecognized = CharSequences.skipTrailingWhitespaces(symbols, position.getIndex(), length);
+        final int unrecognized = CharSequences.skipLeadingWhitespaces(symbols, position.getIndex(), length);
         if (unrecognized < length) {
             throw new ParserException(Errors.format(Errors.Keys.UnexpectedCharactersAfter_2,
                     CharSequences.trimWhitespaces(symbols, 0, unrecognized),
@@ -845,25 +891,27 @@ public class UnitFormat extends Format i
         ArgumentChecks.ensureNonNull("position", position);
         /*
          * Check for authority codes (currently only EPSG, but more could be added later).
-         * If the unit is not an authority code (which is the most common case), then we
-         * will check for hard-coded unit symbols.
-         *
-         * DefinitionURI.codeOf(…) returns 'uom' directly (provided that whitespaces were already trimmed)
-         * if no ':' character were found, in which case the string is assumed to be the code directly.
-         * This is the intended behavior for AuthorityFactory, but in the particular case of this method
-         * we want to try to parse as a xpointer before to give up.
+         * Example: "urn:ogc:def:uom:EPSG::9001". If the unit is not an authority code
+         * (which is the most common case), only then we will parse the unit symbols.
          */
-        int start = CharSequences.skipLeadingWhitespaces(symbols, position.getIndex(), symbols.length());
-        int end = XPaths.endOfURI(symbols, start);
-        if (end >= 0) {
-            final String uom = symbols.subSequence(start, end).toString();
+        int end   = symbols.length();
+        int start = CharSequences.skipLeadingWhitespaces(symbols, position.getIndex(), end);
+        int endOfURI = XPaths.endOfURI(symbols, start);
+        if (endOfURI >= 0) {
+            final String uom = symbols.subSequence(start, endOfURI).toString();
             String code = DefinitionURI.codeOf("uom", Constants.EPSG, uom);
-            if (code != null && code != uom) {                  // Really identity check, see above comment.
+            /*
+             * DefinitionURI.codeOf(…) returns 'uom' directly (provided that whitespaces were already trimmed)
+             * if no ':' character were found, in which case the string is assumed to be the code directly.
+             * This is the intended behavior for AuthorityFactory, but in the particular case of this method
+             * we want to try to parse as a xpointer before to give up.
+             */
+            if (code != null && code != uom) {
                 NumberFormatException failure = null;
                 try {
                     final Unit<?> unit = Units.valueOfEPSG(Integer.parseInt(code));
                     if (unit != null) {
-                        position.setIndex(end);
+                        position.setIndex(endOfURI);
                         return unit;
                     }
                 } catch (NumberFormatException e) {
@@ -871,12 +919,28 @@ public class UnitFormat extends Format i
                 }
                 throw (ParserException) new ParserException(Errors.format(Errors.Keys.UnknownUnit_1,
                         Constants.EPSG + DefaultNameSpace.DEFAULT_SEPARATOR + code),
-                        symbols, start + Math.max(0, uom.indexOf(code))).initCause(failure);
+                        symbols, start + Math.max(0, uom.lastIndexOf(code))).initCause(failure);
             }
+            /*
+             * Not an EPSG code. Maybe it is a URI like this example:
+             * http://schemas.opengis.net/iso/19139/20070417/resources/uom/gmxUom.xml#xpointer(//*[@gml:id='m'])
+             *
+             * If we find such 'uom' value, we could replace 'symbols' by that 'uom'. But it would cause a wrong
+             * error index to be reported in case of parsing failure. We will rather try to adjust the indices
+             * (and replace 'symbols' only in last resort).
+             */
             code = XPaths.xpointer("uom", uom);
             if (code != null) {
-                symbols = code;
-                start = 0;
+                final int base = start;
+                start = endOfURI - code.length();
+                do if (--start < base) {          // Should never happen (see above comment), but we are paranoiac.
+                    symbols = code;
+                    start = 0;
+                    break;
+                } while (!CharSequences.regionMatches(symbols, start, code));
+                end = start + code.length();
+            } else {
+                endOfURI = -1;
             }
         }
         /*
@@ -887,7 +951,7 @@ public class UnitFormat extends Format i
          */
         int operation = NOOP;            // Enumeration value: IMPLICIT, MULTIPLY, DIVIDE.
         Unit<?> unit = null;
-        end = symbols.length();
+        boolean hasSpaces = false;
         int i = start;
 scan:   for (int n; i < end; i += n) {
             final int c = Character.codePointAt(symbols, i);
@@ -896,7 +960,7 @@ scan:   for (int n; i < end; i += n) {
             switch (c) {
                 /*
                  * For any character that are is not an operator or parenthesis, either continue the scanning of
-                 * character or stop it, depending on whether the character is valid for a unit symbol or not.
+                 * characters or stop it, depending on whether the character is valid for a unit symbol or not.
                  * In the later case, we consider that we reached the end of a unit symbol.
                  */
                 default:  {
@@ -906,7 +970,11 @@ scan:   for (int n; i < end; i += n) {
                         }
                         continue;
                     }
-                    if (Character.isWhitespace(c) || Character.isDigit(c) || Characters.isSuperScript(c)) {
+                    if (Character.isDigit(c) || Characters.isSuperScript(c)) {
+                        continue;
+                    }
+                    if (Character.isSpaceChar(c)) {                         // NOT Character.isWhitespace(int)
+                        hasSpaces = true;
                         continue;
                     }
                     break scan;
@@ -972,15 +1040,54 @@ scan:   for (int n; i < end; i += n) {
             if (operation != IMPLICIT) {
                 unit = apply(operation, unit, parseSymbol(symbols, start, i));
             }
+            hasSpaces = false;
             operation = next;
             start = i + n;
         }
         /*
-         * At this point we either found an unrecognized character or reached the end of string. Parse the
-         * remaining characters as a unit and apply the pending unit operation (multiplication or division).
+         * At this point we either found an unrecognized character or reached the end of string. We will
+         * parse the remaining characters as a unit and apply the pending unit operation (multiplication
+         * or division). But before, we need to check if the parsing should stop at the first whitespace.
+         * This verification assumes that spaces are allowed only in labels specified by the label(…)
+         * method and in resource bundles, not in labels specified by AbstractUnit.alternate(String).
          */
-        unit = apply(operation, unit, parseSymbol(symbols, start, i));
-        position.setIndex(i);
+        Unit<?> component = null;
+        if (hasSpaces) {
+            end = i;
+            start = CharSequences.skipLeadingWhitespaces(symbols, start, i);
+search:     while ((i = CharSequences.skipTrailingWhitespaces(symbols, start, i)) > start) {
+                final String uom = symbols.subSequence(start, i).toString();
+                if ((component = labelToUnit.get(uom)) != null) break;
+                if ((component =        fromName(uom)) != null) break;
+                int j=i, c;
+                do {
+                    c = Character.codePointBefore(symbols, j);
+                    j -= Character.charCount(c);
+                    if (j <= start) break search;
+                } while (!Character.isWhitespace(c));
+                /*
+                 * Really use Character.isWhitespace(c) above, not Character.isSpaceChar(c), because we want
+                 * to exclude non-breaking spaces.   This block should be the only place in UnitFormat class
+                 * where we use isWhitespace(c) instead of isSpaceChar(c).
+                 */
+                i = j;                  // Will become the index of first space after search loop completion.
+            }
+            /*
+             * At this point we did not found any user-specified label or localized name matching the substring.
+             * Assume that the parsing should stop at the first space, on the basis that spaces are not allowed
+             * in unit symbols. We make an exception if we detect that the part before the first space contains
+             * digits (not allowed in unit symbols neither), in which case the substring may be something like
+             * "100 feet".
+             */
+            if (hasDigit(symbols, start, i)) {
+                i = end;                        // Restore the full length (until the first illegal character).
+            }
+        }
+        if (component == null) {
+            component = parseSymbol(symbols, start, i);
+        }
+        unit = apply(operation, unit, component);
+        position.setIndex(endOfURI >= 0 ? endOfURI : i);
         return unit;
     }
 
@@ -1017,7 +1124,6 @@ scan:   for (int n; i < end; i += n) {
      * @return the parsed unit symbol (never {@code null}).
      * @throws ParserException if a problem occurred while parsing the given symbols.
      */
-    @SuppressWarnings("fallthrough")
     private Unit<?> parseSymbol(final CharSequence symbols, final int lower, final int upper) throws ParserException {
         final String uom = CharSequences.trimWhitespaces(symbols, lower, upper).toString();
         /*
@@ -1109,38 +1215,10 @@ scan:   for (int n; i < end; i += n) {
                     }
                 }
                 /*
-                 * Check for degrees units. Note that "deg" could be both angular and Celsius degrees.
-                 * We try to resolve this ambiguity in the code below by looking for the "C" suffix.
-                 * We perform a special case for those checks because the above check for unit symbol
-                 * is case-sentive, the check for unit name (later) is case-insensitive, while this
-                 * check for "deg" is a mix of both.
-                 */
-                if (uom.regionMatches(true, 0, "deg", 0, 3)) {
-                    switch (length) {
-                        case 3: return Units.DEGREE;                    // Exactly "deg"  (ignoring case)
-                        case 5: final char c = uom.charAt(3);
-                                if (c != '_' && !Character.isSpaceChar(c)) break;
-                                // else fallthrough
-                        case 4: switch (uom.charAt(length - 1)) {
-                                    case 'K':                           // Unicode U+212A
-                                    case 'K': return Units.KELVIN;      // Exactly "degK" (ignoring case except for 'K')
-                                    case 'C': return Units.CELSIUS;
-                                }
-                    }
-                }
-                /*
                  * At this point, we have determined that the label is not a known unit symbol.
                  * It may be a unit name, in which case the label is not case-sensitive anymore.
-                 * The 'nameToUnit' map contains plural forms (declared in UnitAliases.properties),
-                 * but we make a special case for "degrees", "metres" and "meters" because they
-                 * appear in numerous places.
                  */
-                String lc = uom.replace('_', ' ').toLowerCase(locale);
-                lc = CharSequences.replace(CharSequences.replace(CharSequences.replace(CharSequences.toASCII(lc),
-                        "meters",  "meter"),
-                        "metres",  "metre"),
-                        "degrees", "degree").toString();
-                unit = nameToUnit().get(lc);
+                unit = fromName(uom);
                 if (unit == null) {
                     if (CharSequences.regionMatches(symbols, lower, UNITY, true)) {
                         return Units.UNITY;

Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/measure/package-info.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/measure/package-info.java?rev=1789729&r1=1789728&r2=1789729&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/measure/package-info.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/measure/package-info.java [UTF-8] Fri Mar 31 18:49:16 2017
@@ -56,7 +56,6 @@
  *      ({@link org.apache.sis.measure.ValueRange})</li>
  *   <li>Formatters
  *      ({@link org.apache.sis.measure.AngleFormat},
- *       {@link org.apache.sis.measure.CoordinateFormat},
  *       {@link org.apache.sis.measure.RangeFormat})</li>
  * </ul>
  *

Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/setup/OptionKey.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/setup/OptionKey.java?rev=1789729&r1=1789728&r2=1789729&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/setup/OptionKey.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/setup/OptionKey.java [UTF-8] Fri Mar 31 18:49:16 2017
@@ -113,6 +113,8 @@ public class OptionKey<T> implements Ser
      * <p>If this option is not provided, then the default value is format specific.
      * That default is often, but not necessarily, the {@linkplain Charset#defaultCharset() platform default}.</p>
      *
+     * @see javax.xml.bind.Marshaller#JAXB_ENCODING
+     *
      * @since 0.4
      */
     public static final OptionKey<Charset> ENCODING = new OptionKey<>("ENCODING", Charset.class);
@@ -179,6 +181,27 @@ public class OptionKey<T> implements Ser
     public static final OptionKey<ByteBuffer> BYTE_BUFFER = new OptionKey<>("BYTE_BUFFER", ByteBuffer.class);
 
     /**
+     * The number of spaces to use for indentation when formatting text files in WKT or XML formats.
+     * A value of {@value org.apache.sis.io.wkt.WKTFormat#SINGLE_LINE} means to format the whole WKT
+     * or XML document on a single line without line feeds or indentation.
+     *
+     * <p>If this option is not provided, then the most typical default value used in Apache SIS is 2.
+     * Such small indentation value is used because XML documents defined by OGC standards tend to be
+     * verbose.</p>
+     *
+     * @see org.apache.sis.io.wkt.WKTFormat#SINGLE_LINE
+     * @see javax.xml.bind.Marshaller#JAXB_FORMATTED_OUTPUT
+     *
+     * @since 0.8
+     */
+    public static final OptionKey<Integer> INDENTATION = new OptionKey<>("INDENTATION", Integer.class);
+
+    /*
+     * Note: we do not provide a LINE_SEPARATOR option for now because we can not control the line separator
+     * in JDK's JAXB implementation, and Apache SIS provides an org.apache.sis.io.LineAppender alternative.
+     */
+
+    /**
      * The name of this key. For {@code OptionKey} instances, it shall be the name of the static constants.
      * For subclasses of {@code OptionKey}, there is no restriction.
      */

Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/StringBuilders.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/StringBuilders.java?rev=1789729&r1=1789728&r2=1789729&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/StringBuilders.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/StringBuilders.java [UTF-8] Fri Mar 31 18:49:16 2017
@@ -161,6 +161,72 @@ public final class StringBuilders extend
     }
 
     /**
+     * Inserts the given character <var>n</var> time at the given position.
+     * This method does nothing if the given {@code count} is zero.
+     *
+     * @param  buffer  the buffer where to insert the character.
+     * @param  offset  position where to insert the characters.
+     * @param  c       the character to repeat.
+     * @param  count   number of time to repeat the given character.
+     * @throws NullPointerException if the given buffer is null.
+     * @throws IndexOutOfBoundsException if the given index is invalid.
+     * @throws IllegalArgumentException if the given count is negative.
+     *
+     * @since 0.8
+     */
+    public static void repeat(final StringBuilder buffer, final int offset, final char c, final int count) {
+        ArgumentChecks.ensureNonNull("buffer", buffer);
+        switch (count) {
+            case 0:  break;
+            case 1:  buffer.insert(offset, c); break;
+            default: {
+                ArgumentChecks.ensurePositive("count", count);
+                final CharSequence r;
+                switch (c) {
+                    case ' ': r = CharSequences.spaces(count); break;
+                    case '0': r = Repeat.ZERO; break;
+                    default:  r = new Repeat(c, count); break;
+                }
+                buffer.insert(offset, r, 0, count);
+                break;
+            }
+        }
+    }
+
+    /**
+     * A sequence of a constant character. This implementation does not perform any argument
+     * check since it is for {@link StringBuilder#append(CharSequence, int, int)} usage only.
+     * The intend is to allow {@code StringBuilder} to append the characters in one operation
+     * instead than looping on {@link StringBuilder#insert(int, char)} (which would require
+     * memory move on each call).
+     */
+    private static final class Repeat implements CharSequence {
+        /** An infinite sequence of {@code '0'} character. */
+        static final Repeat ZERO = new Repeat('0', Integer.MAX_VALUE);
+
+        /** The character to repeat. */
+        private final char c;
+
+        /** Number of times the character is repeated. */
+        private final int n;
+
+        /** Creates a new sequence of constant character. */
+        Repeat(final char c, final int n) {
+            this.c = c;
+            this.n = n;
+        }
+
+        /** Returns the number of times the character is repeated. */
+        @Override public int length() {return n;}
+
+        /** Returns the constant character, regardless the index. */
+        @Override public char charAt(int i) {return c;}
+
+        /** Returns a sequence of the same constant character but different length. */
+        @Override public CharSequence subSequence(int start, int end) {return new Repeat(c, end - start);}
+    }
+
+    /**
      * Trims the fractional part of the given formatted number, provided that it doesn't change
      * the value. This method assumes that the number is formatted in the US locale, typically
      * by the {@link Double#toString(double)} method.
@@ -183,11 +249,9 @@ public final class StringBuilders extend
     public static void trimFractionalPart(final StringBuilder buffer) {
         ArgumentChecks.ensureNonNull ("buffer", buffer);
         for (int i=buffer.length(); i > 0;) {
-            final int c = buffer.codePointBefore(i);
-            i -= charCount(c);
-            switch (c) {
+            switch (buffer.charAt(--i)) {               // No need to use Unicode code points here.
                 case '0': continue;
-                case '.': buffer.setLength(i); // Fall through
+                case '.': buffer.setLength(i);          // Fall through
                 default : return;
             }
         }

Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/Version.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/Version.java?rev=1789729&r1=1789728&r2=1789729&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/Version.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/Version.java [UTF-8] Fri Mar 31 18:49:16 2017
@@ -70,7 +70,9 @@ public class Version implements CharSequ
      */
     private static final Version[] CONSTANTS = {
         new Version("1"),
-        new Version("2")
+        new Version("2"),
+        new Version("1.0"),
+        new Version("1.1")
     };
 
     /**

Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/collection/TreeTableFormat.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/collection/TreeTableFormat.java?rev=1789729&r1=1789728&r2=1789729&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/collection/TreeTableFormat.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/collection/TreeTableFormat.java [UTF-8] Fri Mar 31 18:49:16 2017
@@ -324,7 +324,7 @@ public class TreeTableFormat extends Tab
 
     /**
      * Creates a tree from the given character sequence,
-     * or returns {@code null} if an error occurred while parsing the characters.
+     * or returns {@code null} if the given text does not look like a tree for this method.
      * This method can parse the trees created by the {@code format(…)} methods
      * defined in this class.
      *
@@ -338,12 +338,23 @@ public class TreeTableFormat extends Tab
      *     </ol>
      *   </li>
      *   <li>The number of spaces and drawing characters before the node values determines the node
-     *       indentation. This indentation doesn't need to be a factor of the {@link #getIndentation()}
+     *       indentation. This indentation does not need to be a factor of the {@link #getIndentation()}
      *       value, but must be consistent across all the parsed tree.</li>
      *   <li>The indentation determines the parent of each node.</li>
      *   <li>Parsing stops at first empty line (ignoring whitespaces), or at the end of the given text.</li>
      * </ul>
      *
+     * <div class="section">Error index</div>
+     * If the given text does not seem to be a tree table, then this method returns {@code null}.
+     * Otherwise if parsing started but failed, then:
+     *
+     * <ul>
+     *   <li>{@link ParsePosition#getErrorIndex()} will give the index at the beginning
+     *       of line or beginning of cell where the error occurred, and</li>
+     *   <li>{@link ParseException#getErrorOffset()} will give either the same value,
+     *       or a slightly more accurate value inside the cell.</li>
+     * </ul>
+     *
      * @param  text  the character sequence for the tree to parse.
      * @param  pos   the position where to start the parsing.
      * @return the parsed tree, or {@code null} if the given character sequence can not be parsed.
@@ -403,24 +414,31 @@ public class TreeTableFormat extends Tab
              * text are not parsed (the value is left to null).
              */
             final TreeTable.Node node = new DefaultTreeTable.Node(table);
-            try {
-                matcher.region(indexOfValue, endOfLine);
-                for (int ci=0; ci<columns.length; ci++) {
-                    final boolean found = matcher.find();
-                    int endOfColumn = found ? matcher.start() : endOfLine;
-                    indexOfValue   = CharSequences.skipLeadingWhitespaces (text, indexOfValue, endOfColumn);
-                    int endOfValue = CharSequences.skipTrailingWhitespaces(text, indexOfValue, endOfColumn);
-                    if (endOfValue > indexOfValue) {
-                        parseValue(node, columns[ci], formats[ci], text.subSequence(indexOfValue, endOfValue).toString());
+            matcher.region(indexOfValue, endOfLine);
+            for (int ci=0; ci<columns.length; ci++) {
+                final boolean found = matcher.find();
+                int endOfColumn = found ? matcher.start() : endOfLine;
+                indexOfValue   = CharSequences.skipLeadingWhitespaces (text, indexOfValue, endOfColumn);
+                int endOfValue = CharSequences.skipTrailingWhitespaces(text, indexOfValue, endOfColumn);
+                if (endOfValue > indexOfValue) {
+                    final String valueText = text.subSequence(indexOfValue, endOfValue).toString();
+                    try {
+                        parseValue(node, columns[ci], formats[ci], valueText);
+                    } catch (ParseException | ClassCastException e) {
+                        pos.setErrorIndex(indexOfValue);                                    // See method javadoc.
+                        if (e instanceof ParseException) {
+                            indexOfValue += ((ParseException) e).getErrorOffset();
+                        }
+                        throw new LocalizedParseException(getDisplayLocale(), Errors.Keys.UnparsableStringForClass_2,
+                                new Object[] {columns[ci].getElementType(), valueText}, indexOfValue).initCause(e);
                     }
-                    if (!found) break;
-                    // The end of this column will be the beginning of the next column,
-                    // after skipping the last character of the column separator.
-                    indexOfValue = matcher.end();
                 }
-            } catch (ParseException e) {
-                pos.setErrorIndex(indexOfValue);
-                throw e;
+                if (!found) break;
+                /*
+                 * The end of this column will be the beginning of the next column,
+                 * after skipping the last character of the column separator.
+                 */
+                indexOfValue = matcher.end();
             }
             /*
              * If this is the first node created so far, it will be the root.
@@ -439,7 +457,7 @@ public class TreeTableFormat extends Tab
                     if (--indentationLevel < 0) {
                         pos.setErrorIndex(indexOfLineStart);
                         throw new LocalizedParseException(getDisplayLocale(),
-                                Errors.Keys.NodeHasNoParent_1, new Object[] {node}, 0);
+                                Errors.Keys.NodeHasNoParent_1, new Object[] {node}, indexOfLineStart);
                     }
                     lastNode = lastNode.getParent();
                 }
@@ -453,7 +471,7 @@ public class TreeTableFormat extends Tab
                     if (parent == null) {
                         pos.setErrorIndex(indexOfLineStart);
                         throw new LocalizedParseException(getDisplayLocale(),
-                                Errors.Keys.NodeHasNoParent_1, new Object[] {node}, 0);
+                                Errors.Keys.NodeHasNoParent_1, new Object[] {node}, indexOfLineStart);
                     }
                     parent.getChildren().add(node);
                 } else if (i > p) {
@@ -483,12 +501,16 @@ public class TreeTableFormat extends Tab
      * Parses the given string using a format appropriate for the type of values in
      * the given column, and stores the value in the given node.
      *
+     * <p>This work is done in a separated method instead than inlined in the
+     * {@code parse(…)} method because of the {@code <V>} parametric value.</p>
+     *
      * @param  V        the type of values in the given column.
      * @param  node     the node in which to set the value.
      * @param  column   the column in which to set the value.
      * @param  format   the format to use for parsing the value, or {@code null}.
      * @param  text     the textual representation of the value.
      * @throws ParseException if an error occurred while parsing.
+     * @throws ClassCastException if the parsed value is not of the expected type.
      */
     private <V> void parseValue(final TreeTable.Node node, final TableColumn<V> column,
             final Format format, final String text) throws ParseException

Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java?rev=1789729&r1=1789728&r2=1789729&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java [UTF-8] Fri Mar 31 18:49:16 2017
@@ -70,6 +70,11 @@ public final class Errors extends Indexe
         public static final short AmbiguousName_3 = 1;
 
         /**
+         * This object can iterate only once.
+         */
+        public static final short CanIterateOnlyOnce = 172;
+
+        /**
          * No element can be added to this set because properties ‘{0}’ and ‘{1}’ are mutually
          * exclusive.
          */
@@ -106,6 +111,11 @@ public final class Errors extends Indexe
         public static final short CanNotConvertValue_2 = 8;
 
         /**
+         * Can not copy “{0}”.
+         */
+        public static final short CanNotCopy_1 = 169;
+
+        /**
          * Can not open “{0}”.
          */
         public static final short CanNotOpen_1 = 9;
@@ -378,7 +388,7 @@ public final class Errors extends Indexe
         public static final short IllegalUnicodeCodePoint_2 = 61;
 
         /**
-         * Can not use the “{1}” format with “{0}”.
+         * Can not use the {1} format with “{0}”.
          */
         public static final short IncompatibleFormat_2 = 62;
 
@@ -635,6 +645,11 @@ public final class Errors extends Indexe
         public static final short NotAUnicodeIdentifier_1 = 112;
 
         /**
+         * {0} is not an integer value.
+         */
+        public static final short NotAnInteger_1 = 171;
+
+        /**
          * Argument ‘{0}’ shall not be null.
          */
         public static final short NullArgument_1 = 113;
@@ -865,6 +880,16 @@ public final class Errors extends Indexe
         public static final short UnspecifiedFormatForClass_1 = 158;
 
         /**
+         * The “{0}” argument value is unsupported.
+         */
+        public static final short UnsupportedArgumentValue_1 = 170;
+
+        /**
+         * The “{0}” datum is not supported by this operation.
+         */
+        public static final short UnsupportedDatum_1 = 168;
+
+        /**
          * Version {1} of {0} format is not supported.
          */
         public static final short UnsupportedFormatVersion_2 = 159;
@@ -886,7 +911,7 @@ public final class Errors extends Indexe
         public static final short UnsupportedOperation_1 = 162;
 
         /**
-         * The ‘{0}’ type is unsupported.
+         * The ‘{0}’ type is not supported in this context.
          */
         public static final short UnsupportedType_1 = 163;
 

Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties?rev=1789729&r1=1789728&r2=1789729&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties [ISO-8859-1] (original)
+++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties [ISO-8859-1] Fri Mar 31 18:49:16 2017
@@ -25,6 +25,7 @@
 # can reorder the parameters as they want.
 #
 AmbiguousName_3                   = Name \u201c{2}\u201d is ambiguous because it can be understood as either \u201c{0}\u201d or \u201c{1}\u201d.
+CanIterateOnlyOnce                = This object can iterate only once.
 CanNotAddToExclusiveSet_2         = No element can be added to this set because properties \u2018{0}\u2019 and \u2018{1}\u2019 are mutually exclusive.
 CanNotAssign_2                    = Can not assign \u201c{1}\u201d to \u201c{0}\u201d.
 CanNotAssignUnitToDimension_2     = Can not assign units \u201c{1}\u201d to dimension \u201c{0}\u201d.
@@ -32,6 +33,7 @@ CanNotConnectTo_1                 = Can
 CanNotConvertFromType_2           = Can not convert from type \u2018{0}\u2019 to type \u2018{1}\u2019.
 CanNotConvertValue_2              = Can not convert value \u201c{0}\u201d to type \u2018{1}\u2019.
 CanNotCompute_1                   = Can not compute \u201c{0}\u201d.
+CanNotCopy_1                      = Can not copy \u201c{0}\u201d.
 CanNotOpen_1                      = Can not open \u201c{0}\u201d.
 CanNotParseFile_2                 = Can not parse \u201c{1}\u201d as a file in the {0} format.
 CanNotRead_1                      = Can not read \u201c{0}\u201d.
@@ -86,7 +88,7 @@ IllegalPropertyValueClass_2       = Prop
 IllegalPropertyValueClass_3       = Expected an instance of \u2018{1}\u2019 for the \u201c{0}\u201d property, but got an instance of \u2018{2}\u2019.
 IllegalRange_2                    = Range [{0} \u2026 {1}] is not valid.
 IllegalUnicodeCodePoint_2         = Value {1} for \u201c{0}\u201d is not a valid Unicode code point.
-IncompatibleFormat_2              = Can not use the \u201c{1}\u201d format with \u201c{0}\u201d.
+IncompatibleFormat_2              = Can not use the {1} format with \u201c{0}\u201d.
 IncompatiblePropertyValue_1       = Property \u201c{0}\u201d has an incompatible value.
 IncompatibleUnit_1                = Unit \u201c{0}\u201d is incompatible with current value.
 IncompatibleUnits_2               = Units \u201c{0}\u201d and \u201c{1}\u201d are incompatible.
@@ -132,6 +134,7 @@ NonTemporalUnit_1                 = \u20
 NonSystemUnit_1                   = \u201c{0}\u201d is not a fundamental or derived unit.
 NonRatioUnit_1                    = The scale of measurement for \u201c{0}\u201d unit is not a ratio scale.
 NotABackwardReference_1           = No element for the \u201c{0}\u201d identifier, or the identifier is a forward reference.
+NotAnInteger_1                    = {0} is not an integer value.
 NotAKeyValuePair_1                = \u201c{0}\u201d is not a key-value pair.
 NotANumber_1                      = Argument \u2018{0}\u2019 shall not be NaN (Not-a-Number).
 NotAPrimitiveWrapper_1            = Class \u2018{0}\u2019 is not a primitive type wrapper.
@@ -188,7 +191,9 @@ UnsupportedFormatVersion_2        = Vers
 UnsupportedImplementation_1       = Can not handle this instance of \u2018{0}\u2019 because arbitrary implementations are not yet supported.
 UnsupportedInterpolation_1        = The \u201c{0}\u201d interpolation is unsupported.
 UnsupportedOperation_1            = The \u2018{0}\u2019 operation is unsupported.
-UnsupportedType_1                 = The \u2018{0}\u2019 type is unsupported.
+UnsupportedArgumentValue_1        = The \u201c{0}\u201d argument value is unsupported.
+UnsupportedDatum_1                = The \u201c{0}\u201d datum is not supported by this operation.
+UnsupportedType_1                 = The \u2018{0}\u2019 type is not supported in this context.
 ValueAlreadyDefined_1             = A value is already defined for \u201c{0}\u201d.
 ValueNotGreaterThanZero_2         = Value \u2018{0}\u2019 = {1} is invalid. Expected a number greater than 0.
 ValueOutOfRange_4                 = Value \u2018{0}\u2019 = {3} is invalid. Expected a value in the [{1} \u2026 {2}] range.

Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties?rev=1789729&r1=1789728&r2=1789729&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties [ISO-8859-1] (original)
+++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties [ISO-8859-1] Fri Mar 31 18:49:16 2017
@@ -22,6 +22,7 @@
 #   U+00A0 NO-BREAK SPACE         before  :
 #
 AmbiguousName_3                   = Le nom \u00ab\u202f{2}\u202f\u00bb est ambigu\u00eb car il peut \u00eatre interpr\u00e9t\u00e9 comme \u00ab\u202f{0}\u202f\u00bb ou \u00ab\u202f{1}\u202f\u00bb.
+CanIterateOnlyOnce                = Cet objet ne peut it\u00e9rer qu\u2019une seule fois.
 CanNotAddToExclusiveSet_2         = Aucun \u00e9l\u00e9ment ne peut \u00eatre ajout\u00e9 \u00e0 cet ensemble car les propri\u00e9t\u00e9s \u2018{0}\u2019 et \u2018{1}\u2019 sont mutuellement exclusives.
 CanNotAssign_2                    = Ne peut pas assigner \u00ab\u202f{1}\u202f\u00bb \u00e0 \u00ab\u202f{0}\u202f\u00bb.
 CanNotAssignUnitToDimension_2     = Ne peut pas assigner les unit\u00e9s \u00ab\u202f{1}\u202f\u00bb \u00e0 la dimension \u00ab\u202f{0}\u202f\u00bb.
@@ -29,6 +30,7 @@ CanNotConnectTo_1                 = Ne p
 CanNotConvertFromType_2           = Ne peut pas convertir du type \u2018{0}\u2019 vers le type \u2018{1}\u2019.
 CanNotConvertValue_2              = La valeur \u00ab\u202f{0}\u202f\u00bb ne peut pas \u00eatre convertie vers le type \u2018{1}\u2019.
 CanNotCompute_1                   = Ne peut pas calculer \u00ab\u202f{0}\u202f\u00bb.
+CanNotCopy_1                      = Ne peut pas copier \u00ab\u202f{0}\u202f\u00bb.
 CanNotOpen_1                      = Ne peut pas ouvrir \u00ab\u202f{0}\u202f\u00bb.
 CanNotParseFile_2                 = Ne peut pas lire \u00ab\u202f{1}\u202f\u00bb comme un fichier au format {0}.
 CanNotRead_1                      = Ne peut pas lire \u00ab\u202f{0}\u202f\u00bb.
@@ -83,7 +85,7 @@ IllegalPropertyValueClass_2       = La p
 IllegalPropertyValueClass_3       = Une instance \u2018{1}\u2019 \u00e9tait attendue pour la propri\u00e9t\u00e9 \u00ab\u202f{0}\u202f\u00bb, mais la valeur donn\u00e9e est une instance de \u2018{2}\u2019.
 IllegalRange_2                    = La plage [{0} \u2026 {1}] n\u2019est pas valide.
 IllegalUnicodeCodePoint_2         = La valeur {1} de \u00ab\u202f{0}\u202f\u00bb n\u2019est pas un code Unicode valide.
-IncompatibleFormat_2              = Le format \u00ab\u202f{1}\u202f\u00bb ne s\u2019applique pas \u00e0 \u00ab\u202f{0}\u202f\u00bb.
+IncompatibleFormat_2              = Le format {1} ne s\u2019applique pas \u00e0 \u00ab\u202f{0}\u202f\u00bb.
 IncompatiblePropertyValue_1       = La valeur de la propri\u00e9t\u00e9 \u00ab\u202f{0}\u202f\u00bb n\u2019est pas compatible.
 IncompatibleUnit_1                = L\u2019unit\u00e9 \u00ab\u202f{0}\u202f\u00bb n\u2019est pas compatible avec la valeur actuelle.
 IncompatibleUnits_2               = Les unit\u00e9s \u00ab\u202f{0}\u202f\u00bb et \u00ab\u202f{1}\u202f\u00bb ne sont pas compatibles.
@@ -129,6 +131,7 @@ NonTemporalUnit_1                 = \u00
 NonSystemUnit_1                   = \u00ab\u202f{0}\u202f\u00bb n\u2019est pas une unit\u00e9 fondamentale ou d\u00e9riv\u00e9e.
 NonRatioUnit_1                    = L\u2019\u00e9chelle de mesure de l\u2019unit\u00e9 \u00ab\u202f{0}\u202f\u00bb n\u2019est pas une \u00e9chelle de rapports.
 NotABackwardReference_1           = Il n\u2019y a pas d\u2019\u00e9l\u00e9ment pour l\u2019identifiant \u201c{0}\u201d, ou l\u2019identifiant est une r\u00e9f\u00e9rence vers l\u2019avant.
+NotAnInteger_1                    = {0} n\u2019est pas un nombre entier.
 NotAKeyValuePair_1                = \u00ab\u202f{0}\u202f\u00bb n\u2019est pas une paire cl\u00e9-valeur.
 NotANumber_1                      = L\u2019argument \u2018{0}\u2019 ne doit pas \u00eatre NaN (Not-a-Number).
 NotAPrimitiveWrapper_1            = La classe \u2018{0}\u2019 n\u2019est pas un adaptateur d\u2019un type primitif.
@@ -184,7 +187,9 @@ UnsupportedFormatVersion_2        = La v
 UnsupportedImplementation_1       = Cette instance de \u2018{0}\u2019 ne peut pas \u00eatre g\u00e9r\u00e9e parce que les impl\u00e9mentations arbitraires ne sont pas encore support\u00e9es.
 UnsupportedInterpolation_1        = L\u2019interpolation \u201c{0}\u201d n\u2019est pas support\u00e9e.
 UnsupportedOperation_1            = L\u2019op\u00e9ration \u2018{0}\u2019 n\u2019est pas support\u00e9e.
-UnsupportedType_1                 = Le type \u2018{0}\u2019 n\u2019est pas support\u00e9.
+UnsupportedArgumentValue_1        = La valeur d\u2019argument \u00ab\u202f{0}\u202f\u00bb n\u2019est pas support\u00e9e.
+UnsupportedDatum_1                = Le r\u00e9f\u00e9rentiel \u00ab\u202f{0}\u202f\u00bb n\u2019est pas support\u00e9 par cette op\u00e9ration.
+UnsupportedType_1                 = Le type \u2018{0}\u2019 n\u2019est pas support\u00e9 dans ce contexte.
 ValueAlreadyDefined_1             = Une valeur est d\u00e9j\u00e0 d\u00e9finie pour \u00ab\u202f{0}\u202f\u00bb.
 ValueNotGreaterThanZero_2         = La valeur \u2018{0}\u2019 = {1} n\u2019est pas valide. On attendait un nombre positif non-nul.
 ValueOutOfRange_4                 = La valeur \u2018{0}\u2019 = {3} est invalide. Une valeur dans la plage [{1} \u2026 {2}] \u00e9tait attendue.

Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/resources/IndexedResourceBundle.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/resources/IndexedResourceBundle.java?rev=1789729&r1=1789728&r2=1789729&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/resources/IndexedResourceBundle.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/resources/IndexedResourceBundle.java [UTF-8] Fri Mar 31 18:49:16 2017
@@ -452,6 +452,25 @@ public class IndexedResourceBundle exten
     }
 
     /**
+     * Writes the localized string identified by the given key followed by a colon.
+     * The way to write the colon depends on the language.
+     *
+     * @param  key         the key for the desired string.
+     * @param  toAppendTo  where to write the localized string followed by a colon.
+     * @throws IOException if an error occurred while writing to the given destination.
+     *
+     * @since 0.8
+     */
+    public final void appendLabel(final short key, final Appendable toAppendTo) throws IOException {
+        toAppendTo.append(getString(key));
+        if (Locale.FRENCH.getLanguage().equals(getLocale().getLanguage())) {
+            toAppendTo.append("\u00A0:");
+        } else {
+            toAppendTo.append(':');
+        }
+    }
+
+    /**
      * Gets a string for the given key and appends "…" to it.
      * This method is typically used for creating menu items.
      *
@@ -471,7 +490,10 @@ public class IndexedResourceBundle exten
      * @param  key  the key for the desired string.
      * @return the string for the given key.
      * @throws MissingResourceException if no object for the given key can be found.
+     *
+     * @deprecated Replaced by {@link #appendLabel(short, Appendable)}.
      */
+    @Deprecated
     public final String getLabel(final short key) throws MissingResourceException {
         String label = getString(key);
         if (Locale.FRENCH.getLanguage().equals(getLocale().getLanguage())) {

Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java?rev=1789729&r1=1789728&r2=1789729&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java [UTF-8] Fri Mar 31 18:49:16 2017
@@ -62,11 +62,21 @@ public final class Vocabulary extends In
         public static final short Accuracy = 1;
 
         /**
+         * Administrator
+         */
+        public static final short Administrator = 130;
+
+        /**
          * Aliases
          */
         public static final short Aliases = 2;
 
         /**
+         * Alternative identifiers
+         */
+        public static final short AlternativeIdentifiers = 131;
+
+        /**
          * Angle
          */
         public static final short Angle = 3;
@@ -127,6 +137,11 @@ public final class Vocabulary extends In
         public static final short Classpath = 14;
 
         /**
+         * Code
+         */
+        public static final short Code = 128;
+
+        /**
          * {0} code
          */
         public static final short Code_1 = 15;
@@ -147,6 +162,16 @@ public final class Vocabulary extends In
         public static final short Container = 18;
 
         /**
+         * Coordinate
+         */
+        public static final short Coordinate = 129;
+
+        /**
+         * Coordinate reference system
+         */
+        public static final short CoordinateRefSys = 132;
+
+        /**
          * Correlation
          */
         public static final short Correlation = 19;
@@ -252,6 +277,11 @@ public final class Vocabulary extends In
         public static final short DublinJulian = 38;
 
         /**
+         * East bound
+         */
+        public static final short EastBound = 133;
+
+        /**
          * Ellipsoid
          */
         public static final short Ellipsoid = 39;
@@ -267,6 +297,11 @@ public final class Vocabulary extends In
         public static final short EllipsoidalHeight = 41;
 
         /**
+         * End date
+         */
+        public static final short EndDate = 134;
+
+        /**
          * {0} entr{0,choice,0#y|2#ies}
          */
         public static final short EntryCount_1 = 121;
@@ -292,6 +327,11 @@ public final class Vocabulary extends In
         public static final short GeodeticDataset = 45;
 
         /**
+         * Geographic identifier
+         */
+        public static final short GeographicIdentifier = 135;
+
+        /**
          * Height
          */
         public static final short Height = 46;
@@ -382,6 +422,11 @@ public final class Vocabulary extends In
         public static final short Localization = 63;
 
         /**
+         * Location type
+         */
+        public static final short LocationType = 136;
+
+        /**
          * Logging
          */
         public static final short Logging = 64;
@@ -397,6 +442,11 @@ public final class Vocabulary extends In
         public static final short Mandatory = 66;
 
         /**
+         * Mapping
+         */
+        public static final short Mapping = 127;
+
+        /**
          * Maximum value
          */
         public static final short MaximumValue = 67;
@@ -432,6 +482,11 @@ public final class Vocabulary extends In
         public static final short None = 73;
 
         /**
+         * North bound
+         */
+        public static final short NorthBound = 137;
+
+        /**
          * Note
          */
         public static final short Note = 74;
@@ -527,6 +582,11 @@ public final class Vocabulary extends In
         public static final short RemoteConfiguration = 89;
 
         /**
+         * Representative value
+         */
+        public static final short RepresentativeValue = 141;
+
+        /**
          * Root
          */
         public static final short Root = 90;
@@ -552,11 +612,21 @@ public final class Vocabulary extends In
         public static final short Source = 93;
 
         /**
+         * South bound
+         */
+        public static final short SouthBound = 138;
+
+        /**
          * Standard deviation
          */
         public static final short StandardDeviation = 94;
 
         /**
+         * Start date
+         */
+        public static final short StartDate = 139;
+
+        /**
          * Subset of {0}
          */
         public static final short SubsetOf_1 = 95;
@@ -677,6 +747,11 @@ public final class Vocabulary extends In
         public static final short Warnings = 117;
 
         /**
+         * West bound
+         */
+        public static final short WestBound = 140;
+
+        /**
          * World
          */
         public static final short World = 118;

Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties?rev=1789729&r1=1789728&r2=1789729&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties [ISO-8859-1] (original)
+++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties [ISO-8859-1] Fri Mar 31 18:49:16 2017
@@ -15,7 +15,9 @@
 # limitations under the License.
 #
 Accuracy                = Accuracy
+Administrator           = Administrator
 Aliases                 = Aliases
+AlternativeIdentifiers  = Alternative identifiers
 Angle                   = Angle
 AngularDegrees          = Degrees
 AngularMinutes          = Minutes
@@ -28,10 +30,13 @@ CausedBy_1              = Caused by {0}
 CharacterEncoding       = Character encoding
 Characteristics         = Characteristics
 Classpath               = Classpath
+Code                    = Code
 Code_1                  = {0} code
 Commands                = Commands
 ConstantPressureSurface = Constant pressure surface
 Container               = Container
+Coordinate              = Coordinate
+CoordinateRefSys        = Coordinate reference system
 Correlation             = Correlation
 CurrentDateTime         = Current date and time
 CurrentDirectory        = Current directory
@@ -53,14 +58,17 @@ Directory               = Directory
 DittoMark               = \u2033
 Domain                  = Domain
 DublinJulian            = Dublin Julian
+EastBound               = East bound
 Ellipsoid               = Ellipsoid
 EllipsoidChange         = Ellipsoid change
 EllipsoidalHeight       = Ellipsoidal height
+EndDate                 = End date
 EntryCount_1            = {0} entr{0,choice,0#y|2#ies}
 Geocentric              = Geocentric
 GeocentricRadius        = Geocentric radius
 GeocentricConversion    = Geocentric conversion
 GeodeticDataset         = Geodetic dataset
+GeographicIdentifier    = Geographic identifier
 Height                  = Height
 Identifier              = Identifier
 Implementation          = Implementation
@@ -80,8 +88,10 @@ Libraries               = Libraries
 LocalConfiguration      = Local configuration
 Locale                  = Locale
 Localization            = Localization
+LocationType            = Location type
 Logging                 = Logging
 Mandatory               = Mandatory
+Mapping                 = Mapping
 MaximumValue            = Maximum value
 MeanValue               = Mean value
 MinimumValue            = Minimum value
@@ -90,6 +100,7 @@ ModifiedJulian          = Modified Julia
 Name                    = Name
 None                    = None
 Note                    = Note
+NorthBound              = North bound
 NumberOfValues          = Number of values
 NumberOfNaN             = Number of \u2018NaN\u2019
 Obligation              = Obligation
@@ -108,12 +119,15 @@ Quoted_1                = \u201c{0}\u201
 Read                    = Read
 Remarks                 = Remarks
 RemoteConfiguration     = Remote configuration
+RepresentativeValue     = Representative value
 Root                    = Root
 RootMeanSquare          = Root Mean Square
 Scale                   = Scale
 SlashSeparatedList_2    = {0}/{1}
 Source                  = Source
+SouthBound              = South bound
 StandardDeviation       = Standard deviation
+StartDate               = Start date
 SubsetOf_1              = Subset of {0}
 SupersededBy_1          = Superseded by {0}.
 TemporaryFiles          = Temporary files
@@ -138,5 +152,6 @@ Version_2               = {0} version {1
 Versions                = Versions
 Vertical                = Vertical
 Warnings                = Warnings
+WestBound               = West bound
 World                   = World
 Write                   = Write

Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties?rev=1789729&r1=1789728&r2=1789729&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties [ISO-8859-1] (original)
+++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties [ISO-8859-1] Fri Mar 31 18:49:16 2017
@@ -22,7 +22,9 @@
 #   U+00A0 NO-BREAK SPACE         before  :
 #
 Accuracy                = Pr\u00e9cision
+Administrator           = Administrateur
 Aliases                 = Alias
+AlternativeIdentifiers  = Identifiants alternatifs
 Angle                   = Angle
 AngularDegrees          = Degr\u00e9s
 AngularMinutes          = Minutes
@@ -35,10 +37,13 @@ CausedBy_1              = Caus\u00e9e pa
 CharacterEncoding       = Encodage des caract\u00e8res
 Characteristics         = Caract\u00e9ristiques
 Classpath               = Chemin de classes
+Code                    = Code
 Code_1                  = Code {0}
 Commands                = Commandes
 ConstantPressureSurface = Surface \u00e0 pression constante
 Container               = Conteneur
+Coordinate              = Coordonn\u00e9e
+CoordinateRefSys        = Syst\u00e8me de r\u00e9f\u00e9rence des coordonn\u00e9es
 Correlation             = Corr\u00e9lation
 CurrentDateTime         = Date et heure courantes
 CurrentDirectory        = R\u00e9pertoire courant
@@ -60,14 +65,17 @@ Directory               = R\u00e9pertoir
 DittoMark               = \u2033
 Domain                  = Domaine
 DublinJulian            = Julien Dublin
+EastBound               = Limite est
 Ellipsoid               = Ellipso\u00efde
 EllipsoidChange         = Changement d\u2019ellipso\u00efde
 EllipsoidalHeight       = Hauteur ellipso\u00efdale
 EntryCount_1            = {0} entr\u00e9e{0,choice,0#|2#s}
+EndDate                 = Date de fin
 Geocentric              = G\u00e9ocentrique
 GeocentricRadius        = Rayon g\u00e9ocentrique
 GeocentricConversion    = Conversion g\u00e9ocentrique
 GeodeticDataset         = Base de donn\u00e9es g\u00e9od\u00e9sique
+GeographicIdentifier    = Identifiant g\u00e9ographique
 Height                  = Hauteur
 Identifier              = Identifiant
 Implementation          = Impl\u00e9mentation
@@ -87,8 +95,10 @@ Libraries               = Biblioth\u00e8
 LocalConfiguration      = Configuration locale
 Locale                  = Locale
 Localization            = R\u00e9gionalisation
+LocationType            = Type de location
 Logging                 = Journalisation
 Mandatory               = Requis
+Mapping                 = Cartographie
 MaximumValue            = Valeur maximale
 MeanValue               = Valeur moyenne
 MinimumValue            = Valeur minimale
@@ -97,6 +107,7 @@ ModifiedJulian          = Julien modifi\
 Name                    = Nom
 None                    = Aucun
 Note                    = Note
+NorthBound              = Limite nord
 NumberOfValues          = Nombre de valeurs
 NumberOfNaN             = Nombre de \u2018NaN\u2019
 Obligation              = Obligation
@@ -115,12 +126,15 @@ Quoted_1                = \u00ab\u202f{0
 Read                    = Lecture
 Remarks                 = Remarques
 RemoteConfiguration     = Configuration distante
+RepresentativeValue     = Valeur repr\u00e9sentative
 Root                    = Racine
 RootMeanSquare          = Moyenne quadratique
 Scale                   = \u00c9chelle
 SlashSeparatedList_2    = {0}/{1}
 Source                  = Source
+SouthBound              = Limite sud
 StandardDeviation       = \u00c9cart type
+StartDate               = Date de d\u00e9part
 SubsetOf_1              = Sous-ensemble de {0}
 SupersededBy_1          = Remplac\u00e9 par {0}.
 TemporaryFiles          = Fichiers temporaires
@@ -145,5 +159,6 @@ Version_2               = {0} version {1
 Versions                = Versions
 Vertical                = Vertical
 Warnings                = Avertissements
+WestBound               = Limite ouest
 World                   = Monde
 Write                   = \u00c9criture

Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/xml/MarshallerPool.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/xml/MarshallerPool.java?rev=1789729&r1=1789728&r2=1789729&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/xml/MarshallerPool.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/xml/MarshallerPool.java [UTF-8] Fri Mar 31 18:49:16 2017
@@ -32,7 +32,9 @@ import org.apache.sis.internal.system.De
 import org.apache.sis.internal.system.DefaultFactories;
 import org.apache.sis.internal.jaxb.AdapterReplacement;
 import org.apache.sis.internal.jaxb.TypeRegistration;
+import org.apache.sis.internal.util.Constants;
 import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.CharSequences;
 
 
 /**
@@ -66,11 +68,6 @@ import org.apache.sis.util.ArgumentCheck
  */
 public class MarshallerPool {
     /**
-     * The indentation string, fixed to 2 spaces instead of 4 because ISO/OGC XML are very verbose.
-     */
-    private static final String INDENTATION = "  ";
-
-    /**
      * Amount of nanoseconds to wait before to remove unused (un)marshallers.
      * This is a very approximative value: actual timeout will not be shorter,
      * but may be twice longer.
@@ -78,21 +75,19 @@ public class MarshallerPool {
     private static final long TIMEOUT = 15000000000L;           // 15 seconds.
 
     /**
-     * Kind of JAXB implementations.
-     */
-    private static final byte INTERNAL = 0, ENDORSED = 1, OTHER = 2;
-
-    /**
      * The JAXB context to use for creating marshaller and unmarshaller.
+     *
+     * @see #createMarshaller()
+     * @see #createUnmarshaller()
      */
-    private final JAXBContext context;
+    protected final JAXBContext context;
 
     /**
-     * {@link #INTERNAL} if the JAXB implementation is the one bundled in the JDK,
-     * {@link #ENDORSED} if the TAXB implementation is the endorsed JAXB (Glassfish), or
-     * {@link #OTHER} if unknown.
+     * {@code INTERNAL} if the JAXB implementation is the one bundled in the JDK,
+     * {@code ENDORSED} if the TAXB implementation is the endorsed JAXB (Glassfish), or
+     * {@code null} if unknown.
      */
-    private final byte implementation;
+    private final Implementation implementation;
 
     /**
      * The mapper between namespaces and prefix.
@@ -164,7 +159,12 @@ public class MarshallerPool {
      * @throws JAXBException if the JAXB context can not be created.
      */
     public MarshallerPool(final Map<String,?> properties) throws JAXBException {
-        this(TypeRegistration.getSharedContext(), properties);
+        /*
+         * We currently add the default root adapters only when using the JAXB context provided by Apache SIS.
+         * We presume that if the user specified his own JAXBContext, then he does not expect us to change the
+         * classes that he wants to marshal.
+         */
+        this(TypeRegistration.getSharedContext(), TypeRegistration.addDefaultRootAdapters(properties));
     }
 
     /**
@@ -186,30 +186,12 @@ public class MarshallerPool {
         ArgumentChecks.ensureNonNull("context", context);
         this.context = context;
         replacements = DefaultFactories.createServiceLoader(AdapterReplacement.class);
-        /*
-         * Detects if we are using the endorsed JAXB implementation (i.e. the one provided in
-         * separated JAR files) or the one bundled in JDK 6. We use the JAXB context package
-         * name as a criterion:
-         *
-         *   JAXB endorsed JAR uses    "com.sun.xml.bind"
-         *   JAXB bundled in JDK uses  "com.sun.xml.internal.bind"
-         */
-        String classname = context.getClass().getName();
-        if (classname.startsWith("com.sun.xml.internal.bind.")) {
-            classname = "org.apache.sis.xml.OGCNamespacePrefixMapper";
-            implementation = INTERNAL;
-        } else if (classname.startsWith(Pooled.ENDORSED_PREFIX)) {
-            classname = "org.apache.sis.xml.OGCNamespacePrefixMapper_Endorsed";
-            implementation = ENDORSED;
-        } else {
-            classname = null;
-            implementation = OTHER;
-        }
+        implementation = Implementation.detect(context);
         /*
          * Prepares a copy of the property map (if any), then removes the
          * properties which are handled especially by this constructor.
          */
-        template = new PooledTemplate(properties, implementation == INTERNAL);
+        template = new PooledTemplate(properties, implementation);
         final Object rootNamespace = template.remove(XML.DEFAULT_NAMESPACE, "");
         /*
          * Instantiates the OGCNamespacePrefixMapper appropriate for the implementation
@@ -217,6 +199,7 @@ public class MarshallerPool {
          * usual ClassNotFoundException if the class was found but its parent class has
          * not been found.
          */
+        final String classname = implementation.mapper;
         if (classname == null) {
             mapper = null;
         } else try {
@@ -243,8 +226,10 @@ public class MarshallerPool {
         try {
             ((Pooled) marshaller).reset(template);
         } catch (JAXBException exception) {
-            // Not expected to happen because we are supposed
-            // to reset the properties to their initial values.
+            /*
+             * Not expected to happen because we are supposed
+             * to reset the properties to their initial values.
+             */
             Logging.unexpectedException(Logging.getLogger(Loggers.XML), MarshallerPool.class, "recycle", exception);
             return;
         }
@@ -438,23 +423,23 @@ public class MarshallerPool {
      *
      * @return a new marshaller configured for formatting OGC/ISO XML.
      * @throws JAXBException if an error occurred while creating and configuring the marshaller.
+     *
+     * @see #context
+     * @see #acquireMarshaller()
      */
     protected Marshaller createMarshaller() throws JAXBException {
         final Marshaller marshaller = context.createMarshaller();
         marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
-        marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
-        switch (implementation) {
-            case INTERNAL: {
-                marshaller.setProperty("com.sun.xml.internal.bind.namespacePrefixMapper", mapper);
-                marshaller.setProperty("com.sun.xml.internal.bind.indentString", INDENTATION);
-                break;
-            }
-            case ENDORSED: {
-                marshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper", mapper);
-                marshaller.setProperty("com.sun.xml.bind.indentString", INDENTATION);
-                break;
-            }
-            // Do nothing for the OTHER case.
+        /*
+         * Note: we do not set the Marshaller.JAXB_ENCODING property because specification
+         * said that the default value is "UTF-8", which is what we want.
+         */
+        String key;
+        if ((key = implementation.mapperKey) != null) {
+            marshaller.setProperty(key, mapper);
+        }
+        if ((key = implementation.indentKey) != null) {
+            marshaller.setProperty(key, CharSequences.spaces(Constants.DEFAULT_INDENTATION));
         }
         synchronized (replacements) {
             for (final AdapterReplacement adapter : replacements) {
@@ -471,6 +456,9 @@ public class MarshallerPool {
      *
      * @return a new unmarshaller configured for parsing OGC/ISO XML.
      * @throws JAXBException if an error occurred while creating and configuring the unmarshaller.
+     *
+     * @see #context
+     * @see #acquireUnmarshaller()
      */
     protected Unmarshaller createUnmarshaller() throws JAXBException {
         final Unmarshaller unmarshaller = context.createUnmarshaller();

Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/xml/Pooled.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/xml/Pooled.java?rev=1789729&r1=1789728&r2=1789729&view=diff
==============================================================================
--- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/xml/Pooled.java [UTF-8] (original)
+++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/xml/Pooled.java [UTF-8] Fri Mar 31 18:49:16 2017
@@ -38,6 +38,7 @@ import org.apache.sis.util.logging.Warni
 import org.apache.sis.internal.util.CollectionsExt;
 import org.apache.sis.internal.jaxb.Context;
 import org.apache.sis.internal.jaxb.LegacyNamespaces;
+import org.apache.sis.internal.jaxb.TypeRegistration;
 
 
 /**
@@ -49,7 +50,7 @@ import org.apache.sis.internal.jaxb.Lega
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.3
- * @version 0.3
+ * @version 0.8
  * @module
  */
 abstract class Pooled {
@@ -60,21 +61,12 @@ abstract class Pooled {
     private static final String[] SCHEMA_KEYS = {"gmd"};
 
     /**
-     * The prefix of property names which are provided in external (endorsed) implementation of JAXB.
-     * This is slightly different than the prefix used by the implementation bundled with the JDK 6,
-     * which is {@code "com.sun.xml.internal.bind"}.
-     *
-     * @see #convertPropertyKey(String)
-     */
-    static final String ENDORSED_PREFIX = "com.sun.xml.bind.";
-
-    /**
      * {@code true} if the JAXB implementation is the one bundled in JDK 6, or {@code false}
      * if this is the external implementation provided as a JAR file in the endorsed directory.
      * If {@code true}, then an additional {@code "internal"} package name needs to be inserted
      * in the property keys.
      *
-     * @see #convertPropertyKey(String)
+     * @see Implementation#toInternal(String)
      */
     private final boolean internal;
 
@@ -156,6 +148,14 @@ abstract class Pooled {
     private ValueConverter converter;
 
     /**
+     * Converters from arbitrary classes implementing GeoAPI interfaces to Apache SIS implementations
+     * providing JAXB annotations, or null or an empty array if none. This is used at marshalling time.
+     *
+     * @see #getRootAdapters()
+     */
+    private TypeRegistration[] rootAdapters;
+
+    /**
      * The object to inform about warnings, or {@code null} if none.
      */
     private WarningListener<?> warningListener;
@@ -170,7 +170,7 @@ abstract class Pooled {
     /**
      * Creates a {@link PooledTemplate}.
      *
-     * @param internal {@code true} if the JAXB implementation is the one bundled in JDK 6,
+     * @param internal  {@code true} if the JAXB implementation is the one bundled in JDK 6,
      *        or {@code false} if this is the external implementation provided as a JAR file
      *        in the endorsed directory.
      */
@@ -199,7 +199,7 @@ abstract class Pooled {
      * @throws JAXBException if an error occurred while setting a property.
      */
     final void initialize(final Pooled template) throws JAXBException {
-        reset(template); // Set the SIS properties first. JAXB properties are set below.
+        reset(template);     // Set the SIS properties first. JAXB properties are set below.
         for (final Map.Entry<Object,Object> entry : template.initialProperties.entrySet()) {
             setStandardProperty((String) entry.getKey(), entry.getValue());
         }
@@ -226,6 +226,7 @@ abstract class Pooled {
         versionGML       = template.versionGML;
         resolver         = template.resolver;
         converter        = template.converter;
+        rootAdapters     = template.rootAdapters;
         warningListener  = template.warningListener;
         resetTime        = System.nanoTime();
         if (this instanceof Marshaller) {
@@ -303,22 +304,6 @@ abstract class Pooled {
     }
 
     /**
-     * Converts a property key from the JAXB name to the underlying implementation name.
-     * This applies only to property keys in the {@code "com.sun.xml.bind"} namespace.
-     *
-     * @param  key  the JAXB property key.
-     * @return the property key to use.
-     */
-    private String convertPropertyKey(String key) {
-        if (internal && key.startsWith(ENDORSED_PREFIX)) {
-            final StringBuilder buffer = new StringBuilder(key.length() + 10);
-            key = buffer.append("com.sun.xml.internal.bind.")
-                    .append(key, ENDORSED_PREFIX.length(), key.length()).toString();
-        }
-        return key;
-    }
-
-    /**
      * A method which is common to both {@code Marshaller} and {@code Unmarshaller}.
      * It saves the initial state if it was not already done, but subclasses will
      * need to complete the work.
@@ -397,6 +382,11 @@ abstract class Pooled {
                     }
                     return;
                 }
+                case TypeRegistration.ROOT_ADAPTERS: {
+                    rootAdapters = (TypeRegistration[]) value;
+                    // No clone for now because ROOT_ADAPTERS is not yet a public API.
+                    return;
+                }
             }
         } catch (ClassCastException | IllformedLocaleException e) {
             throw new PropertyException(Errors.format(
@@ -406,7 +396,9 @@ abstract class Pooled {
          * If we reach this point, the given name is not a SIS property. Try to handle
          * it as a (un)marshaller-specific property, after saving the previous value.
          */
-        name = convertPropertyKey(name);
+        if (internal) {
+            name = Implementation.toInternal(name);
+        }
         if (!initialProperties.containsKey(name)) {
             if (initialProperties.put(name, getStandardProperty(name)) != null) {
                 // Should never happen, unless on concurrent changes in a backgroung thread.
@@ -420,7 +412,7 @@ abstract class Pooled {
      * A method which is common to both {@code Marshaller} and {@code Unmarshaller}.
      */
     @SuppressWarnings("ReturnOfCollectionOrArrayField")     // Because unmodifiable.
-    public final Object getProperty(final String name) throws PropertyException {
+    public final Object getProperty(String name) throws PropertyException {
         switch (name) {
             case XML.LOCALE:           return locale;
             case XML.TIMEZONE:         return timezone;
@@ -445,8 +437,12 @@ abstract class Pooled {
                     default: return null;
                 }
             }
+            case TypeRegistration.ROOT_ADAPTERS: return (rootAdapters != null) ? rootAdapters.clone() : null;
             default: {
-                return getStandardProperty(convertPropertyKey(name));
+                if (internal) {
+                    name = Implementation.toInternal(name);
+                }
+                return getStandardProperty(name);
             }
         }
     }
@@ -493,6 +489,18 @@ abstract class Pooled {
     public abstract <A extends XmlAdapter> A getAdapter(final Class<A> type);
 
     /**
+     * Returns the adapters to apply on the root object to marshal, or {@code null} or an empty array if none.
+     * This is used for converting from arbitrary implementations of GeoAPI interfaces to Apache SIS implementations
+     * providing JAXB annotations.
+     *
+     * @return a direct reference to the internal array of converters - do not modify.
+     */
+    @SuppressWarnings("ReturnOfCollectionOrArrayField")
+    final TypeRegistration[] getRootAdapters() {
+        return rootAdapters;
+    }
+
+    /**
      * A method which is common to both {@code Marshaller} and {@code Unmarshaller}.
      * It saves the initial state if it was not already done, but subclasses will
      * need to complete the work.



Mime
View raw message