/* * 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.jcr.base.util; import org.osgi.service.cm.ConfigurationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Dictionary; import java.util.Enumeration; import java.util.HashMap; import java.util.Hashtable; import java.util.Map; /** TODO move this to a testing utilities module? */ public class ConfigAnnotationUtil { private static final Logger LOG = LoggerFactory.getLogger(ConfigAnnotationUtil.class); @SuppressWarnings("unchecked") public static <T> T fromDictionary(final Class<T> clazz, final Dictionary<String, ?> properties) throws ConfigurationException { return (T)Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{ clazz }, new Handler(copyAndVerify(properties, clazz))); } private static class Handler implements InvocationHandler { private Dictionary<String, ?> properties; private Handler(final Dictionary<String, ?> properties) { this.properties = properties; } @Override public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { final String methodName = method.getName(); if ("toString".equals(methodName)) { return proxy.getClass().getName() + "[" + properties.toString() + "]"; } final String propertyName = toPropertyName(methodName); Object value; if (properties != null && (value = properties.get(propertyName)) != null) { return value; } else { return method.getDefaultValue(); } } } private static String toPropertyName(final String methodName) { return methodName.replace('_', '.'); } private static String toMethodName(final String propertyName) { return propertyName.replace('.', '_'); } private static <T> Dictionary<String, ?> copyAndVerify(final Dictionary<String, ?> properties, final Class<T> clazz) throws ConfigurationException { if (properties == null) { return null; } final Hashtable<String, Object> copy = new Hashtable<>(); final Enumeration<String> keys = properties.keys(); while (keys.hasMoreElements()) { final String propertyName = keys.nextElement(); final Object value = properties.get(propertyName); verifyValueType(clazz, propertyName, value); copy.put(propertyName, value); } return copy; } private static <T> void verifyValueType(final Class<T> clazz, final String propertyName, final Object value) throws ConfigurationException { final Method method = getDeclaredMethodByName(clazz, toMethodName(propertyName)); if (method != null) { final Class<?> returnType = method.getReturnType(); final Class<?> valueClass = value.getClass(); if (!isAssignable(returnType, valueClass)) { LOG.error("Invalid value type for {} ({} instead of {})", propertyName, valueClass, returnType); throw new ConfigurationException(propertyName, "Value of incorrect type " + valueClass.getName() + " instead of " + returnType.getName()); } } } private static boolean isAssignable(final Class<?> type, final Class<?> superType) { final Class<?> wrappedType = primitiveToWrapper(type); final Class<?> wrappedSuperType = primitiveToWrapper(superType); return wrappedType.isAssignableFrom(wrappedSuperType); } private static Method getDeclaredMethodByName(final Class<?> clazz, final String methodName) { final Method[] declaredMethods = clazz.getDeclaredMethods(); Method method = null; for (final Method declaredMethod : declaredMethods) { if (declaredMethod.getName().equals(methodName)) { method = declaredMethod; break; } } return method; } private static Class<?> primitiveToWrapper(Class<?> clazz) { if (primitiveToWrapper.containsKey(clazz)) { return primitiveToWrapper.get(clazz); } else { return clazz; } } private static final Map<Class<?>, Class<?>> primitiveToWrapper = new HashMap<Class<?>, Class<?>>(); static { primitiveToWrapper.put(Boolean.TYPE, Boolean.class); primitiveToWrapper.put(Byte.TYPE, Byte.class); primitiveToWrapper.put(Character.TYPE, Character.class); primitiveToWrapper.put(Short.TYPE, Short.class); primitiveToWrapper.put(Integer.TYPE, Integer.class); primitiveToWrapper.put(Long.TYPE, Long.class); primitiveToWrapper.put(Double.TYPE, Double.class); primitiveToWrapper.put(Float.TYPE, Float.class); } }