Package com.dtolabs.rundeck.core.plugins.configuration

Source Code of com.dtolabs.rundeck.core.plugins.configuration.PluginAdapterUtility$PropertyDefaultValues

/*
* Copyright 2012 DTO Labs, Inc. (http://dtolabs.com)
*
* 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.
*
*/

/*
* PluginAdapterUtility.java
*
* User: Greg Schueler <a href="mailto:greg@dtosolutions.com">greg@dtosolutions.com</a>
* Created: 12/11/12 9:08 AM
*
*/
package com.dtolabs.rundeck.core.plugins.configuration;

import java.lang.reflect.Field;
import java.util.*;

import com.dtolabs.rundeck.core.common.PropertyRetriever;
import com.dtolabs.rundeck.core.plugins.Plugin;
import com.dtolabs.rundeck.plugins.descriptions.*;
import com.dtolabs.rundeck.plugins.util.DescriptionBuilder;
import com.dtolabs.rundeck.plugins.util.PropertyBuilder;


/**
* Utility for creating {@link Description}s from Plugin class annotations and setting property values for annotated
* property fields.
*
* @author Greg Schueler <a href="mailto:greg@dtosolutions.com">greg@dtosolutions.com</a>
*/
public class PluginAdapterUtility {

    /**
     * Return true if the object has a valid Plugin annotation
     */
    public static boolean canBuildDescription(final Object object) {
        final Plugin annotation1 = object.getClass().getAnnotation(Plugin.class);
        return null != annotation1;
    }

    /**
     * Create a Description using a builder by analyzing the annotations on a plugin object, and including
     * annotations on fields as DescriptionProperties.
     *
     * @param object  the object
     * @param builder builder
     */
    public static Description buildDescription(final Object object, final DescriptionBuilder builder) {
        return buildDescription(object, builder, true);
    }

    /**
     * Create a Description using a builder by analyzing the annotations on a plugin object.
     *
     * @param object  the object
     * @param builder builder
     * @param includeAnnotatedFieldProperties
     *                if true, add DescriptionProperties to the Description based on annotations of fields in the class of the instance
     */
    public static Description buildDescription(final Object object, final DescriptionBuilder builder,
                                               final boolean includeAnnotatedFieldProperties) {
        //analyze this class to determine properties
        final Plugin annotation1 = object.getClass().getAnnotation(Plugin.class);
        if (null != annotation1) {
            final String pluginName = annotation1.name();
            builder
                    .name(pluginName)
                    .title(pluginName)
                    .description("");
        }

        final PluginDescription descAnnotation = object.getClass().getAnnotation(PluginDescription.class);
        if (null != descAnnotation) {
            if (!"".equals(descAnnotation.title())) {
                builder.title(descAnnotation.title());
            }
            if (!"".equals(descAnnotation.description())) {
                builder.description(descAnnotation.description());
            }
        }

        if (includeAnnotatedFieldProperties) {
            for (final Field field : collectFields(object)) {
                final PluginProperty annotation = field.getAnnotation(PluginProperty.class);
                if (null == annotation) {
                    continue;
                }
                final Property pbuild = propertyFromField(field, annotation);
                if (null == pbuild) {
                    continue;
                }
                builder.property(pbuild);
            }
        }
        builder.collaborate(object);
        return builder.build();
    }

    private static Field fieldForPropertyName(final String name, final Object object) {
        for (final Field field : collectFields(object)) {
            final PluginProperty annotation = field.getAnnotation(PluginProperty.class);
            if (null == annotation) {
                continue;
            }
            if (!"".equals(annotation.name()) && name.equals(annotation.name())) {
                return field;
            } else if ("".equals(annotation.name()) && name.equals(field.getName())) {
                return field;
            }
        }
        return null;
    }

    private static Collection<Field> collectFields(final Object object){
        ArrayList<Field> fields = new ArrayList<Field>();
        Class<?> clazz = object.getClass();
        do{
          fields.addAll(Arrays.asList(clazz.getDeclaredFields()));
          clazz = clazz.getSuperclass();
        }
        while(clazz != Object.class);
        return fields;
    }

