| /* |
| * 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.ignite.internal.configuration.hocon; |
| |
| import java.util.List; |
| import com.typesafe.config.ConfigValue; |
| import org.apache.ignite.internal.configuration.TypeUtils; |
| import org.apache.ignite.internal.configuration.tree.ConfigurationSource; |
| import org.apache.ignite.internal.configuration.tree.ConstructableTreeNode; |
| |
| import static com.typesafe.config.ConfigValueType.BOOLEAN; |
| import static com.typesafe.config.ConfigValueType.NUMBER; |
| import static com.typesafe.config.ConfigValueType.STRING; |
| import static java.lang.String.format; |
| import static org.apache.ignite.internal.configuration.util.ConfigurationUtil.join; |
| |
| /** |
| * {@link ConfigurationSource} created from a HOCON element representing a primitive type. |
| */ |
| class HoconPrimitiveConfigurationSource implements ConfigurationSource { |
| /** |
| * Current path inside the top-level HOCON object. |
| */ |
| private final List<String> path; |
| |
| /** |
| * HOCON object that this source has been created from. |
| */ |
| private final ConfigValue hoconCfgValue; |
| |
| /** |
| * Creates a {@link ConfigurationSource} from the given HOCON object representing a primitive type. |
| * |
| * @param path current path inside the top-level HOCON object. Can be empty if the given {@code hoconCfgValue} |
| * is the top-level object |
| * @param hoconCfgValue HOCON object |
| */ |
| HoconPrimitiveConfigurationSource(List<String> path, ConfigValue hoconCfgValue) { |
| assert !path.isEmpty(); |
| |
| this.path = path; |
| this.hoconCfgValue = hoconCfgValue; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public <T> T unwrap(Class<T> clazz) { |
| if (clazz.isArray()) |
| throw wrongTypeException(clazz, path, -1); |
| |
| return unwrapPrimitive(hoconCfgValue, clazz, path, -1); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public void descend(ConstructableTreeNode node) { |
| throw new IllegalArgumentException( |
| format("'%s' is expected to be a composite configuration node, not a single value", join(path)) |
| ); |
| } |
| |
| /** |
| * Returns exception with the message that a value is expected to be of a specific type. |
| * |
| * @param clazz Expected type of the value. |
| * @param path Path to the value. |
| * @param idx Index in the array if the value is an array element. {@code -1} if it's not. |
| * @return New {@link IllegalArgumentException} instance. |
| */ |
| public static IllegalArgumentException wrongTypeException(Class<?> clazz, List<String> path, int idx) { |
| return new IllegalArgumentException(format( |
| "'%s' is expected as a type for the '%s' configuration value", |
| unbox(clazz).getSimpleName(), formatArrayPath(path, idx) |
| )); |
| } |
| |
| /** |
| * Non-null wrapper over {@link TypeUtils#unboxed}. |
| * |
| * @param clazz Class for non-primitive objects. |
| * @return Unboxed class fox boxed classes, same object otherwise. |
| */ |
| private static Class<?> unbox(Class<?> clazz) { |
| assert !clazz.isPrimitive(); |
| |
| Class<?> unboxed = TypeUtils.unboxed(clazz); |
| |
| return unboxed == null ? clazz : unboxed; |
| } |
| |
| /** |
| * Extracts the value from the given {@link ConfigValue} based on the expected type. |
| * |
| * @param hoconCfgValue Configuration value to unwrap. |
| * @param clazz Class that signifies resulting type of the value. Boxed primitive or String. |
| * @param path Path to the value, used for error messages. |
| * @param idx Index in the array if the value is an array element. {@code -1} if it's not. |
| * @param <T> Type of the resulting unwrapped object. |
| * @return Unwrapped object. |
| * @throws IllegalArgumentException In case of type mismatch or numeric overflow. |
| */ |
| public static <T> T unwrapPrimitive(ConfigValue hoconCfgValue, Class<T> clazz, List<String> path, int idx) { |
| assert !clazz.isArray(); |
| assert !clazz.isPrimitive(); |
| |
| if (clazz == String.class) { |
| if (hoconCfgValue.valueType() != STRING) |
| throw wrongTypeException(clazz, path, idx); |
| |
| return clazz.cast(hoconCfgValue.unwrapped()); |
| } |
| else if (clazz == Boolean.class) { |
| if (hoconCfgValue.valueType() != BOOLEAN) |
| throw wrongTypeException(clazz, path, idx); |
| |
| return clazz.cast(hoconCfgValue.unwrapped()); |
| } |
| else if (clazz == Character.class) { |
| if (hoconCfgValue.valueType() != STRING || hoconCfgValue.unwrapped().toString().length() != 1) |
| throw wrongTypeException(clazz, path, idx); |
| |
| return clazz.cast(hoconCfgValue.unwrapped().toString().charAt(0)); |
| } |
| else if (Number.class.isAssignableFrom(clazz)) { |
| if (hoconCfgValue.valueType() != NUMBER) |
| throw wrongTypeException(clazz, path, idx); |
| |
| Number numberValue = (Number)hoconCfgValue.unwrapped(); |
| |
| if (clazz == Byte.class) { |
| checkBounds(numberValue, Byte.MIN_VALUE, Byte.MAX_VALUE, path, idx); |
| |
| return clazz.cast(numberValue.byteValue()); |
| } |
| else if (clazz == Short.class) { |
| checkBounds(numberValue, Short.MIN_VALUE, Short.MAX_VALUE, path, idx); |
| |
| return clazz.cast(numberValue.shortValue()); |
| } |
| else if (clazz == Integer.class) { |
| checkBounds(numberValue, Integer.MIN_VALUE, Integer.MAX_VALUE, path, idx); |
| |
| return clazz.cast(numberValue.intValue()); |
| } |
| else if (clazz == Long.class) |
| return clazz.cast(numberValue.longValue()); |
| else if (clazz == Float.class) |
| return clazz.cast(numberValue.floatValue()); |
| else if (clazz == Double.class) |
| return clazz.cast(numberValue.doubleValue()); |
| } |
| |
| throw new IllegalArgumentException("Unsupported type: " + clazz); |
| } |
| |
| /** |
| * Checks that a number fits into the given bounds. |
| * |
| * @param numberValue Number value to validate. |
| * @param lower Lower bound, inclusive. |
| * @param upper Upper bound, inclusive. |
| * @param path Path to the value, used for error messages. |
| * @param idx Index in the array if the value is an array element. {@code -1} if it's not. |
| */ |
| private static void checkBounds(Number numberValue, long lower, long upper, List<String> path, int idx) { |
| long longValue = numberValue.longValue(); |
| |
| if (longValue < lower || longValue > upper) { |
| throw new IllegalArgumentException(format( |
| "Value '%d' of '%s' is out of its declared bounds: [%d : %d]", |
| longValue, formatArrayPath(path, idx), lower, upper |
| )); |
| } |
| } |
| |
| /** |
| * Creates a string representation of the current HOCON path inside of an array. |
| * |
| * @param path Path to the value. |
| * @param idx Index in the array if the value is an array element. {@code -1} if it's not. |
| * @return Path in a proper format. |
| */ |
| public static String formatArrayPath(List<String> path, int idx) { |
| return join(path) + (idx == -1 ? "" : ("[" + idx + "]")); |
| } |
| } |