blob: 7705e85f5bf4a29106e85e1529478ca21ba1174f [file] [log] [blame]
/*
* 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
*
* https://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.ant.s3;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.time.Duration;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.lang3.reflect.TypeUtils;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.utils.AttributeMap;
/**
* Static utility class for conversions from {@link String} to needed types.
*/
class StringConversions {
private static final TypeVariable<?> ATTRIBUTE_KEY_TYPE = AttributeMap.Key.class.getTypeParameters()[0];
private static final Map<Class<?>, Function<String, ?>> CONVERTERS;
private static final Map<Class<?>, Class<?>> PRIMITIVE_TO_WRAPPER =
Stream
.of(Byte.class, Short.class, Integer.class, Character.class, Long.class, Float.class, Double.class,
Boolean.class)
.collect(Collectors.collectingAndThen(Collectors.<Class<?>, Class<?>, Class<?>> toMap(t -> {
try {
return (Class<?>) t.getDeclaredField("TYPE").get(null);
} catch (IllegalAccessException | NoSuchFieldException e) {
throw new IllegalStateException(e);
}
}, Function.identity()), Collections::unmodifiableMap));
static {
final Map<Class<?>, Function<String, ?>> cnv = new LinkedHashMap<>();
cnv.put(Byte.class, Byte::valueOf);
cnv.put(Short.class, Short::valueOf);
cnv.put(Integer.class, Integer::valueOf);
cnv.put(Character.class, s -> s.charAt(0));
cnv.put(Long.class, Long::valueOf);
cnv.put(Float.class, Float::valueOf);
cnv.put(Double.class, Double::valueOf);
cnv.put(Boolean.class, Boolean::valueOf);
cnv.put(String.class, Function.identity());
cnv.put(Region.class, Region::of);
cnv.put(Duration.class, Duration::parse);
CONVERTERS = Collections.unmodifiableMap(cnv);
}
private static Field keyField(Class<? extends AttributeMap.Key<?>> keyType, String name) {
try {
final Field result = keyType.getDeclaredField(name.toUpperCase(Locale.US));
Exceptions.raiseUnless(Modifier.isStatic(result.getModifiers()) && result.getType().equals(keyType),
IllegalArgumentException::new,
() -> String.format("Illegal %s key: %s", keyType.getSimpleName(), name));
return result;
} catch (NoSuchFieldException | SecurityException e) {
throw new BuildException(e);
}
}
/**
* Convert {@code value} to {@code type}.
*
* @param type
* @param value
* @return T
*/
static <T> T as(Class<?> type, String value) {
if (type.isPrimitive()) {
type = PRIMITIVE_TO_WRAPPER.get(type);
}
final Optional<Object> converted = Optional.of(type).map(CONVERTERS::get).map(fn -> fn.apply(value));
if (converted.isPresent()) {
@SuppressWarnings("unchecked")
final T result = (T) converted.get();
return result;
}
if (type.isEnum()) {
try {
@SuppressWarnings({ "unchecked", "rawtypes" })
final T result = (T) Enum.valueOf((Class) type, value);
return result;
} catch (IllegalArgumentException e) {
}
@SuppressWarnings({ "unchecked", "rawtypes" })
final T result = (T) Enum.valueOf((Class) type, value.toUpperCase(Locale.US));
return result;
}
// Ant conventions
Constructor<T> ctor;
try {
@SuppressWarnings("unchecked")
final Constructor<T> _ctor = (Constructor<T>) type.getDeclaredConstructor(Project.class, String.class);
ctor = _ctor;
} catch (NoSuchMethodException | SecurityException e) {
try {
@SuppressWarnings("unchecked")
final Constructor<T> _ctor = (Constructor<T>) type.getDeclaredConstructor(String.class);
ctor = _ctor;
} catch (NoSuchMethodException | SecurityException e2) {
ctor = null;
}
}
if (ctor != null) {
try {
return ctor.newInstance(value);
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new IllegalStateException(e);
}
}
throw new IllegalArgumentException();
}
/**
* Generate an AWS SDK {@link AttributeMap} from the specified
* {@link String} {@link Map} for the specified key type.
*
* @param <K>
* @param keyType
* @param m
* @return {@link AttributeMap}
*/
static <K extends AttributeMap.Key<?>> AttributeMap attributes(Class<K> keyType, Map<String, String> m) {
final AttributeMap.Builder b = AttributeMap.builder();
m.forEach((k, v) -> {
final Field keyField = keyField(keyType, k);
final AttributeMap.Key<Object> key;
try {
@SuppressWarnings("unchecked")
final AttributeMap.Key<Object> _key = (AttributeMap.Key<Object>) keyField.get(null);
key = _key;
} catch (IllegalArgumentException | IllegalAccessException e) {
throw new BuildException(e);
}
final Type valueType =
TypeUtils.getTypeArguments(keyField.getGenericType(), AttributeMap.Key.class).get(ATTRIBUTE_KEY_TYPE);
final Object value = as(TypeUtils.getRawType(valueType, null), v);
b.<Object> put(key, value);
});
return b.build();
}
}