    private static Property.Type propertyTypeFromFieldType(final Class clazz) {
        if (clazz == Integer.class || clazz == int.class) {
            return Property.Type.Integer;
        } else if (clazz == Long.class || clazz == long.class) {
            return Property.Type.Long;
        } else if (clazz == Boolean.class || clazz == boolean.class) {
            return Property.Type.Boolean;
        } else if (clazz == String.class) {
            return Property.Type.String;
        }
        return null;
    }

    private static Property propertyFromField(final Field field, final PluginProperty annotation) {
        final PropertyBuilder pbuild = PropertyBuilder.builder();
        //determine type
        final Property.Type type = propertyTypeFromFieldType(field.getType());
        if (null == type) {
            return null;
        }
        pbuild.type(type);
        if (type == Property.Type.String) {
            StringRenderingConstants.DisplayType renderBehaviour = StringRenderingConstants.DisplayType.SINGLE_LINE;
            //set select/freeselect
            final SelectValues selectAnnotation = field.getAnnotation(SelectValues.class);
            if (null != selectAnnotation) {
                pbuild.type(selectAnnotation.freeSelect() ? Property.Type.FreeSelect : Property.Type.Select);
                pbuild.values(selectAnnotation.values());
            }
           
            if (field.getAnnotation(TextArea.class) != null) {
                renderBehaviour = StringRenderingConstants.DisplayType.MULTI_LINE;
            }

            if (field.getAnnotation(Password.class) != null) {
                renderBehaviour = StringRenderingConstants.DisplayType.PASSWORD;
            }

            pbuild.renderingOption(StringRenderingConstants.DISPLAY_TYPE_KEY, renderBehaviour);
        }

        String name = annotation.name();
        if (null == name || "".equals(name)) {
            name = field.getName();
        }
        pbuild.name(name);

        if (null != annotation.title() && !"".equals(annotation.title())) {
            pbuild.title(annotation.title());
        } else {
            pbuild.title(name);
        }

        pbuild.description(annotation.description());

        if (null != annotation.defaultValue() && !"".equals(annotation.defaultValue())) {
            pbuild.defaultValue(annotation.defaultValue());
        }
        pbuild.required(annotation.required());

        pbuild.scope(annotation.scope());

        return pbuild.build();
    }

    private static final List<PropertyScope> instanceScopes = Arrays.asList(PropertyScope.Instance,
            PropertyScope.InstanceOnly);


    /**
     * Set field values on a plugin object by using annotated field values to create a Description, and setting field
     * values to resolved property values. Any resolved properties that are not mapped to a field will  be included in
     * the return result.
     *
     * @return Map of resolved properties that were not configured in the object's fields
     */
    public static Map<String, Object> configureProperties(final PropertyResolver resolver,
                                                          final Object object) {
        //use a default scope of InstanceOnly if the Property doesn't specify it
        return configureProperties(resolver, buildDescription(object, DescriptionBuilder.builder()), object,
                PropertyScope.InstanceOnly);
    }

    /**
     * Set field values on a plugin object by using a Description, and setting field values to resolved property values.
     * Any resolved properties that are not mapped to a field will  be included in the return result.
     *
     * @param resolver the property resolver
     * @param description the property descriptions
     * @param object the target object, which can implement {@link Configurable}, otherwise introspection will be used
     * @param defaultScope a default property scope to assume for unspecified properties
     *
     * @return Map of resolved properties that were not configured in the object's fields
     */
    public static Map<String, Object> configureProperties(final PropertyResolver resolver,
            final Description description,
            final Object object, PropertyScope defaultScope) {
        final Map<String, Object> inputConfig = mapDescribedProperties(resolver, description, defaultScope);
        if (object instanceof Configurable) {
            Configurable configObject = (Configurable) object;
            Properties configuration = new Properties();
            configuration.putAll(inputConfig);
            try {
                configObject.configure(configuration);
            } catch (ConfigurationException e) {

            }
        } else {
            for (final Property property : description.getProperties()) {
                if (null != inputConfig.get(property.getName())) {
                    if (setValueForProperty(property, inputConfig.get(property.getName()), object)) {
                        inputConfig.remove(property.getName());
                    }
                }
            }
        }
        return inputConfig;
    }

    private static class PropertyDefaultValues implements PropertyRetriever {
        private Map<String, String> properties;

        private PropertyDefaultValues(final List<Property> properties1) {
            properties = new HashMap<String, String>();
            for (final Property property : properties1) {
                if (null != property.getDefaultValue()) {
                    properties.put(property.getName(), property.getDefaultValue());
                }

            }
        }

