Package org.jboss.arquillian.drone.configuration

Source Code of org.jboss.arquillian.drone.configuration.ConfigurationMapper

/*
* JBoss, Home of Professional Open Source
* Copyright 2011, Red Hat Middleware LLC, and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* Licensed 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.jboss.arquillian.drone.configuration;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.jboss.arquillian.config.descriptor.api.ArquillianDescriptor;
import org.jboss.arquillian.config.descriptor.api.ExtensionDef;
import org.jboss.arquillian.core.spi.Validate;
import org.jboss.arquillian.drone.configuration.legacy.LegacyPropertyToCapabilityMapper;
import org.jboss.arquillian.drone.configuration.mapping.BooleanValueMapper;
import org.jboss.arquillian.drone.configuration.mapping.DoubleValueMapper;
import org.jboss.arquillian.drone.configuration.mapping.IntegerValueMapper;
import org.jboss.arquillian.drone.configuration.mapping.LongValueMapper;
import org.jboss.arquillian.drone.configuration.mapping.StringValueMapper;
import org.jboss.arquillian.drone.configuration.mapping.URIValueMapper;
import org.jboss.arquillian.drone.configuration.mapping.URLValueMapper;
import org.jboss.arquillian.drone.configuration.mapping.ValueMapper;
import org.jboss.arquillian.drone.spi.DroneConfiguration;

/**
* Utility which maps Arquillian Descriptor and System Properties to a Drone configuration.
*
* Configuration mapper does inspect a configuration for available fields and it tries to fill the values according to what is
* provided in arquillian.xml or in system properties.
*
* All properties, which does not have an appropriate fields to be assigned, are stored in each available map, given that
* configuration provides a {@code Map<String,String>} fields. Properties using their name as a key.
*
* @author <a href="kpiwko@redhat.com>Karel Piwko</a>
* @see DroneConfiguration
*/
public class ConfigurationMapper {
    private static final Logger log = Logger.getLogger(ConfigurationMapper.class.getName());

    // FIXME this should not be a static helper class but a proper observer on ArquillianDescriptor
    private ConfigurationMapper() {
        throw new InstantiationError();
    }

    // FIXME this should be in SPI with a proper event model
    public static final List<ValueMapper<?>> VALUE_MAPPERS;
    static {
        VALUE_MAPPERS = new ArrayList<ValueMapper<?>>();
        VALUE_MAPPERS.add(BooleanValueMapper.INSTANCE);
        VALUE_MAPPERS.add(DoubleValueMapper.INSTANCE);
        VALUE_MAPPERS.add(IntegerValueMapper.INSTANCE);
        VALUE_MAPPERS.add(LongValueMapper.INSTANCE);
        VALUE_MAPPERS.add(StringValueMapper.INSTANCE);
        VALUE_MAPPERS.add(URIValueMapper.INSTANCE);
        VALUE_MAPPERS.add(URLValueMapper.INSTANCE);
    }

    /**
     * Maps a configuration using Arquillian Descriptor file
     *
     * @param <T> Type of the configuration
     * @param descriptor Arquillian Descriptor
     * @param configuration Configuration object
     * @param qualifier Qualifier annotation
     * @return Configured configuration
     */
    public static <T extends DroneConfiguration<T>> T fromArquillianDescriptor(ArquillianDescriptor descriptor,
            T configuration, Class<? extends Annotation> qualifier) {
        Validate.notNull(descriptor, "Descriptor must not be null");
        Validate.notNull(configuration, "Configuration object must not be null");
        Validate.notNull(qualifier, "Qualifier object must not be null");

        String descriptorQualifier = configuration.getConfigurationName();
        String qualifierName = qualifier.getSimpleName().toLowerCase();

        Map<String, String> nameValuePairs = loadNameValuePairs(descriptor, descriptorQualifier, qualifierName);

        return mapFromNameValuePairs(configuration, nameValuePairs);
    }

    /**
     * Maps a configuration using System Properties.
     *
     * This method is <b>deprecated</b>. Use Arquillian Core property format instead.
     *
     * @param <T> Type of the configuration
     * @param configuration Configuration object
     * @param qualifier Qualifier annotation
     * @return Configured configuration
     */
    @Deprecated
    public static <T extends DroneConfiguration<T>> T fromSystemConfiguration(T configuration,
            Class<? extends Annotation> qualifier) {
        Validate.notNull(configuration, "Configuration object must not be null");
        Validate.notNull(qualifier, "Qualifier object must not be null");

        String descriptorQualifier = configuration.getConfigurationName();
        String qualifierName = qualifier.getSimpleName().toLowerCase();

        Map<String, String> nameValuePairs = loadNameValuePairs(descriptorQualifier, qualifierName);

        return mapFromNameValuePairs(configuration, nameValuePairs);
    }

