diff --git a/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataCreateIndexService.java b/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataCreateIndexService.java index 0471ef1c09b..c19dcdd0ecb 100644 --- a/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataCreateIndexService.java +++ b/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataCreateIndexService.java @@ -474,12 +474,7 @@ public class MetaDataCreateIndexService extends AbstractComponent { } } - CollectionUtil.timSort(templateMetadata, new Comparator() { - @Override - public int compare(IndexTemplateMetaData o1, IndexTemplateMetaData o2) { - return o2.order() - o1.order(); - } - }); + CollectionUtil.timSort(templateMetadata, Comparator.comparingInt(IndexTemplateMetaData::order).reversed()); return templateMetadata; } diff --git a/core/src/main/java/org/elasticsearch/common/Numbers.java b/core/src/main/java/org/elasticsearch/common/Numbers.java index 52d0337ef73..1735a0dfa65 100644 --- a/core/src/main/java/org/elasticsearch/common/Numbers.java +++ b/core/src/main/java/org/elasticsearch/common/Numbers.java @@ -21,6 +21,9 @@ package org.elasticsearch.common; import org.apache.lucene.util.BytesRef; +import java.math.BigDecimal; +import java.math.BigInteger; + /** * A set of utilities for numbers. */ @@ -178,4 +181,56 @@ public final class Numbers { } return true; } + + /** Return the long that {@code n} stores, or throws an exception if the + * stored value cannot be converted to a long that stores the exact same + * value. */ + public static long toLongExact(Number n) { + if (n instanceof Byte || n instanceof Short || n instanceof Integer + || n instanceof Long) { + return n.longValue(); + } else if (n instanceof Float || n instanceof Double) { + double d = n.doubleValue(); + if (d != Math.round(d)) { + throw new IllegalArgumentException(n + " is not an integer value"); + } + return n.longValue(); + } else if (n instanceof BigDecimal) { + return ((BigDecimal) n).toBigIntegerExact().longValueExact(); + } else if (n instanceof BigInteger) { + return ((BigInteger) n).longValueExact(); + } else { + throw new IllegalArgumentException("Cannot check whether [" + n + "] of class [" + n.getClass().getName() + + "] is actually a long"); + } + } + + /** Return the int that {@code n} stores, or throws an exception if the + * stored value cannot be converted to an int that stores the exact same + * value. */ + public static int toIntExact(Number n) { + return Math.toIntExact(toLongExact(n)); + } + + /** Return the short that {@code n} stores, or throws an exception if the + * stored value cannot be converted to a short that stores the exact same + * value. */ + public static short toShortExact(Number n) { + long l = toLongExact(n); + if (l != (short) l) { + throw new ArithmeticException("short overflow: " + l); + } + return (short) l; + } + + /** Return the byte that {@code n} stores, or throws an exception if the + * stored value cannot be converted to a byte that stores the exact same + * value. */ + public static byte toByteExact(Number n) { + long l = toLongExact(n); + if (l != (byte) l) { + throw new ArithmeticException("byte overflow: " + l); + } + return (byte) l; + } } diff --git a/core/src/main/java/org/elasticsearch/common/xcontent/support/XContentMapValues.java b/core/src/main/java/org/elasticsearch/common/xcontent/support/XContentMapValues.java index a94bf63e270..a1affb4fe57 100644 --- a/core/src/main/java/org/elasticsearch/common/xcontent/support/XContentMapValues.java +++ b/core/src/main/java/org/elasticsearch/common/xcontent/support/XContentMapValues.java @@ -24,6 +24,7 @@ import org.apache.lucene.util.automaton.Automaton; import org.apache.lucene.util.automaton.CharacterRunAutomaton; import org.apache.lucene.util.automaton.Operations; import org.elasticsearch.ElasticsearchParseException; +import org.elasticsearch.common.Numbers; import org.elasticsearch.common.Strings; import org.elasticsearch.common.regex.Regex; import org.elasticsearch.common.unit.TimeValue; @@ -357,7 +358,7 @@ public class XContentMapValues { public static int nodeIntegerValue(Object node) { if (node instanceof Number) { - return ((Number) node).intValue(); + return Numbers.toIntExact((Number) node); } return Integer.parseInt(node.toString()); } @@ -366,10 +367,7 @@ public class XContentMapValues { if (node == null) { return defaultValue; } - if (node instanceof Number) { - return ((Number) node).intValue(); - } - return Integer.parseInt(node.toString()); + return nodeIntegerValue(node); } public static short nodeShortValue(Object node, short defaultValue) { @@ -381,7 +379,7 @@ public class XContentMapValues { public static short nodeShortValue(Object node) { if (node instanceof Number) { - return ((Number) node).shortValue(); + return Numbers.toShortExact((Number) node); } return Short.parseShort(node.toString()); } @@ -395,7 +393,7 @@ public class XContentMapValues { public static byte nodeByteValue(Object node) { if (node instanceof Number) { - return ((Number) node).byteValue(); + return Numbers.toByteExact((Number) node); } return Byte.parseByte(node.toString()); } @@ -409,7 +407,7 @@ public class XContentMapValues { public static long nodeLongValue(Object node) { if (node instanceof Number) { - return ((Number) node).longValue(); + return Numbers.toLongExact((Number) node); } return Long.parseLong(node.toString()); } diff --git a/core/src/test/java/org/elasticsearch/common/NumbersTests.java b/core/src/test/java/org/elasticsearch/common/NumbersTests.java new file mode 100644 index 00000000000..e5563993ad5 --- /dev/null +++ b/core/src/test/java/org/elasticsearch/common/NumbersTests.java @@ -0,0 +1,146 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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.elasticsearch.common; + +import org.elasticsearch.test.ESTestCase; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.concurrent.atomic.AtomicInteger; + +public class NumbersTests extends ESTestCase { + + public void testToLongExact() { + assertEquals(3L, Numbers.toLongExact(Long.valueOf(3L))); + assertEquals(3L, Numbers.toLongExact(Integer.valueOf(3))); + assertEquals(3L, Numbers.toLongExact(Short.valueOf((short) 3))); + assertEquals(3L, Numbers.toLongExact(Byte.valueOf((byte) 3))); + assertEquals(3L, Numbers.toLongExact(3d)); + assertEquals(3L, Numbers.toLongExact(3f)); + assertEquals(3L, Numbers.toLongExact(BigInteger.valueOf(3L))); + assertEquals(3L, Numbers.toLongExact(BigDecimal.valueOf(3L))); + + IllegalArgumentException e = expectThrows(IllegalArgumentException.class, + () -> Numbers.toLongExact(3.1d)); + assertEquals("3.1 is not an integer value", e.getMessage()); + e = expectThrows(IllegalArgumentException.class, + () -> Numbers.toLongExact(Double.NaN)); + assertEquals("NaN is not an integer value", e.getMessage()); + e = expectThrows(IllegalArgumentException.class, + () -> Numbers.toLongExact(Double.POSITIVE_INFINITY)); + assertEquals("Infinity is not an integer value", e.getMessage()); + e = expectThrows(IllegalArgumentException.class, + () -> Numbers.toLongExact(3.1f)); + assertEquals("3.1 is not an integer value", e.getMessage()); + e = expectThrows(IllegalArgumentException.class, + () -> Numbers.toLongExact(new AtomicInteger(3))); // not supported + assertEquals("Cannot check whether [3] of class [java.util.concurrent.atomic.AtomicInteger] is actually a long", e.getMessage()); + } + + public void testToIntExact() { + assertEquals(3L, Numbers.toIntExact(Long.valueOf(3L))); + assertEquals(3L, Numbers.toIntExact(Integer.valueOf(3))); + assertEquals(3L, Numbers.toIntExact(Short.valueOf((short) 3))); + assertEquals(3L, Numbers.toIntExact(Byte.valueOf((byte) 3))); + assertEquals(3L, Numbers.toIntExact(3d)); + assertEquals(3L, Numbers.toIntExact(3f)); + assertEquals(3L, Numbers.toIntExact(BigInteger.valueOf(3L))); + assertEquals(3L, Numbers.toIntExact(BigDecimal.valueOf(3L))); + + IllegalArgumentException e = expectThrows(IllegalArgumentException.class, + () -> Numbers.toIntExact(3.1d)); + assertEquals("3.1 is not an integer value", e.getMessage()); + e = expectThrows(IllegalArgumentException.class, + () -> Numbers.toLongExact(Double.NaN)); + assertEquals("NaN is not an integer value", e.getMessage()); + e = expectThrows(IllegalArgumentException.class, + () -> Numbers.toLongExact(Double.POSITIVE_INFINITY)); + assertEquals("Infinity is not an integer value", e.getMessage()); + e = expectThrows(IllegalArgumentException.class, + () -> Numbers.toIntExact(3.1f)); + assertEquals("3.1 is not an integer value", e.getMessage()); + ArithmeticException ae = expectThrows(ArithmeticException.class, + () -> Numbers.toIntExact(1L << 40)); + assertEquals("integer overflow", ae.getMessage()); + e = expectThrows(IllegalArgumentException.class, + () -> Numbers.toIntExact(new AtomicInteger(3))); // not supported + assertEquals("Cannot check whether [3] of class [java.util.concurrent.atomic.AtomicInteger] is actually a long", e.getMessage()); + } + + public void testToShortExact() { + assertEquals(3L, Numbers.toShortExact(Long.valueOf(3L))); + assertEquals(3L, Numbers.toShortExact(Integer.valueOf(3))); + assertEquals(3L, Numbers.toShortExact(Short.valueOf((short) 3))); + assertEquals(3L, Numbers.toShortExact(Byte.valueOf((byte) 3))); + assertEquals(3L, Numbers.toShortExact(3d)); + assertEquals(3L, Numbers.toShortExact(3f)); + assertEquals(3L, Numbers.toShortExact(BigInteger.valueOf(3L))); + assertEquals(3L, Numbers.toShortExact(BigDecimal.valueOf(3L))); + + IllegalArgumentException e = expectThrows(IllegalArgumentException.class, + () -> Numbers.toShortExact(3.1d)); + assertEquals("3.1 is not an integer value", e.getMessage()); + e = expectThrows(IllegalArgumentException.class, + () -> Numbers.toLongExact(Double.NaN)); + assertEquals("NaN is not an integer value", e.getMessage()); + e = expectThrows(IllegalArgumentException.class, + () -> Numbers.toLongExact(Double.POSITIVE_INFINITY)); + assertEquals("Infinity is not an integer value", e.getMessage()); + e = expectThrows(IllegalArgumentException.class, + () -> Numbers.toShortExact(3.1f)); + assertEquals("3.1 is not an integer value", e.getMessage()); + ArithmeticException ae = expectThrows(ArithmeticException.class, + () -> Numbers.toShortExact(100000)); + assertEquals("short overflow: " + 100000, ae.getMessage()); + e = expectThrows(IllegalArgumentException.class, + () -> Numbers.toShortExact(new AtomicInteger(3))); // not supported + assertEquals("Cannot check whether [3] of class [java.util.concurrent.atomic.AtomicInteger] is actually a long", e.getMessage()); + } + + public void testToByteExact() { + assertEquals(3L, Numbers.toByteExact(Long.valueOf(3L))); + assertEquals(3L, Numbers.toByteExact(Integer.valueOf(3))); + assertEquals(3L, Numbers.toByteExact(Short.valueOf((short) 3))); + assertEquals(3L, Numbers.toByteExact(Byte.valueOf((byte) 3))); + assertEquals(3L, Numbers.toByteExact(3d)); + assertEquals(3L, Numbers.toByteExact(3f)); + assertEquals(3L, Numbers.toByteExact(BigInteger.valueOf(3L))); + assertEquals(3L, Numbers.toByteExact(BigDecimal.valueOf(3L))); + + IllegalArgumentException e = expectThrows(IllegalArgumentException.class, + () -> Numbers.toByteExact(3.1d)); + assertEquals("3.1 is not an integer value", e.getMessage()); + e = expectThrows(IllegalArgumentException.class, + () -> Numbers.toLongExact(Double.NaN)); + assertEquals("NaN is not an integer value", e.getMessage()); + e = expectThrows(IllegalArgumentException.class, + () -> Numbers.toLongExact(Double.POSITIVE_INFINITY)); + assertEquals("Infinity is not an integer value", e.getMessage()); + e = expectThrows(IllegalArgumentException.class, + () -> Numbers.toByteExact(3.1f)); + assertEquals("3.1 is not an integer value", e.getMessage()); + ArithmeticException ae = expectThrows(ArithmeticException.class, + () -> Numbers.toByteExact(300)); + assertEquals("byte overflow: " + 300, ae.getMessage()); + e = expectThrows(IllegalArgumentException.class, + () -> Numbers.toByteExact(new AtomicInteger(3))); // not supported + assertEquals("Cannot check whether [3] of class [java.util.concurrent.atomic.AtomicInteger] is actually a long", e.getMessage()); + } +}