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 aa27c5dff7de00f3283e43138573caeca673ab60 Author: Martin Desruisseaux <martin.desruisse...@geomatys.com> AuthorDate: Wed Feb 26 15:19:36 2025 +0100 Add internal utility methods which will be needed for GeoHEIF: - For using arrays of long as keys in a hash map. - For aligning pixel coordinates to tiles border. --- .../org/apache/sis/storage/aggregate/Group.java | 22 +----- .../org/apache/sis/storage/base/ArrayOfLongs.java | 86 ++++++++++++++++++++++ .../main/org/apache/sis/util/privy/Numerics.java | 19 +++++ .../test/org/apache/sis/pending/jdk/JDK18Test.java | 53 +++++++++++++ .../org/apache/sis/util/privy/NumericsTest.java | 26 ++++--- 5 files changed, 174 insertions(+), 32 deletions(-) diff --git a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/aggregate/Group.java b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/aggregate/Group.java index 0e7c83f46b..e673244f99 100644 --- a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/aggregate/Group.java +++ b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/aggregate/Group.java @@ -17,13 +17,13 @@ package org.apache.sis.storage.aggregate; import java.util.List; -import java.util.Arrays; import java.util.ArrayList; import java.util.Map; import java.util.HashMap; import java.util.Locale; import java.util.stream.Stream; import org.apache.sis.util.privy.Strings; +import org.apache.sis.storage.base.ArrayOfLongs; import org.apache.sis.storage.event.StoreListeners; import org.apache.sis.coverage.grid.GridCoverageProcessor; @@ -137,25 +137,7 @@ abstract class Group<E> { * @return shared instance of the given array. */ final long[] unique(final long[] array) { - Object existing = sharedInstances.putIfAbsent(new Key(array), array); - return (existing != null) ? (long[]) existing : array; - } - - /** - * Workaround for the use of arrays as keys in a hash map. - */ - private static final class Key { - private final long[] array; - - Key(final long[] array) { - this.array = array; - } - @Override public boolean equals(final Object other) { - return (other instanceof Key) && Arrays.equals(array, ((Key) other).array); - } - @Override public int hashCode() { - return Arrays.hashCode(array); - } + return new ArrayOfLongs(array).unique(sharedInstances); } /** diff --git a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/ArrayOfLongs.java b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/ArrayOfLongs.java new file mode 100644 index 0000000000..a6478a59f9 --- /dev/null +++ b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/ArrayOfLongs.java @@ -0,0 +1,86 @@ +/* + * 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.storage.base; + +import java.util.Map; +import java.util.Arrays; + + +/** + * Workaround for the use of {@code long[]} arrays as keys in a hash map. + * + * @author Martin Desruisseaux (Geomatys) + */ +public final class ArrayOfLongs { + /** + * The array. + */ + private final long[] array; + + /** + * Creates a new key. + * + * @param array the array. + */ + public ArrayOfLongs(final long[] array) { + this.array = array; + } + + /** + * Returns a unique instance of the array wrapped by this key. + * + * @param sharedInstances a map containing shared instances of arrays. + * @return a shared instance of the array wrapped by this key. + * @throws ClassCastException if this key is associated in given map to an object other than a {@code long[]}. + */ + public long[] unique(final Map<? super ArrayOfLongs, ? super long[]> sharedInstances) { + Object existing = sharedInstances.putIfAbsent(this, array); + return (existing != null) ? (long[]) existing : array; + } + + /** + * Returns whether the given object is a key wrapping + * an array equals to the array wrapped by this key. + * + * @param other the other object to compare with this key. + * @return whether the two objects are wrapping equal arrays. + */ + @Override + public boolean equals(final Object other) { + return (other instanceof ArrayOfLongs) && Arrays.equals(array, ((ArrayOfLongs) other).array); + } + + /** + * Returns a hash code value for this key. + * + * @return the array hash code. + */ + @Override + public int hashCode() { + return Arrays.hashCode(array); + } + + /** + * Returns a string representation of the wrapped array. + * + * @return a string representation for debugging purposes. + */ + @Override + public String toString() { + return Arrays.toString(array); + } +} diff --git a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/privy/Numerics.java b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/privy/Numerics.java index 433b7d2455..bbd5cf0e39 100644 --- a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/privy/Numerics.java +++ b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/privy/Numerics.java @@ -209,6 +209,25 @@ public final class Numerics extends Static { return x == Math.rint(x); // `rint` is reported faster than `floor`. } + /** + * Makes the given value a multiple of the given divisor, rounding up. + * If the given value is already a multiple of the divisor, then it is returned as-is. + * Otherwise, this method returns the next multiple of the divisor which is greater than the given value. + * + * @param value the value which need to be a multiple of {@code divisor}. + * @param divisor the divisor. Cannot be zero. The sign is ignored (always handed as positive). + * @return the smallest multiple of {@code divisor} which is ≥ {@code value}. + */ + public static int snapToCeil(int value, final int divisor) { + final int r = value % divisor; // Always has the sign of `value`. + if (r > 0) { + value += Math.abs(divisor) - r; + } else { + value -= r; + } + return value; + } + /** * Returns x/y with the requirement that the division must be integer. * diff --git a/endorsed/src/org.apache.sis.util/test/org/apache/sis/pending/jdk/JDK18Test.java b/endorsed/src/org.apache.sis.util/test/org/apache/sis/pending/jdk/JDK18Test.java new file mode 100644 index 0000000000..33542335f2 --- /dev/null +++ b/endorsed/src/org.apache.sis.util/test/org/apache/sis/pending/jdk/JDK18Test.java @@ -0,0 +1,53 @@ +/* + * 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.pending.jdk; + +import static org.apache.sis.pending.jdk.JDK18.ceilDiv; + +// Test dependencies +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; +import org.apache.sis.test.TestCase; + + +/** + * Tests the {@link JDK18} class. + * + * @author Martin Desruisseaux (Geomatys) + */ +public final class JDK18Test extends TestCase { + /** + * Creates a new test case. + */ + public JDK18Test() { + } + + /** + * Tests {@link JDK18#ceilDiv(int, int)} and {@link JDK18#ceilDiv(long, long)}. + */ + @Test + public void testCeilDiv() { + assertEquals( 4, ceilDiv( 12, 3 )); + assertEquals( 4L, ceilDiv( 12L, 3L)); + assertEquals( 3, ceilDiv( 8, 3 )); + assertEquals( 3L, ceilDiv( 8L, 3L)); + assertEquals(-4, ceilDiv(-12, 3 )); + assertEquals(-4L, ceilDiv(-12L, 3L)); + assertEquals(-2, ceilDiv( -8, 3 )); + assertEquals(-2L, ceilDiv( -8L, 3L)); + } +} diff --git a/endorsed/src/org.apache.sis.util/test/org/apache/sis/util/privy/NumericsTest.java b/endorsed/src/org.apache.sis.util/test/org/apache/sis/util/privy/NumericsTest.java index e87ecd09c6..e912cd12ec 100644 --- a/endorsed/src/org.apache.sis.util/test/org/apache/sis/util/privy/NumericsTest.java +++ b/endorsed/src/org.apache.sis.util/test/org/apache/sis/util/privy/NumericsTest.java @@ -24,7 +24,6 @@ import static java.lang.Double.NEGATIVE_INFINITY; import org.apache.sis.math.MathFunctions; import org.apache.sis.util.ComparisonMode; import static org.apache.sis.util.privy.Numerics.*; -import static org.apache.sis.pending.jdk.JDK18.ceilDiv; import static org.apache.sis.pending.jdk.JDK19.FLOAT_PRECISION; import static org.apache.sis.pending.jdk.JDK19.DOUBLE_PRECISION; @@ -77,18 +76,21 @@ public final class NumericsTest extends TestCase { } /** - * Tests {@link Numerics#ceilDiv(int, int)} and {@link Numerics#ceilDiv(long, long)}. + * Tests {@link Numerics#snapToCeil(int, int)}. */ - @Test - public void testCeilDiv() { - assertEquals( 4, ceilDiv( 12, 3 )); - assertEquals( 4L, ceilDiv( 12L, 3L)); - assertEquals( 3, ceilDiv( 8, 3 )); - assertEquals( 3L, ceilDiv( 8L, 3L)); - assertEquals(-4, ceilDiv(-12, 3 )); - assertEquals(-4L, ceilDiv(-12L, 3L)); - assertEquals(-2, ceilDiv( -8, 3 )); - assertEquals(-2L, ceilDiv( -8L, 3L)); + public void testSnapToCeil() { + boolean negative = false; + do { // Executed exactly twice. + final int divisor = negative ? -3 : 3; + assertEquals( 9, Numerics.snapToCeil( 9, divisor)); + assertEquals( 12, Numerics.snapToCeil( 10, divisor)); + assertEquals( 12, Numerics.snapToCeil( 11, divisor)); + assertEquals( 12, Numerics.snapToCeil( 12, divisor)); + assertEquals( -9, Numerics.snapToCeil( -9, divisor)); + assertEquals( -9, Numerics.snapToCeil(-10, divisor)); + assertEquals( -9, Numerics.snapToCeil(-11, divisor)); + assertEquals(-12, Numerics.snapToCeil(-12, divisor)); + } while (negative = !negative); } /**