    /**
     * Maps configuration values from Arquillian Descriptor
     *
     * @param <T> A type of configuration
     * @param configuration Configuration object
     * @return Configured configuration of given type
     */
    // @SuppressWarnings("unchecked")
    static <T extends DroneConfiguration<T>> T mapFromNameValuePairs(T configuration, Map<String, String> nameValuePairs) {
        Map<String, Field> fields = SecurityActions.getAccessableFields(configuration.getClass());

        // extract all Map<String,String> in the configuration and initialize them
        List<Field> maps = SecurityActions.getMapFields(configuration.getClass(), String.class, String.class);
        for (Field mapField : maps) {
            try {
                // get or create a map
                @SuppressWarnings("unchecked")
                Map<String, String> map = (Map<String, String>) mapField.get(configuration);
                if (map == null) {
                    map = new HashMap<String, String>();
                }
                mapField.set(configuration, map);
            } catch (Exception e) {
                throw new RuntimeException("Could not map Drone configuration(" + configuration.getConfigurationName()
                        + ") for " + configuration.getClass().getName() + " from Arquillian Descriptor", e);
            }
        }

        // map basic fields
        for (Map.Entry<String, String> nameValue : nameValuePairs.entrySet()) {
            String name = nameValue.getKey();

            // map a field which has a field directly available in the configuration
            if (fields.containsKey(name)) {
                injectField(configuration, maps, fields, name, nameValue.getValue());
            }
            // map a field which comes from a system property which has a field available in the configuration
            else if (fields.containsKey(keyTransformReverse(name))) {
                // we prefer new format arquillian.mockdriver.intField over arquillian.mockdriver.int.field
                log.log(Level.WARNING,
                        "The system property \"{0}\" used in Arquillian \"{1}\" configuration is deprecated, please rather use new format \"{2}\"",
                        new Object[] { name, configuration.getConfigurationName(), keyTransformReverse(name) });
                injectField(configuration, maps, fields, keyTransformReverse(name), nameValue.getValue());
            }
            // map a field which does not have this luck into all available maps in configuration
            else {
                injectMapProperty(configuration, maps, fields, name, nameValue.getValue());
            }

        }

        return configuration;
    }

    /**
     * Parses Arquillian Descriptor into property name - value pairs value
     *
     * @param descriptor An Arquillian Descriptor
     * @param descriptorQualifier A qualifier used for extension configuration in the descriptor
     * @param qualifierName Name of the qualifier passed
     */
    static Map<String, String> loadNameValuePairs(ArquillianDescriptor descriptor, String descriptorQualifier,
            String qualifierName) {
        String fullDescriptorQualifier = new StringBuilder(descriptorQualifier).append("-").append(qualifierName).toString();

        ExtensionDef match = null;
        for (ExtensionDef extension : descriptor.getExtensions()) {
            if (fullDescriptorQualifier.equals(extension.getExtensionName())) {
                Map<String, String> nameValuePairs = extension.getExtensionProperties();
                if (log.isLoggable(Level.FINE)) {
                    log.fine("Using <extension qualifier=\"" + extension.getExtensionName() + "\"> for Drone Configuration");
                }
                return nameValuePairs;
            } else if (descriptorQualifier.equals(extension.getExtensionName())) {
                match = extension;
            }
        }

        // found generic only
        if (match != null) {
            Map<String, String> nameValuePairs = match.getExtensionProperties();
            if (log.isLoggable(Level.FINE)) {
                log.fine("Using <extension qualifier=\"" + match.getExtensionName() + "\"> for Drone Configuration");
            }
            return nameValuePairs;
        }

        return Collections.emptyMap();
    }

