| /* |
| * 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.sling.caconfig.spi.metadata; |
| |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.Set; |
| |
| import org.apache.commons.lang3.ClassUtils; |
| import org.jetbrains.annotations.NotNull; |
| import org.osgi.annotation.versioning.ProviderType; |
| |
| /** |
| * Defines a configuration property. |
| * @param <T> Property value type |
| */ |
| @ProviderType |
| public final class PropertyMetadata<T> extends AbstractMetadata<PropertyMetadata<T>> { |
| |
| // these are all types supported for fields of annotation classes (plus class which indicates nested configurations) |
| private static final Class<?>[] SUPPORTED_TYPES_ARRAY = { |
| String.class, |
| int.class, |
| long.class, |
| double.class, |
| boolean.class |
| }; |
| |
| /** |
| * Set with all types support for property metadata (not including nested configurations). |
| */ |
| public static final Set<Class<?>> SUPPORTED_TYPES = Collections.unmodifiableSet( |
| new HashSet<>(Arrays.asList(SUPPORTED_TYPES_ARRAY))); |
| |
| private final Class<T> type; |
| private T defaultValue; |
| private ConfigurationMetadata configurationMetadata; |
| private int order; |
| |
| /** |
| * @param name Property name |
| * @param type Property type |
| */ |
| @SuppressWarnings({ "unchecked", "null" }) |
| public PropertyMetadata(@NotNull String name, @NotNull Class<T> type) { |
| super(name); |
| Class<T> convertedType = (Class<T>)typeToPrimitive(type); |
| if (!isSupportedType(convertedType)) { |
| throw new IllegalArgumentException("Invalid type for property '" + name + "': " + type); |
| } |
| this.type = convertedType; |
| } |
| |
| /** |
| * @param name Property name |
| * @param defaultValue Default value (also defines property type) |
| */ |
| @SuppressWarnings("unchecked") |
| public PropertyMetadata(@NotNull String name, @NotNull T defaultValue) { |
| this(name, (Class<T>)defaultValue.getClass()); |
| this.defaultValue = defaultValue; |
| } |
| |
| private static Class<?> typeToPrimitive(Class<?> clazz) { |
| if (clazz.isArray()) { |
| if (ClassUtils.isPrimitiveWrapper(clazz.getComponentType())) { |
| if (clazz == Integer[].class) { |
| return int[].class; |
| } |
| if (clazz == Long[].class) { |
| return long[].class; |
| } |
| if (clazz == Double[].class) { |
| return double[].class; |
| } |
| if (clazz == Boolean[].class) { |
| return boolean[].class; |
| } |
| } |
| } |
| else if (ClassUtils.isPrimitiveWrapper(clazz)) { |
| if (clazz == Integer.class) { |
| return int.class; |
| } |
| if (clazz == Long.class) { |
| return long.class; |
| } |
| if (clazz == Double.class) { |
| return double.class; |
| } |
| if (clazz == Boolean.class) { |
| return boolean.class; |
| } |
| } |
| return clazz; |
| } |
| |
| private static boolean isSupportedType(Class<?> paramType) { |
| if (paramType.isArray()) { |
| return isSupportedType(paramType.getComponentType()); |
| } |
| for (Class<?> type : SUPPORTED_TYPES) { |
| if (type.equals(paramType)) { |
| return true; |
| } |
| } |
| if (paramType == ConfigurationMetadata.class) { |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * @return Parameter type |
| */ |
| public @NotNull Class<T> getType() { |
| return this.type; |
| } |
| |
| /** |
| * @return Default value if parameter is not set for configuration |
| */ |
| public T getDefaultValue() { |
| return this.defaultValue; |
| } |
| |
| /** |
| * @param value Default value if parameter is not set for configuration |
| * @return this; |
| */ |
| public PropertyMetadata<T> defaultValue(T value) { |
| this.defaultValue = value; |
| return this; |
| } |
| |
| /** |
| * @return Number to control property order in configuration editor. |
| */ |
| public int getOrder() { |
| return order; |
| } |
| |
| /** |
| * @param value Number to control property order in configuration editor. |
| * @return this |
| */ |
| public PropertyMetadata<T> order(int value) { |
| this.order = value; |
| return this; |
| } |
| |
| /** |
| * @return Metadata for nested configuration |
| */ |
| public ConfigurationMetadata getConfigurationMetadata() { |
| return configurationMetadata; |
| } |
| |
| /** |
| * @param configurationMetadata Metadata for nested configuration |
| * @return this; |
| */ |
| public PropertyMetadata<T> configurationMetadata(ConfigurationMetadata configurationMetadata) { |
| this.configurationMetadata = configurationMetadata; |
| return this; |
| } |
| |
| /** |
| * @return true if this property describes a nested configuration. |
| * In this case it is ensured configuration metadata is present, and the type is ConfigurationMetadata or ConfigurationMetadata[]. |
| */ |
| public boolean isNestedConfiguration() { |
| return configurationMetadata != null |
| && (this.type.equals(ConfigurationMetadata.class) || this.type.equals(ConfigurationMetadata[].class)); |
| } |
| |
| @Override |
| public String toString() { |
| return getName() + "[" + this.type.getSimpleName() + "]"; |
| } |
| |
| } |