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 8ae43dd0ce149da6e0d598dc9e79b5d8de699230 Author: Martin Desruisseaux <[email protected]> AuthorDate: Sun Mar 21 21:26:54 2021 +0100 Fix an `ArrayIndexoutOfBoundsException` in `WKTUtilities.cornersAndCenter(…)` when the grid size is smaller than `2*cornerSize + 1`. --- .../sis/internal/referencing/WKTUtilities.java | 28 ++++++++++++++++++---- .../main/java/org/apache/sis/io/wkt/Formatter.java | 20 ++++++++-------- 2 files changed, 33 insertions(+), 15 deletions(-) diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/WKTUtilities.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/WKTUtilities.java index 89ac500..5a8e0ce 100644 --- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/WKTUtilities.java +++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/WKTUtilities.java @@ -432,7 +432,13 @@ public final class WKTUtilities extends Static { * Returns the values in the corners and in the center of the given tensor. The values are returned in a * <var>n</var>-dimensional array of {@link Number} where <var>n</var> is the length of {@code size}. * If some values have been skipped, {@code null} values are inserted in the rows or columns where the - * skipping occurs. + * skipping occurs. Caller may replace null values by {@code "…"} string at formatting time for example. + * + * <p>Indices of elements in the returned array are in reverse order than in the {@code size} argument. + * For example if {@code size} contains the values for dimensions (x,y,z) in that order, then elements + * in the returned array are accessed with {@code cornersAndCenter[z][y][x]} indices in that order. + * It is done that way because in the common case where there is only two dimensions, + * {@code cornersAndCenter[y]} is a row. This is what WKT formatter expects among others.</p> * * @param tensor function providing values of the tensor. Inputs are indices of the desired value with * index in each dimension ranging from 0 inclusive to {@code size[dimension]} exclusive. @@ -444,7 +450,7 @@ public final class WKTUtilities extends Static { */ public static Object[] cornersAndCenter(final Function<int[],Number> tensor, final int[] size, final int cornerSize) { /* - * The 'source' array will contain indices of values to fetch in the tensor, and the 'target' array will contain + * The `source` array will contain indices of values to fetch in the tensor, and the `target` array will contain * indices where to store those values in the returned data structure. Other arrays contain threshold indices of * points of interest in the target data structure. */ @@ -459,11 +465,18 @@ public final class WKTUtilities extends Static { } final int[] source = new int[shown.length]; final int[] target = new int[shown.length]; - final Object[] numbers = (Object[]) Array.newInstance(Number.class, shown); + final Object[] numbers; + { + final int[] reversed = new int[shown.length]; + for (int i=0; i<reversed.length;) { + reversed[i] = shown[shown.length - ++i]; + } + numbers = (Object[]) Array.newInstance(Number.class, reversed); + } /* * The loops below are used for simulating GOTO statements. This is usually a deprecated practice, * but in this case we can hardly use normal loops because the number of nested loops is dynamic. - * We want something equivalent to the code below where 'n' - the number of nested loops - is not + * We want something equivalent to the code below where `n` - the number of nested loops - is not * known at compile-time: * * for (int i0=0; i0<size[0]; i0++) { @@ -516,10 +529,15 @@ fill: for (;;) { Object walk = numbers; Object[] previous = null; for (int d=size.length; --d >= 0;) { + final int p = empty[d]; previous = (Object[]) walk; - walk = previous[empty[d]]; + if (p >= previous.length) { + return numbers; + } + walk = previous[p]; source[d] = size[d] / 2; } + assert walk == previous[empty[0]]; if (walk == null) { previous[empty[0]] = tensor.apply(source); } diff --git a/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/Formatter.java b/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/Formatter.java index 11a1d0c..ef162d1 100644 --- a/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/Formatter.java +++ b/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/Formatter.java @@ -525,7 +525,7 @@ public class Formatter implements Localized { if (colorApplied == 0) { final String color = colors.getAnsiSequence(type); if (color == null) { - // Do not increment 'colorApplied' for giving a chance to children to apply their colors. + // Do not increment `colorApplied` for giving a chance to children to apply their colors. return; } final boolean isStart = (buffer.length() == elementStart); @@ -1195,7 +1195,7 @@ public class Formatter implements Localized { public void append(final long number) { appendSeparator(); /* - * The check for 'isComplement' is a hack for ImmutableIdentifier.formatTo(Formatter). + * The check for `isComplement` is a hack for ImmutableIdentifier.formatTo(Formatter). * We do not have a public API for controlling the integer colors (it may not be desirable). */ setColor(isComplement ? ElementKind.IDENTIFIER : ElementKind.INTEGER); @@ -1215,12 +1215,12 @@ public class Formatter implements Localized { setColor(ElementKind.NUMBER); /* * Use scientific notation if the number magnitude is too high or too low. The threshold values used here - * may be different than the threshold values used in the standard 'StringBuilder.append(double)' method. + * may be different than the threshold values used in the standard `StringBuilder.append(double)` method. * In particular, we use a higher threshold for large numbers because ellipsoid axis lengths are above the * JDK threshold when the axis length is given in feet (about 2.1E+7) while we still want to format them * as usual numbers. * - * Note that we perform this special formatting only if the 'NumberFormat' is not localized + * Note that we perform this special formatting only if the `NumberFormat` is not localized * (which is the usual case). */ if (symbols.useScientificNotation(Math.abs(number))) { @@ -1290,7 +1290,7 @@ public class Formatter implements Localized { marginBeforeRow = ""; } /* - * 'formattedNumberMarks' contains, for each number in each row, positions in the 'buffer' where + * `formattedNumberMarks` contains, for each number in each row, positions in the `buffer` where * the number starts and position where it ends. Those positions are stored as (start,end) pairs. * We compute those marks unconditionally for simplicity, but will ignore them if formatting on * a single line. @@ -1530,9 +1530,9 @@ public class Formatter implements Localized { ((InternationalString) value).toString(locale) : value.toString(), null); } else if (value.getClass().isArray()) { /* - * All above cases delegated to another method which invoke 'appendSeparator()'. + * 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' + * `appendSeparator()` here. This block invokes (indirectly) this `appendValue` * method recursively for some or all elements in the list. */ appendSeparator(); @@ -1542,7 +1542,7 @@ public class Formatter implements Localized { 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 + * 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. */ @@ -1710,7 +1710,7 @@ public class Formatter implements Localized { * The unit that we replaced was not the expected one. Probably the user has invoked * addContextualUnit(…) again without a matching call to restoreContextualUnit(…). * Note that this case should never happen in Convention.WKT1_COMMON_UNITS mode, - * since 'previous' should never be non-null in that mode (if the user followed + * since `previous` should never be non-null in that mode (if the user followed * the documented pattern). */ throw new IllegalStateException(); @@ -1752,7 +1752,7 @@ public class Formatter implements Localized { return (warnings != null) || (buffer != null && buffer.length() == 0); /* * Note: we really use a "and" condition (not an other "or") for the buffer test because - * the buffer is reset to 'null' by WKTFormat after a successfull formatting. + * the buffer is reset to `null` by WKTFormat after a successfull formatting. */ }
