diff --git a/opc-ua-sdk/sdk-core/src/main/java/org/eclipse/milo/opcua/sdk/core/NumericRange.java b/opc-ua-sdk/sdk-core/src/main/java/org/eclipse/milo/opcua/sdk/core/NumericRange.java index 8f707f9faa..3c0ee4424f 100644 --- a/opc-ua-sdk/sdk-core/src/main/java/org/eclipse/milo/opcua/sdk/core/NumericRange.java +++ b/opc-ua-sdk/sdk-core/src/main/java/org/eclipse/milo/opcua/sdk/core/NumericRange.java @@ -221,30 +221,29 @@ private static Object writeToValueAtRange( if (dimension == dimensionCount) { if (current.getClass().isArray()) { - Class currentType = ArrayUtil.getType(current); - Class updateType = ArrayUtil.getType(update); - - if (currentType != updateType) { + if (ArrayUtil.getBoxedType(current) != ArrayUtil.getBoxedType(update)) { throw new UaException( StatusCodes.Bad_TypeMismatch, - String.format("currentType=%s, updateType=%s", current, update)); + String.format( + "current=%s, update=%s", + ArrayUtil.getBoxedType(current), ArrayUtil.getBoxedType(update))); } int length = Array.getLength(current); - Object copy = Array.newInstance(currentType, length); + Object copy = Array.newInstance(ArrayUtil.getType(current), length); if (low >= length || high >= length) { throw new UaException(StatusCodes.Bad_IndexRangeNoData); } for (int i = 0; i < length; i++) { + Object element; if (i < low || i > high) { - Object element = Array.get(current, i); - Array.set(copy, i, element); + element = Array.get(current, i); } else { - Object element = Array.get(update, i - low); - Array.set(copy, i, element); + element = Array.get(update, i - low); } + Array.set(copy, i, element); } return copy; diff --git a/opc-ua-sdk/sdk-server/src/test/java/org/eclipse/milo/opcua/sdk/core/NumericRangeTest.java b/opc-ua-sdk/sdk-server/src/test/java/org/eclipse/milo/opcua/sdk/core/NumericRangeTest.java index 660cd84e65..9f7b29e53e 100644 --- a/opc-ua-sdk/sdk-server/src/test/java/org/eclipse/milo/opcua/sdk/core/NumericRangeTest.java +++ b/opc-ua-sdk/sdk-server/src/test/java/org/eclipse/milo/opcua/sdk/core/NumericRangeTest.java @@ -233,6 +233,28 @@ public void testWriteByteString() throws UaException { assertEquals(new ByteString(new byte[] {0, 2, 4, 3}), updated); } + @Test + void writePrimitiveUpdateToBoxedCurrent() throws UaException { + Variant current = new Variant(new Integer[] {0, 1, 2, 3}); + Variant update = new Variant(new int[] {2, 4}); + NumericRange range = NumericRange.parse("1:2"); + + Object updated = NumericRange.writeToValueAtRange(current, update, range); + assertInstanceOf(Integer[].class, updated); + assertArrayEquals(new Integer[] {0, 2, 4, 3}, (Integer[]) updated); + } + + @Test + void writeBoxedUpdateToPrimitiveCurrent() throws UaException { + Variant current = new Variant(new int[] {0, 1, 2, 3}); + Variant update = new Variant(new Integer[] {2, 4}); + NumericRange range = NumericRange.parse("1:2"); + + Object updated = NumericRange.writeToValueAtRange(current, update, range); + assertInstanceOf(int[].class, updated); + assertArrayEquals(new int[] {0, 2, 4, 3}, (int[]) updated); + } + private static Object[][] getInvalidRanges() { return new Object[][] { {"0:0"}, {"1:1"}, {"-4:0"}, {"0:-4"}, {"3:1"}, {"abc,def"}, {"1:2,3:1"}, diff --git a/opc-ua-stack/stack-core/src/main/java/org/eclipse/milo/opcua/stack/core/util/ArrayUtil.java b/opc-ua-stack/stack-core/src/main/java/org/eclipse/milo/opcua/stack/core/util/ArrayUtil.java index fc1f4bceb9..088ee95faa 100644 --- a/opc-ua-stack/stack-core/src/main/java/org/eclipse/milo/opcua/stack/core/util/ArrayUtil.java +++ b/opc-ua-stack/stack-core/src/main/java/org/eclipse/milo/opcua/stack/core/util/ArrayUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 the Eclipse Milo Authors + * Copyright (c) 2025 the Eclipse Milo Authors * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -124,6 +124,12 @@ public static int getValueRank(Object array) { return rank == 0 ? -1 : rank; } + /** + * Get the base component type for the provided array. + * + * @param array the array whose base component type is to be determined. + * @return the {@code Class} object representing the base component type of the array. + */ public static Class getType(Object array) { Class type = array.getClass(); @@ -134,6 +140,27 @@ public static Class getType(Object array) { return type; } + /** + * Get the (boxed) base component type for the provided array. + * + * @param array the array whose (boxed) base component type is to be determined. + * @return the {@code Class} object representing the (boxed) base component type of the array. + */ + public static Class getBoxedType(Object array) { + Class type = getType(array); + + if (type == byte.class) return Byte.class; + if (type == short.class) return Short.class; + if (type == int.class) return Integer.class; + if (type == long.class) return Long.class; + if (type == float.class) return Float.class; + if (type == double.class) return Double.class; + if (type == char.class) return Character.class; + if (type == boolean.class) return Boolean.class; + + return type; + } + /** * If the provided Object is a primitive array, return a boxed array of the same type, otherwise * return the original Object. diff --git a/opc-ua-stack/stack-core/src/test/java/org/eclipse/milo/opcua/stack/core/util/ArrayUtilTest.java b/opc-ua-stack/stack-core/src/test/java/org/eclipse/milo/opcua/stack/core/util/ArrayUtilTest.java index fde3d407c5..75877040ed 100644 --- a/opc-ua-stack/stack-core/src/test/java/org/eclipse/milo/opcua/stack/core/util/ArrayUtilTest.java +++ b/opc-ua-stack/stack-core/src/test/java/org/eclipse/milo/opcua/stack/core/util/ArrayUtilTest.java @@ -95,6 +95,27 @@ public void testGetType(Object array, Class type) throws Exception { assertEquals(type, ArrayUtil.getType(array)); } + public static Object[][] getBoxedTypedArrays() { + return new Object[][] { + {new byte[1], Byte.class}, + {new short[1], Short.class}, + {new int[1], Integer.class}, + {new long[1], Long.class}, + {new float[1], Float.class}, + {new double[1], Double.class}, + {new char[1], Character.class}, + {new boolean[1], Boolean.class}, + {new Integer[1], Integer.class}, + {new String[1], String.class}, + }; + } + + @ParameterizedTest + @MethodSource("getBoxedTypedArrays") + public void testGetBoxedType(Object array, Class type) throws Exception { + assertEquals(type, ArrayUtil.getBoxedType(array)); + } + @Test void getValueRank() { assertEquals(-1, ArrayUtil.getValueRank(0));