This is an automated email from the ASF dual-hosted git repository. desruisseaux pushed a commit to branch geoapi-4.0 in repository https://gitbox.apache.org/repos/asf/sis.git
commit 85cfff7a0c67f3367f1f0ed4ca0da9e7443b2a52 Author: Martin Desruisseaux <[email protected]> AuthorDate: Thu Nov 19 00:51:38 2020 +0100 Handle the corner-case of coordinate system WKT (the "CS" keyword) in definition of aliases. --- .../java/org/apache/sis/io/wkt/AbstractParser.java | 19 +++- .../main/java/org/apache/sis/io/wkt/Element.java | 2 +- .../apache/sis/io/wkt/GeodeticObjectParser.java | 4 +- .../org/apache/sis/io/wkt/SingletonElement.java | 83 +++++++++++++++ .../java/org/apache/sis/io/wkt/StoredTree.java | 116 +++++++++++++++++---- .../java/org/apache/sis/io/wkt/WKTDictionary.java | 30 ++++-- .../main/java/org/apache/sis/io/wkt/WKTFormat.java | 53 +++++++--- .../resources/org/apache/sis/io/wkt/ExtraCRS.txt | 30 ++---- 8 files changed, 267 insertions(+), 70 deletions(-) diff --git a/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/AbstractParser.java b/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/AbstractParser.java index 09d9e77..74196b9 100644 --- a/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/AbstractParser.java +++ b/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/AbstractParser.java @@ -237,10 +237,11 @@ abstract class AbstractParser implements Parser { */ @Override public final Object createFromWKT(final String wkt) throws FactoryException { + final ParsePosition position = new ParsePosition(0); Object result = null; Warnings warnings; try { - result = createFromWKT(wkt, new ParsePosition(0)); + result = createFromWKT(wkt, position); } catch (ParseException exception) { final Throwable cause = exception.getCause(); if (cause instanceof FactoryException) { @@ -250,6 +251,12 @@ abstract class AbstractParser implements Parser { } finally { warnings = getAndClearWarnings(result); } + final CharSequence unparsed = CharSequences.token(wkt, position.getIndex()); + if (unparsed.length() != 0) { + throw new FactoryException(Errors.getResources(errorLocale).getString( + Errors.Keys.UnexpectedCharactersAfter_2, + CharSequences.token(wkt, 0) + "[…]", unparsed)); + } if (warnings != null) { log(new LogRecord(Level.WARNING, warnings.toString())); } @@ -259,8 +266,10 @@ abstract class AbstractParser implements Parser { /** * Parses a <cite>Well-Know Text</cite> from specified position as a geodetic object. * Caller should invoke {@link #getAndClearWarnings(Object)} in a {@code finally} block - * after this method. + * after this method and should decide what to do with remaining character at the end of the string. * + * @param text the Well-Known Text (WKT) to parse. + * @param position index of the first character to parse (on input) or after last parsed character (on output). * @return the parsed object. * @throws ParseException if the string can not be parsed. */ @@ -296,7 +305,7 @@ abstract class AbstractParser implements Parser { * @return the parsed object as a tree of {@link Element}s. * @throws ParseException if the string can not be parsed. * - * @see WKTFormat#textToTree(String, ParsePosition) + * @see WKTFormat#textToTree(String, ParsePosition, String) */ final Element textToTree(final String wkt, final ParsePosition position) throws ParseException { int lower = CharSequences.skipLeadingWhitespaces(wkt, position.getIndex(), wkt.length()); @@ -316,7 +325,9 @@ abstract class AbstractParser implements Parser { throw new UnparsableObjectException(errorLocale, Errors.Keys.NoSuchValue_1, new Object[] {id}, lower); } position.setIndex(upper); - return fragment.toElement(this, ~0); + final SingletonElement singleton = new SingletonElement(); + fragment.toElements(this, singleton, ~0); + return singleton.value; } /** diff --git a/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/Element.java b/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/Element.java index 711170d..192df2d 100644 --- a/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/Element.java +++ b/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/Element.java @@ -267,7 +267,7 @@ final class Element { position.setErrorIndex(lower); throw new UnparsableObjectException(errorLocale, Errors.Keys.NoSuchValue_1, new Object[] {id}, lower); } - children.add(fragment.toElement(parser, ~lower)); // Set offset to '$' in "$FOO". + fragment.toElements(parser, children, ~lower); // Set offset to '$' in "$FOO". lower = upper; } else if (Character.isUnicodeIdentifierStart(firstChar)) { /* diff --git a/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/GeodeticObjectParser.java b/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/GeodeticObjectParser.java index 66ee90d..706f0f4 100644 --- a/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/GeodeticObjectParser.java +++ b/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/GeodeticObjectParser.java @@ -232,8 +232,8 @@ class GeodeticObjectParser extends MathTransformParser implements Comparator<Coo * Caller should invoke {@link #getAndClearWarnings(Object)} in a {@code finally} block * after this method. * - * @param text the text to be parsed. - * @param position the position to start parsing from. + * @param text the Well-Known Text (WKT) to parse. + * @param position index of the first character to parse (on input) or after last parsed character (on output). * @return the parsed object. * @throws ParseException if the string can not be parsed. */ diff --git a/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/SingletonElement.java b/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/SingletonElement.java new file mode 100644 index 0000000..8fce746 --- /dev/null +++ b/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/SingletonElement.java @@ -0,0 +1,83 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sis.io.wkt; + +import java.util.AbstractSet; +import java.util.Collections; +import java.util.Collection; +import java.util.Iterator; + + +/** + * A mutable set containing either {@code null} or a single element. + * If more than one element is added, only the first one is kept. + * This is for use with {@link StoredTree#toElements(AbstractParser, Collection, int)} + * in the common case where we expect exactly one element. + * + * @author Martin Desruisseaux (Geomatys) + * @version 1.1 + * @since 1.1 + * @module + */ +final class SingletonElement extends AbstractSet<Element> { + /** + * The singleton element, or {@code null} if none. + */ + Element value; + + /** + * Creates an initially empty singleton. + */ + SingletonElement() { + } + + /** + * Returns {@code true} if no value has been specified yet. + */ + @Override + public boolean isEmpty() { + return value == null; + } + + /** + * Returns the number of elements in this set, which can not be greater than 1. + */ + @Override + public int size() { + return isEmpty() ? 0 : 1; + } + + /** + * Returns an iterator over the elements in this set. + */ + @Override + public Iterator<Element> iterator() { + return (isEmpty() ? Collections.<Element>emptySet() : Collections.singleton(value)).iterator(); + } + + /** + * Adds the given value if this set is empty. + */ + @Override + public boolean add(final Element n) { + if (isEmpty()) { + value = n; + return true; + } + return false; + } +} diff --git a/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/StoredTree.java b/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/StoredTree.java index d29c13c..8e6259c 100644 --- a/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/StoredTree.java +++ b/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/StoredTree.java @@ -17,7 +17,9 @@ package org.apache.sis.io.wkt; import java.util.Arrays; +import java.util.Collection; import java.util.LinkedList; +import java.util.List; import java.util.Locale; import java.util.Map; import java.util.stream.Stream; @@ -71,7 +73,9 @@ final class StoredTree implements Serializable { private static final long serialVersionUID = 1463070931527783896L; /** - * Copy of {@link Element#keyword} reference. Never {@code null}. + * Copy of {@link Element#keyword} reference, or {@code null} if this node is anonymous. + * Anonymous nodes are used only as wrappers for array of roots in the corner cases + * documented by {@link StoredTree#root}. * * @see StoredTree#keyword() */ @@ -86,9 +90,25 @@ final class StoredTree implements Serializable { private final Object[] children; /** + * Creates an anonymous node for an array of roots. This constructor is only for the corner + * case documented in <cite>"Multi roots"</cite> section of {@link StoredTree#root} javadoc. + * + * @see StoredTree#StoredTree(List, Map) + */ + Node(final Deflater deflater, final List<Element> elements) { + keyword = null; + children = new Node[elements.size()]; + for (int i=0; i<children.length; i++) { + children[i] = deflater.unique(new Node(deflater, elements.get(i))); + } + } + + /** * Creates an immutable copy of the given element. Keywords and children references * are copied in this new {@code Node} but {@link Element#offset}s are copied in a * separated array for making possible to share {@code Node} instances. + * + * @see StoredTree#StoredTree(Element, Map) */ Node(final Deflater deflater, final Element element) { keyword = (String) deflater.unique(element.keyword); @@ -106,12 +126,28 @@ final class StoredTree implements Serializable { } /** + * Copies this node in modifiable {@link Element}s and add them to the given list. + * This is the converse of the {@link #Node(Deflater, List)} constructor. + * This method usually adds exactly one element to the given list, except + * for the "multi-roots" corner case documented in {@link StoredTree#root}. + * + * @see StoredTree#toElements(AbstractParser, Collection, int) + */ + final void toElements(final Inflater inflater, final Collection<? super Element> addTo) { + if (keyword != null) { + addTo.add(toElement(inflater)); // Standard case. + } else { + for (final Node child : (Node[]) children) { + addTo.add(child.toElement(inflater)); + } + } + } + + /** * Copies this node in a modifiable {@link Element}. * This is the converse of the {@link #Node(Deflater, Element)} constructor. - * - * @see StoredTree#toElement(AbstractParser, int) */ - final Element toElement(final Inflater inflater) { + private Element toElement(final Inflater inflater) { final LinkedList<Object> list; if (children == null) { list = null; @@ -144,6 +180,7 @@ final class StoredTree implements Serializable { final Node node = (Node) object; if (node.children != null) { for (final String key : keys) { + // Keyword is never null for children. if (node.keyword.equalsIgnoreCase(key)) { return node; } @@ -180,7 +217,9 @@ final class StoredTree implements Serializable { * @see StoredTree#forEachValue(Consumer) */ final void forEachValue(final Consumer<Object> addTo) { - addTo.accept(keyword); + if (keyword != null) { + addTo.accept(keyword); + } if (children != null) { for (final Object child : children) { addTo.accept(child); @@ -215,6 +254,7 @@ final class StoredTree implements Serializable { */ @Override public int hashCode() { + // We never use hashCode()/equals(Object) with anonymous node (null keyword). int hash = keyword.hashCode(); if (children != null) { for (final Object value : children) { @@ -235,6 +275,7 @@ final class StoredTree implements Serializable { public boolean equals(final Object other) { if (other instanceof Node) { final Node that = (Node) other; + // We never use hashCode()/equals(Object) with anonymous node (null keyword). if (keyword.equals(that.keyword)) { if (children == that.children) { return true; @@ -258,6 +299,23 @@ final class StoredTree implements Serializable { /** * Root of a tree of {@link Element} snapshots. + * + * <h4>Multi-roots</h4> + * There is exactly one root in the vast majority of cases. However there is a situation + * where we need to allow more roots: when user wants to represent a coordinate system. + * A WKT 2 coordinate system looks like: + * + * {@preformat wkt + * CS[Cartesian, 2], + * Axis["Easting (E)", east], + * Axis["Northing (N)", north], + * Unit["metre", 1] + * } + * + * While axes are conceptually parts of coordinate system, they are not declared inside the {@code CS[…]} + * element for historical reasons (for compatibility with WKT 1). For representing such "flattened tree", + * we need an array of roots. We do that by wrapping that array in a synthetic {@link Node} with null + * {@link Node#keyword} (an "anonymous node"). */ private final Node root; @@ -272,28 +330,42 @@ final class StoredTree implements Serializable { private final short[] offsets; /** - * Creates a new {@code StoredTree} with a copy of given arrays. - * Changes to the given array after construction will not affect this {@code StoredTree}. + * Creates a new {@code StoredTree} with a snapshot of given tree of elements. + * + * @param tree root of the tree of WKT elements. + * @param sharedValues pool to use for sharing unique instances of values. + */ + StoredTree(final Element tree, final Map<Object,Object> sharedValues) { + final Deflater deflater = new Deflater(sharedValues); + root = (Node) deflater.unique(new Node(deflater, tree)); + offsets = deflater.offsets(); + } + + /** + * Creates a new {@code StoredTree} with a snapshot of given trees of elements. + * This is for a corner case only; see <cite>"Multi roots"</cite> in {@link #root}. * - * @param root root of the tree of WKT elements. + * @param trees roots of the trees of WKT elements. * @param sharedValues pool to use for sharing unique instances of values. */ - StoredTree(final Element root, final Map<Object,Object> sharedValues) { + StoredTree(final List<Element> trees, final Map<Object,Object> sharedValues) { final Deflater deflater = new Deflater(sharedValues); - this.root = (Node) deflater.unique(new Node(deflater, root)); + root = new Node(deflater, trees); // Do not invoke `unique(…)` on anymous node. offsets = deflater.offsets(); } /** - * Recreates {@link Element} tree. This method is the converse of the constructor. + * Recreates {@link Element} trees. This method is the converse of the constructor. + * This method usually adds exactly one element to the given list, except + * for the "multi-roots" corner case documented in {@link #root}. * * @param parser the parser which will be used for parsing the tree. + * @param addTo where to add the elements. * @param isFragment non-zero if and only if {@link Element#isFragment} shall be {@code true}. * In such case, this value must be <code>~{@linkplain Element#offset}</code>. - * @return root of {@link Element} tree. */ - final Element toElement(final AbstractParser parser, final int isFragment) { - return root.toElement(new Inflater(parser, offsets, isFragment)); + final void toElements(final AbstractParser parser, final Collection<? super Element> addTo, final int isFragment) { + root.toElements(new Inflater(parser, offsets, isFragment), addTo); } /** @@ -302,7 +374,7 @@ final class StoredTree implements Serializable { * Each instances shall be used for constructing only one {@link Node}. After node construction, this * instance lives longer in the {@link #sharedValues} map for sharing {@link #offsets} arrays. * - * @see StoredTree#StoredTree(Element, Map) + * @see StoredTree#StoredTree(List, Map) */ private static final class Deflater { /** @@ -336,16 +408,18 @@ final class StoredTree implements Serializable { } /** - * Returns a unique instance of given node. + * Returns a unique instance of given object. The given value can be a {@link Node} instance + * provided that it is not an anonymous node (i.e. {@link Node#keyword} shall be non-null). * - * @return a previous instance from the pool, or {@code node} if none. + * @param value the value for which to get a unique instance. + * @return a previous instance from the pool, or {@code value} if none. * * @see Node#hashCode() * @see Node#equals(Object) */ - final Object unique(final Object node) { - final Object existing = sharedValues.putIfAbsent(node, node); - return (existing != null) ? existing : node; + final Object unique(final Object value) { + final Object existing = sharedValues.putIfAbsent(value, value); + return (existing != null) ? existing : value; } /** @@ -403,7 +477,7 @@ final class StoredTree implements Serializable { * A helper class for decompressing a tree of {@link Element}s from a tree of {@link Node}s. * This is the converse of {@link Deflater}. * - * @see StoredTree#toElement(AbstractParser, int) + * @see StoredTree#toElements(AbstractParser, Collection, int) */ private static final class Inflater { /** diff --git a/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/WKTDictionary.java b/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/WKTDictionary.java index 996bbc9..854ed10 100644 --- a/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/WKTDictionary.java +++ b/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/WKTDictionary.java @@ -591,11 +591,10 @@ public class WKTDictionary extends GeodeticAuthorityFactory { if (buffer.length() != 0) { pos.setIndex(0); final String wkt = buffer.toString(); - final StoredTree tree = parser.textToTree(wkt, pos); + final StoredTree tree = parser.textToTree(wkt, pos, aliasKey); final int end = pos.getIndex(); if (end < wkt.length()) { // Trailing white spaces already removed by `read(…)`. - throw new FactoryDataException(resources().getString(Resources.Keys.UnexpectedTextAtLine_2, - getLineNumber(), CharSequences.token(wkt, end))); + throw new FactoryDataException(unexpectedText(getLineNumber(), wkt, end)); } if (aliasKey != null) { parser.addFragment(aliasKey, tree); @@ -653,10 +652,8 @@ public class WKTDictionary extends GeodeticAuthorityFactory { /** * Adds definitions of CRS (or other geodetic objects) from Well-Known Texts. Blank strings are ignored. - * Each non-blank {@link String} shall contain the complete definition of at least one geodetic object. - * More than one geodetic object can appear in the same {@link String} if they are separated by spaces - * or line separators. However the same geodetic object can not have its definition splitted in two or - * more {@link String}s. + * Each non-blank {@link String} shall contain the complete definition of exactly one geodetic object. + * A geodetic object can not have its definition splitted in two or more {@link String}s. * * <p>The key associated to each object is given by the {@code ID[…]} or {@code AUTHORITY[…]} element, * which is typically the last element of a WKT string and is mandatory. WKT strings can contain line @@ -682,9 +679,10 @@ public class WKTDictionary extends GeodeticAuthorityFactory { try { while (it.hasNext()) { final String wkt = it.next(); - final int length = CharSequences.skipTrailingWhitespaces(wkt, 0, wkt.length()); - while (pos.getIndex() < length) { - addDefinition(parser.textToTree(wkt, pos)); + addDefinition(parser.textToTree(wkt, pos, null)); + final int end = pos.getIndex(); + if (end < CharSequences.skipTrailingWhitespaces(wkt, 0, wkt.length())) { + throw new FactoryDataException(unexpectedText(lineNumber, wkt, end)); } pos.setIndex(0); lineNumber++; @@ -702,6 +700,18 @@ public class WKTDictionary extends GeodeticAuthorityFactory { } /** + * Produces an error message for unexpected characters at the end of WKT string. + * + * @param lineNumber line where the error occurred. + * @param wkt the WKT being parsed. + * @param end end of WKT parsing. + * @return message to give to exception constructor. + */ + private String unexpectedText(final int lineNumber, final String wkt, final int end) { + return resources().getString(Resources.Keys.UnexpectedTextAtLine_2, lineNumber, CharSequences.token(wkt, end)); + } + + /** * Convenience methods for resources in the language used for error messages. */ private Resources resources() { diff --git a/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/WKTFormat.java b/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/WKTFormat.java index 95a78af..a8d5f7c 100644 --- a/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/WKTFormat.java +++ b/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/WKTFormat.java @@ -23,6 +23,8 @@ import java.util.Set; import java.util.Map; import java.util.HashMap; import java.util.TreeMap; +import java.util.List; +import java.util.ArrayList; import java.util.Collections; import java.io.IOException; import java.text.Format; @@ -761,7 +763,7 @@ public class WKTFormat extends CompoundFormat<Object> { throw new IllegalArgumentException(errors().getString(Errors.Keys.NotAUnicodeIdentifier_1, name)); } final ParsePosition pos = new ParsePosition(0); - final StoredTree definition = textToTree(wkt, pos); + final StoredTree definition = textToTree(wkt, pos, name); final int length = wkt.length(); final int index = CharSequences.skipLeadingWhitespaces(wkt, pos.getIndex(), length); if (index < length) { @@ -790,23 +792,45 @@ public class WKTFormat extends CompoundFormat<Object> { * This method should be invoked only for WKT trees to be stored for a long time. * It should not be invoked for immediate {@link IdentifiedObject} parsing. * - * @param wkt the Well Know Text (WKT) fragment to parse. - * @param pos index of the first character to parse (on input) or after last parsed character (on output). + * <p>If {@code aliasKey} is non-null, this method may return a multi-roots tree. + * See {@link StoredTree#root} for a discussion. Note that in both cases (single + * root or multi-roots), we may have some unparsed characters at the end of the string.</p> + * + * @param wkt the Well Know Text (WKT) fragment to parse. + * @param pos index of the first character to parse (on input) or after last parsed character (on output). + * @param aliasKey key of the alias, or {@code null} if this method is not invoked + * for defining a {@linkplain #addFragment(String, String) fragment}. * @return root of the tree of elements. */ - final StoredTree textToTree(final String wkt, final ParsePosition pos) throws ParseException { - final AbstractParser parser = parser(true); - Element result = null; + final StoredTree textToTree(final String wkt, final ParsePosition pos, final String aliasKey) throws ParseException { + final AbstractParser parser = parser(true); + final List<Element> results = new ArrayList<>(4); warnings = null; try { - result = parser.textToTree(wkt, pos); + for (;;) { + results.add(parser.textToTree(wkt, pos)); + if (aliasKey == null) break; + /* + * If we find a separator (usually a coma), search for another element. Contrarily to equivalent + * loop in `Element(AbstractParser, …)` constructor, we do not parse number or dates because we + * do not have a way as reliable as above-cited constructor to differentiate the kind of value. + */ + final int p = CharSequences.skipLeadingWhitespaces(wkt, pos.getIndex(), wkt.length()); + final String separator = parser.symbols.trimmedSeparator(); + if (!wkt.startsWith(separator, p)) break; + pos.setIndex(p + separator.length()); + } } finally { - warnings = parser.getAndClearWarnings(result); + warnings = parser.getAndClearWarnings(results.isEmpty() ? null : results.get(0)); } if (sharedValues == null) { sharedValues = new HashMap<>(); } - return new StoredTree(result, sharedValues); + if (results.size() == 1) { + return new StoredTree(results.get(0), sharedValues); // Standard case. + } else { + return new StoredTree(results, sharedValues); // Anonymous wrapper around multi-roots. + } } /** @@ -824,7 +848,7 @@ public class WKTFormat extends CompoundFormat<Object> { * In case of error, {@link ParseException#getErrorOffset()} gives the position of the first illegal character. * * @param wkt the character sequence for the object to parse. - * @param pos the position where to start the parsing. + * @param pos index of the first character to parse (on input) or after last parsed character (on output). * @return the parsed object (never {@code null}). * @throws ParseException if an error occurred while parsing the WKT. */ @@ -844,8 +868,9 @@ public class WKTFormat extends CompoundFormat<Object> { } /** - * Parses a tree of {@link Element}s to produce a geodetic object. The {@code root} argument - * should be a value returned by {@link #textToTree(String, ParsePosition)}. + * Parses a tree of {@link Element}s to produce a geodetic object. The {@code tree} argument + * should be a value returned by {@link #textToTree(String, ParsePosition, String)}. + * This method is for {@link WKTDictionary#createObject(String)} usage. * * @param tree the tree of WKT elements. * @return the parsed object (never {@code null}). @@ -854,7 +879,9 @@ public class WKTFormat extends CompoundFormat<Object> { final Object buildFromTree(StoredTree tree) throws ParseException { clear(); final AbstractParser parser = parser(false); - final Element root = new Element(tree.toElement(parser, 0)); + final SingletonElement singleton = new SingletonElement(); + tree.toElements(parser, singleton, 0); + final Element root = new Element(singleton.value); Object result = null; try { result = parser.buildFromTree(root); diff --git a/core/sis-referencing/src/test/resources/org/apache/sis/io/wkt/ExtraCRS.txt b/core/sis-referencing/src/test/resources/org/apache/sis/io/wkt/ExtraCRS.txt index 816038e..8240374 100644 --- a/core/sis-referencing/src/test/resources/org/apache/sis/io/wkt/ExtraCRS.txt +++ b/core/sis-referencing/src/test/resources/org/apache/sis/io/wkt/ExtraCRS.txt @@ -6,12 +6,17 @@ # # Alias for WGS84 geographic CRS. # -SET DEGREE = Unit["Degree", 0.0174532925199433] SET WGS84_BASE = BaseGeodCRS["GCS_WGS_1984", Datum["D_WGS_1984", Ellipsoid["WGS_1984", 6378137, 298.257223563]], - $DEGREE] + AngleUnit["Degree", 0.0174532925199433]] + +SET ELLIPSOIDAL_CS = + CS[ellipsoidal, 2], + Axis["Latitude", north], + Axis["Longitude", east], + AngleUnit["Degree", 0.0174532925199433] # @@ -50,19 +55,13 @@ ProjectedCRS["South_Pole_Stereographic", GeodCRS["Anguilla 1957", Datum["Anguilla 1957", Ellipsoid["Clarke 1880", 6378249.145, 293.465]], - CS[ellipsoidal, 2], - Axis["Latitude", north], - Axis["Longitude", east], - $DEGREE, + $ELLIPSOIDAL_CS, Id["TEST", 102021]] GeodCRS["Anguilla 1957 (bis)", Datum["Anguilla 1957", Ellipsoid["Clarke 1880", 6378249.145, 293.465]], - CS[ellipsoidal, 2], - Axis["Latitude", north], - Axis["Longitude", east], - $DEGREE, + $ELLIPSOIDAL_CS, Id["TEST", 102021, "v2"]] @@ -71,19 +70,12 @@ GeodCRS["Anguilla 1957 (bis)", # The erroneous element should be on the first line for avoiding platform-dependency # caused by various line separators ("\n" versus "\r\n"). # - SET BAD_DATUM = Datum["Erroneous", Ellipsoid["Missing axis length"]] GeodCRS["Error index 69 (on Ellipsoid)", Datum["Erroneous", Ellipsoid["Missing axis length"]], - CS[ellipsoidal, 2], - Axis["Latitude", north], - Axis["Longitude", east], - $DEGREE, + $ELLIPSOIDAL_CS, Id["TEST", "E1"]] GeodCRS["Error index 42 (on $BAD_DATUM)", $BAD_DATUM, - CS[ellipsoidal, 2], - Axis["Latitude", north], - Axis["Longitude", east], - $DEGREE, + $ELLIPSOIDAL_CS, Id["TEST", "E2"]]