        public String getProperty(final String name) {
            return properties.get(name);
        }

    }

    /**
     * Retrieve the Description's Properties mapped to resolved values given the resolver, using InsanceOnly default scope.
     *
     * @return All mapped properties by name and value.
     */
    public static Map<String, Object> mapDescribedProperties(final PropertyResolver resolver,
                                                             final Description description) {
        //use a default scope of InstanceOnly if the Property doesn't specify it
        //use property default value if otherwise not resolved
        return mapDescribedProperties(resolver, description, null);
    }

    /**
     * Retrieve the Description's Properties mapped to resolved values given the resolver, with a default property
     * scope
     *
     * @return All mapped properties by name and value.
     */
    public static Map<String, Object> mapDescribedProperties(final PropertyResolver resolver,
            final Description description, final PropertyScope defaultPropertyScope) {
        final List<Property> properties = description.getProperties();
        final PropertyResolver defaulted =
                PropertyResolverFactory.withDefaultValues(
                        PropertyResolverFactory.withDefaultScope(null != defaultPropertyScope ? defaultPropertyScope
                                : PropertyScope.InstanceOnly, resolver),
                        new PropertyDefaultValues(properties)
                );

        return PropertyResolverFactory.mapPropertyValues(properties, defaulted);
    }

    /**
     * Set instance field value for the given property, returns true if the field value was set, false otherwise
     */
    private static boolean setValueForProperty(final Property property, final Object value, final Object object) {
        final Field field = fieldForPropertyName(property.getName(), object);
        if (null == field) {
            return false;
        }
        final Property.Type type = property.getType();
        final Property.Type ftype = propertyTypeFromFieldType(field.getType());
        if (ftype != property.getType()
                && !(ftype == Property.Type.String
                && (property.getType() == Property.Type.Select
                || property.getType() == Property.Type.FreeSelect))) {

            throw new IllegalStateException(
                    "cannot map property {" + property.getName() + " type: " + property.getType() + "} to field {"
                            + field.getName() + " type: " + ftype + "}");
        }
        final Object resolvedValue;
        if (type == Property.Type.Integer) {
            final Integer intvalue;
            if (value instanceof String) {
                intvalue = Integer.parseInt((String) value);
            } else if (value instanceof Integer) {
                intvalue = (Integer) value;
            } else {
                //XXX
                return false;
            }
            resolvedValue = intvalue;
        } else if (type == Property.Type.Long) {
            final Long longvalue;
            if (value instanceof String) {
                longvalue = Long.parseLong((String) value);
            } else if (value instanceof Long) {
                longvalue = (Long) value;
            } else if (value instanceof Integer) {
                final int val = (Integer) value;
                longvalue = (long) val;
            } else {
                //XXX
                return false;
            }
            resolvedValue = longvalue;
        } else if (type == Property.Type.Boolean) {
            final Boolean boolvalue;
            if (value instanceof String) {
                boolvalue = Boolean.parseBoolean((String) value);
            } else if (value instanceof Boolean) {
                boolvalue = (Boolean) value;
            } else {
                //XXX
                return false;
            }
            resolvedValue = boolvalue;
        } else if (type == Property.Type.String || type == Property.Type.FreeSelect) {
            if (value instanceof String) {
                resolvedValue = value;
            } else {
                //XXX
                return false;
            }
        } else if (type == Property.Type.Select) {
            if (value instanceof String) {
                resolvedValue = value;
                if (!property.getSelectValues().contains((String) resolvedValue)) {
                    throw new RuntimeException(
                            "value not allowed for property " + property.getName() + ": " + resolvedValue);
                }
            } else {
                //XXX
                return false;
            }
        } else {
            //XXX
            return false;
        }
        try {
            setFieldValue(field, resolvedValue, object);
        } catch (IllegalAccessException e) {
            throw new RuntimeException("Unable to configure plugin: " + e.getMessage(), e);
        }
        return true;
    }

    private static void setFieldValue(final Field field, final Object value, final Object object)
            throws IllegalAccessException {
        if (!field.isAccessible()) {
            field.setAccessible(true);
        }
        field.set(object, value);
    }
}
TOP

Related Classes of com.dtolabs.rundeck.core.plugins.configuration.PluginAdapterUtility$PropertyDefaultValues

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.