    /**
     * Parses System properties into property name - value pairs.
     *
     * This method is now deprecated and should not be used anymore. Arquillian Core contains possibility to load configuration
     * from System properties.
     *
     * @param descriptorQualifier A qualifier used for extension configuration in the descriptor
     * @param qualifierName Name of the qualifier passed
     */
    @Deprecated
    static Map<String, String> loadNameValuePairs(String descriptorQualifier, String qualifierName) {
        String fullQualifiedPrefix = new StringBuilder("arquillian.").append(descriptorQualifier).append(".")
                .append(qualifierName).append(".").toString();

        String qualifiedPrefix = new StringBuilder("arquillian.").append(descriptorQualifier).append(".").toString();

        // try to get fully qualified prefix properties first
        Map<String, String> candidates = SecurityActions.getProperties(fullQualifiedPrefix);
        if (candidates.isEmpty()) {
            candidates.putAll(SecurityActions.getProperties(qualifiedPrefix));
        }

        // properly rename
        Map<String, String> nameValuePairs = new HashMap<String, String>(candidates.size());
        for (Map.Entry<String, String> entry : candidates.entrySet()) {
            String name = entry.getKey();

            // make a nasty warning that this will be removed
            if (log.isLoggable(Level.WARNING)) {
                String propertyName = name.contains(fullQualifiedPrefix) ? name.substring(fullQualifiedPrefix.length()) : name
                        .substring(qualifiedPrefix.length());

                String newSysPropertyKey = new StringBuilder("arq.extension.")
                        .append(name.contains(fullQualifiedPrefix) ? (descriptorQualifier + "-" + qualifierName)
                                : descriptorQualifier).append(".").append(propertyName).toString();

                log.log(Level.WARNING, "Old system property format \"{0}\" is deprecated. You should use \"{1}\" instead.",
                        new Object[] { name, newSysPropertyKey });
            }
            // trim name
            name = name.contains(fullQualifiedPrefix) ? name.substring(fullQualifiedPrefix.length()) : name
                    .substring(qualifiedPrefix.length());
            nameValuePairs.put(name, entry.getValue());
        }

        return nameValuePairs;

    }

    /**
     * Maps a property key to a field name.
     *
     * Replaces dot ('.') and lower case character with an upper case character
     *
     * @param propertyName The name of field
     * @return Corresponding field name
     */
    static String keyTransformReverse(String propertyName) {
        StringBuilder sb = new StringBuilder();

        boolean upperCaseFlag = false;
        for (int i = 0; i < propertyName.length(); i++) {
            char c = propertyName.charAt(i);
            if (c == '.') {
                upperCaseFlag = true;
            } else if (upperCaseFlag && Character.isLowerCase(c)) {
                sb.append(Character.toUpperCase(c));
                upperCaseFlag = false;
            } else {
                sb.append(c);
            }
        }

        return sb.toString();
    }

    static <T extends DroneConfiguration<T>> Field injectField(T configuration, List<Field> maps, Map<String, Field> fields,
            String fieldName, String value) {
        try {
            Field f = fields.get(fieldName);
            if (f.getAnnotation(Deprecated.class) != null) {
                log.log(Level.WARNING, "The property \"{0}\" used in Arquillian \"{1}\" configuration is deprecated.",
                        new Object[] { f.getName(), configuration.getConfigurationName() });
            }

            // remap the property into capability if this is a legacy one
            if (LegacyPropertyToCapabilityMapper.isLegacy(fieldName)) {
                String capabilityName = LegacyPropertyToCapabilityMapper.remapKey(fieldName);
                String capabilityValue = LegacyPropertyToCapabilityMapper.remapValue(fieldName, value);
                injectMapProperty(configuration, maps, fields, capabilityName, capabilityValue);
            }

            f.set(configuration, convert(f.getType(), value));
            return f;
        } catch (Exception e) {
            throw new RuntimeException("Could not map Drone configuration(" + configuration.getConfigurationName() + ") for "
                    + configuration.getClass().getName() + " from Arquillian Descriptor", e);
        }

    }

    static <T extends DroneConfiguration<T>> void injectMapProperty(T configuration, List<Field> maps,
            Map<String, Field> fields, String propertyName, String value) {

        try {
            for (Field mapField : maps) {
                try {
                    // put property into a map
                    @SuppressWarnings("unchecked")
                    Map<String, String> map = (Map<String, String>) mapField.get(configuration);
                    map.put(propertyName, value);
                } catch (Exception e) {
                    throw new RuntimeException("Could not map Drone configuration(" + configuration.getConfigurationName()
                            + ") for " + configuration.getClass().getName() + " from Arquillian Descriptor", e);
                }
            }
        } catch (Exception e) {
            throw new RuntimeException("Could not map Drone configuration(" + configuration.getConfigurationName() + ") for "
                    + configuration.getClass().getName() + " from Arquillian Descriptor", e);
        }

    }

    /**
     * A helper converting method.
     *
     * Converts string to a class of given type
     *
     * @param <T> Type of returned value
     * @param clazz Type of desired value
     * @param value String value to be converted
     * @return Value converted to a appropriate type
     */
    static Object convert(Class<?> clazz, String value) {

        for (ValueMapper<?> mapper : VALUE_MAPPERS) {
            if (mapper.handles(clazz)) {
                return mapper.transform(value);
            }
        }

        throw new IllegalArgumentException("Unable to convert value " + value + "to a class: " + clazz.getName());
    }
}
TOP

Related Classes of org.jboss.arquillian.drone.configuration.ConfigurationMapper

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.