sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] 02/03: Avoid formatting too long parameter values in WKT. In particular interpolation tables can have thousands of values.
Date Sun, 16 Dec 2018 16:47:11 GMT
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 c97b20669a8b436f16c9a1c0d6cc3bfb759df307
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Sun Dec 16 16:04:20 2018 +0100

    Avoid formatting too long parameter values in WKT. In particular interpolation tables
can have thousands of values.
---
 .../apache/sis/internal/metadata/Resources.java    |  5 ++
 .../sis/internal/metadata/Resources.properties     |  1 +
 .../sis/internal/metadata/Resources_fr.properties  |  1 +
 .../org/apache/sis/io/wkt/FormattableObject.java   |  3 +-
 .../main/java/org/apache/sis/io/wkt/Formatter.java | 67 ++++++++++++++++------
 .../main/java/org/apache/sis/io/wkt/WKTFormat.java | 51 ++++++++++++++--
 .../operation/transform/PassThroughTransform.java  |  8 ++-
 7 files changed, 111 insertions(+), 25 deletions(-)

diff --git a/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/Resources.java
b/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/Resources.java
index 2687c4e..63b8335 100644
--- a/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/Resources.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/Resources.java
@@ -71,6 +71,11 @@ public final class Resources extends IndexedResourceBundle {
         public static final short ElementAlreadyInitialized_1 = 2;
 
         /**
+         * … {0} elements omitted …
+         */
+        public static final short ElementsOmitted_1 = 4;
+
+        /**
          * This metadata is not modifiable.
          */
         public static final short UnmodifiableMetadata = 1;
diff --git a/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/Resources.properties
b/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/Resources.properties
index 1e5dda0..9f9540a 100644
--- a/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/Resources.properties
+++ b/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/Resources.properties
@@ -21,4 +21,5 @@
 #
 BoxCrossesAntiMeridian            = Bounding box crosses the antimeridian.
 ElementAlreadyInitialized_1       = This metadata element is already initialized with value
\u201c{0}\u201d.
+ElementsOmitted_1                 = \u2026 {0} elements omitted \u2026
 UnmodifiableMetadata              = This metadata is not modifiable.
diff --git a/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/Resources_fr.properties
b/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/Resources_fr.properties
index 1184335..48a6c58 100644
--- a/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/Resources_fr.properties
+++ b/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/Resources_fr.properties
@@ -26,4 +26,5 @@
 #
 BoxCrossesAntiMeridian            = La bo\u00eete englobante traverse l\u2019antim\u00e9ridien.
 ElementAlreadyInitialized_1       = Cet \u00e9l\u00e9ment de m\u00e9ta-donn\u00e9e est d\u00e9j\u00e0
initialis\u00e9 avec la valeur \u00ab\u202f{0}\u202f\u00bb.
+ElementsOmitted_1                 = \u2026 {0} \u00e9l\u00e9ments omis \u2026
 UnmodifiableMetadata              = Cette m\u00e9ta-donn\u00e9e n\u2019est pas modifiable.
diff --git a/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/FormattableObject.java
b/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/FormattableObject.java
index 0914ad1..36fca3a 100644
--- a/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/FormattableObject.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/FormattableObject.java
@@ -189,7 +189,8 @@ public abstract class FormattableObject {
         formatter.configure(convention, null, colorize ? Colors.DEFAULT : null,
                 convention.toUpperCase           ? (byte) +1 : 0,
                 (convention.majorVersion() == 1) ? (byte) -1 : 0,
-                Constants.DEFAULT_INDENTATION);
+                Constants.DEFAULT_INDENTATION,
+                strict ? Integer.MAX_VALUE : 10);       // Arbitrary limit of 10 elements
in double[] parameter values.
         if (!strict) {
             formatter.transliterator = Transliterator.IDENTITY;
         }
diff --git a/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Formatter.java b/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Formatter.java
index e8c8ccb..2089d58 100644
--- a/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Formatter.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Formatter.java
@@ -76,6 +76,7 @@ import org.apache.sis.internal.util.StandardDateFormat;
 import org.apache.sis.internal.simple.SimpleExtent;
 import org.apache.sis.internal.metadata.WKTKeywords;
 import org.apache.sis.internal.metadata.ReferencingServices;
+import org.apache.sis.internal.metadata.Resources;
 import org.apache.sis.measure.UnitFormat;
 import org.apache.sis.measure.Range;
 import org.apache.sis.measure.MeasurementRange;
@@ -156,7 +157,7 @@ public class Formatter implements Localized {
      * If non-null, the terminal must be ANSI X3.64 compatible.
      * The default value is {@code null}.
      *
-     * @see #configure(Convention, Citation, Colors, byte, byte, byte)
+     * @see #configure(Convention, Citation, Colors, byte, byte, byte, int)
      */
     private Colors colors;
 
@@ -164,14 +165,14 @@ public class Formatter implements Localized {
      * The preferred convention for objects or parameter names.
      * This field should never be {@code null}.
      *
-     * @see #configure(Convention, Citation, Colors, byte, byte, byte)
+     * @see #configure(Convention, Citation, Colors, byte, byte, byte, int)
      */
     private Convention convention;
 
     /**
      * The preferred authority for objects or parameter names.
      *
-     * @see #configure(Convention, Citation, Colors, byte, byte, byte)
+     * @see #configure(Convention, Citation, Colors, byte, byte, byte, int)
      */
     private Citation authority;
 
@@ -257,7 +258,7 @@ public class Formatter implements Localized {
     /**
      * {@code 1} if keywords shall be converted to upper cases, or {@code -1} for lower cases.
      *
-     * @see #configure(Convention, Citation, Colors, byte, byte, byte)
+     * @see #configure(Convention, Citation, Colors, byte, byte, byte, int)
      */
     private byte toUpperCase;
 
@@ -267,6 +268,16 @@ public class Formatter implements Localized {
     private byte longKeywords;
 
     /**
+     * Maximum number of elements to show in lists, or {@link Integer#MAX_VALUE} if unlimited.
+     * If a list is longer than this length, only the first and the last elements will be
shown.
+     * This limit applies in particular to {@link MathTransform} parameter values of {@code
double[]}
+     * type, since those parameters may be large interpolation tables.
+     *
+     * @see #configure(Convention, Citation, Colors, byte, byte, byte, int)
+     */
+    private int listSizeLimit;
+
+    /**
      * Incremented when {@link #setColor(ElementKind)} is invoked, and decremented when {@link
#resetColor()}
      * is invoked. Used in order to prevent child elements to overwrite the colors decided
by enclosing elements.
      */
@@ -276,7 +287,7 @@ public class Formatter implements Localized {
      * The amount of spaces to use in indentation, or {@value org.apache.sis.io.wkt.WKTFormat#SINGLE_LINE}
      * if indentation is disabled.
      *
-     * @see #configure(Convention, Citation, Colors, byte, byte, byte)
+     * @see #configure(Convention, Citation, Colors, byte, byte, byte, int)
      */
     private byte indentation;
 
@@ -398,11 +409,11 @@ public class Formatter implements Localized {
      * @param  colors        the syntax coloring, or {@code null} if none.
      * @param  toUpperCase   whether keywords shall be converted to upper cases.
      * @param  longKeywords  {@code -1} for short keywords, {@code +1} for long keywords
or 0 for the default.
-     * @param  indentation   the amount of spaces to use in indentation for WKT formatting,
-     *                       or {@link WKTFormat#SINGLE_LINE}.
+     * @param  indentation   the amount of spaces to use in indentation for WKT formatting,
or {@link WKTFormat#SINGLE_LINE}.
+     * @param  listSizeLimit maximum number of elements to show in lists, or {@link Integer#MAX_VALUE}
if unlimited.
      */
     final void configure(Convention convention, final Citation authority, final Colors colors,
-            final byte toUpperCase, final byte longKeywords, final byte indentation)
+            final byte toUpperCase, final byte longKeywords, final byte indentation, final
int listSizeLimit)
     {
         this.convention     = convention;
         this.authority      = (authority != null) ? authority : convention.getNameAuthority();
@@ -410,6 +421,7 @@ public class Formatter implements Localized {
         this.toUpperCase    = toUpperCase;
         this.longKeywords   = longKeywords;
         this.indentation    = indentation;
+        this.listSizeLimit  = listSizeLimit;
         this.transliterator = (convention == Convention.INTERNAL) ? Transliterator.IDENTITY
: Transliterator.DEFAULT;
         unitFormat.setLocale(convention.usesCommonUnits ? Locale.US : Locale.ROOT);
     }
@@ -1469,15 +1481,7 @@ public class Formatter implements Localized {
      * @return {@code true} on success, or {@code false} if the given type is not recognized.
      */
     final boolean appendValue(final Object value) {
-        if (value.getClass().isArray()) {
-            appendSeparator();
-            elementStart = buffer.appendCodePoint(symbols.getOpenSequence()).length();
-            final int length = Array.getLength(value);
-            for (int i=0; i<length; i++) {
-                appendAny(Array.get(value, i));
-            }
-            buffer.appendCodePoint(symbols.getCloseSequence());
-        } else if (value instanceof Number) {
+        if (value instanceof Number) {
             final Number number = (Number) value;
             if (Numbers.isInteger(number.getClass())) {
                 append(number.longValue());
@@ -1493,6 +1497,35 @@ public class Formatter implements Localized {
         } else if (value instanceof CharSequence) {
             append((value instanceof InternationalString) ?
                     ((InternationalString) value).toString(locale) : value.toString(), null);
+        } else if (value.getClass().isArray()) {
+            /*
+             * All above cases delegated to another method which invoke 'appendSeparator()'.
+             * Since the following block is writing itself a new element, we need to invoke
+             * 'appendSeparator()' here. This block invokes (indirectly) this 'appendValue'
+             * method recursively for some or all elements in the list.
+             */
+            appendSeparator();
+            elementStart = buffer.appendCodePoint(symbols.getOpenSequence()).length();
+            final int length = Array.getLength(value);
+            final int cut = (length <= listSizeLimit) ? length : Math.max(listSizeLimit/2
- 1, 1);
+            for (int i=0; i<length; i++) {
+                if (i == cut) {
+                    /*
+                     * Skip elements in the middle if the list is too long. The 'cut' index
has been computed
+                     * in such a way that the number of elements to skip should be greater
than 1, otherwise
+                     * formatting the single missing element would often have been shorter.
+                     */
+                    final int skip = length - Math.min(2*cut, listSizeLimit);
+                    buffer.append(symbols.getSeparator());
+                    setColor(ElementKind.REMARKS);
+                    buffer.append(Resources.forLocale(locale).getString(Resources.Keys.ElementsOmitted_1,
skip));
+                    resetColor();
+                    i += skip;
+                    setInvalidWKT(value.getClass().getSimpleName(), null);
+                }
+                appendAny(Array.get(value, i));
+            }
+            buffer.appendCodePoint(symbols.getCloseSequence());
         } else {
             return false;
         }
diff --git a/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/WKTFormat.java b/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/WKTFormat.java
index 6dd751d..1e5f8f4 100644
--- a/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/WKTFormat.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/WKTFormat.java
@@ -183,6 +183,16 @@ public class WKTFormat extends CompoundFormat<Object> {
     private byte indentation;
 
     /**
+     * Maximum number of elements to show in lists, or {@link Integer#MAX_VALUE} if unlimited.
+     * If a list is longer than this length, only the first and the last elements will be
shown.
+     * This limit applies in particular to {@link org.opengis.referencing.operation.MathTransform}
+     * parameter values of {@code double[]} type, since those parameters may be large interpolation
tables.
+     *
+     * @see #getMaximumListElements()
+     */
+    private int listSizeLimit;
+
+    /**
      * WKT fragments that can be inserted in longer WKT strings, or {@code null} if none.
Keys are short identifiers
      * and values are WKT subtrees to substitute to the identifiers when they are found in
a WKT to parse.
      *
@@ -237,11 +247,12 @@ public class WKTFormat extends CompoundFormat<Object> {
      */
     public WKTFormat(final Locale locale, final TimeZone timezone) {
         super(locale, timezone);
-        convention   = Convention.DEFAULT;
-        symbols      = Symbols.getDefault();
-        keywordCase  = KeywordCase.DEFAULT;
-        keywordStyle = KeywordStyle.DEFAULT;
-        indentation  = Constants.DEFAULT_INDENTATION;
+        convention    = Convention.DEFAULT;
+        symbols       = Symbols.getDefault();
+        keywordCase   = KeywordCase.DEFAULT;
+        keywordStyle  = KeywordStyle.DEFAULT;
+        indentation   = Constants.DEFAULT_INDENTATION;
+        listSizeLimit = Integer.MAX_VALUE;
     }
 
     /**
@@ -516,7 +527,7 @@ public class WKTFormat extends CompoundFormat<Object> {
                 case LONG:  longKeywords = +1; break;
                 default:    longKeywords = (convention.majorVersion() == 1) ? (byte) -1 :
0; break;
             }
-            formatter.configure(convention, authority, colors, toUpperCase, longKeywords,
indentation);
+            formatter.configure(convention, authority, colors, toUpperCase, longKeywords,
indentation, listSizeLimit);
             if (transliterator != null) {
                 formatter.transliterator = transliterator;
             }
@@ -548,6 +559,34 @@ public class WKTFormat extends CompoundFormat<Object> {
     }
 
     /**
+     * Returns the maximum number of elements to show in lists of values. If a list length
is greater than this limit,
+     * then only the first and last elements will be shown together with a message saying
that some elements were omitted.
+     * This limit is useful in particular with {@link org.opengis.referencing.operation.MathTransform}
parameter values of
+     * {@code double[]} type, since those parameters may be large interpolation tables.
+     *
+     * @return the current lists size limit, or {@link Integer#MAX_VALUE} if unlimited.
+     *
+     * @since 1.0
+     */
+    public int getMaximumListElements() {
+        return listSizeLimit;
+    }
+
+    /**
+     * Sets a new limit for the number of elements to show in lists.
+     * If this method is never invoked, then the default is unlimited.
+     *
+     * @param  limit  the new lists size limit, or {@link Integer#MAX_VALUE} if unlimited.
+     *
+     * @since 1.0
+     */
+    public void setMaximumListElements(final int limit) {
+        ArgumentChecks.ensureStrictlyPositive("limit", limit);
+        listSizeLimit = limit;
+        updateFormatter(formatter);
+    }
+
+    /**
      * Verifies if the given type is a valid key for the {@link #factories} map.
      */
     private void ensureValidFactoryType(final Class<?> type) throws IllegalArgumentException
{
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/PassThroughTransform.java
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/PassThroughTransform.java
index f0020ae..225585a 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/PassThroughTransform.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/PassThroughTransform.java
@@ -858,9 +858,15 @@ public class PassThroughTransform extends AbstractMathTransform implements
Seria
         formatter.append(firstAffectedOrdinate);
         if (numTrailingOrdinates != 0) {
             formatter.append(numTrailingOrdinates);
-            formatter.setInvalidWKT(PassThroughTransform.class, null);
         }
         formatter.append(subTransform);
+        if (numTrailingOrdinates != 0) {
+            /*
+             * setInvalidWKT(…) shall be invoked only after we finished to format
+             * sub-transform, otherwise the wrong WKT element will be highlighted.
+             */
+            formatter.setInvalidWKT(PassThroughTransform.class, null);
+        }
         return WKTKeywords.PassThrough_MT;
     }
 }


Mime
View raw message