jp.ikedam.jenkins.plugins.extensible_choice_parameter.ExtensibleChoiceParameterDefinition.java Source code

Java tutorial

Introduction

Here is the source code for jp.ikedam.jenkins.plugins.extensible_choice_parameter.ExtensibleChoiceParameterDefinition.java

Source

/*
 * The MIT License
 * 
 * Copyright (c) 2012-2013 IKEDA Yasuyuki
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package jp.ikedam.jenkins.plugins.extensible_choice_parameter;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

import jenkins.model.Jenkins;
import hudson.Extension;
import hudson.DescriptorExtensionList;
import hudson.Util;
import hudson.model.Descriptor;
import hudson.model.Describable;
import hudson.model.ParameterValue;
import hudson.model.StringParameterValue;
import hudson.model.SimpleParameterDefinition;
import hudson.util.FormValidation;
import hudson.util.VariableResolver;

import org.apache.commons.lang.StringUtils;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.StaplerRequest;
import net.sf.json.JSONObject;

/**
 * Provides a choice parameter whose choices can be extended using Extension Points.
 *
 */
public class ExtensibleChoiceParameterDefinition extends SimpleParameterDefinition {
    private static final long serialVersionUID = 1L;

    private static final Pattern namePattern = Pattern.compile("[A-Za-z_][A-Za-z_0-9]*");

    /**
     * Deprecated
     */
    @Deprecated
    public static Pattern getNamePattern() {
        return namePattern;
    }

    /**
     * The internal class to work with views.
     * 
     * The following files are used (put in main/resource directory in the source tree).
     * <dl>
     *     <dt>config.jelly</dt>
     *         <dd>shown as a part of a job configuration page.</dd>
     *     <dt>index.jelly</dt>
     *         <dd>shown when a user launches a build, and specifies parameters of the build.</dd>
     *     </dt>
     * </dl>
     */
    @Extension
    public static class DescriptorImpl extends ParameterDescriptor {
        /**
         * Create a new instance of {@link SystemGroovyChoiceListProvider} from user inputs.
         * 
         * @param req
         * @param formData
         * @return
         * @throws hudson.model.Descriptor.FormException
         * @see hudson.model.Descriptor#newInstance(org.kohsuke.stapler.StaplerRequest, net.sf.json.JSONObject)
         */
        @Override
        public ExtensibleChoiceParameterDefinition newInstance(StaplerRequest req, JSONObject formData)
                throws hudson.model.Descriptor.FormException {
            return new ExtensibleChoiceParameterDefinition(formData.getString("name"),
                    bindJSONWithDescriptor(req, formData, "choiceListProvider", ChoiceListProvider.class),
                    formData.getBoolean("editable"), formData.getString("description"));
        }

        /**
         * Create a new {@link Describable} object from user inputs.
         * 
         * @param req
         * @param formData
         * @param fieldName
         * @param clazz
         * @return
         * @throws hudson.model.Descriptor.FormException
         */
        private <T extends Describable<?>> T bindJSONWithDescriptor(StaplerRequest req, JSONObject formData,
                String fieldName, Class<T> clazz) throws hudson.model.Descriptor.FormException {
            formData = formData.getJSONObject(fieldName);
            if (formData == null || formData.isNullObject()) {
                return null;
            }
            if (!formData.has("stapler-class")) {
                throw new FormException("No stapler-class is specified", fieldName);
            }
            String staplerClazzName = formData.getString("stapler-class");
            if (staplerClazzName == null) {
                throw new FormException("No stapler-class is specified", fieldName);
            }
            try {
                @SuppressWarnings("unchecked")
                Class<? extends T> staplerClass = (Class<? extends T>) Jenkins.getInstance()
                        .getPluginManager().uberClassLoader.loadClass(staplerClazzName);
                Descriptor<?> d = Jenkins.getInstance().getDescriptorOrDie(staplerClass);

                @SuppressWarnings("unchecked")
                T instance = (T) d.newInstance(req, formData);

                return instance;
            } catch (ClassNotFoundException e) {
                throw new FormException(String.format("Failed to instantiate %s", staplerClazzName), e, fieldName);
            }
        }

        /**
         * Returns the string to be shown in a job configuration page, in the dropdown of &quot;Add Parameter&quot;.
         * 
         * @return a name of this parameter type.
         * @see hudson.model.ParameterDefinition.ParameterDescriptor#getDisplayName()
         */
        @Override
        public String getDisplayName() {
            return Messages._ExtensibleChoiceParameterDefinition_DisplayName().toString();
        }

        /**
         * Returns all the available methods to provide choices.
         * 
         * Used for showing dropdown for users to select a choice provider.
         * 
         * @return DescriptorExtensionList of ChoiceListProvider subclasses.
         */
        public DescriptorExtensionList<ChoiceListProvider, Descriptor<ChoiceListProvider>> getChoiceListProviderList() {
            return ChoiceListProvider.all();
        }

        public FormValidation doCheckName(@QueryParameter String name) {
            if (StringUtils.isBlank(name)) {
                return FormValidation.error(Messages.ExtensibleChoiceParameterDefinition_Name_empty());
            }

            final String trimmedName = StringUtils.trim(name);
            final String EXPANDED = "GOOD";
            String expanded = Util.replaceMacro(String.format("${%s}", trimmedName),
                    new VariableResolver<String>() {
                        @Override
                        public String resolve(String name) {
                            if (trimmedName.equals(name)) {
                                return EXPANDED;
                            }
                            return null;
                        }
                    });

            if (!EXPANDED.equals(expanded)) {
                return FormValidation.warning(Messages.ExtensibleChoiceParameterDefinition_Name_invalid());
            }

            return FormValidation.ok();
        }
    }

    private boolean editable = false;

    /**
     * Is this parameter value can be set to a value not in the choices?
     * 
     * @return whether this parameter is editable.
     */
    public boolean isEditable() {
        return editable;
    }

    private ChoiceListProvider choiceListProvider = null;

    /**
     * The choice provider the user specified.
     * 
     * @return choice provider.
     */
    public ChoiceListProvider getChoiceListProvider() {
        return choiceListProvider;
    }

    /**
     * Return choices available for this parameter.
     * 
     * @return list of choices. never null.
     */
    public List<String> getChoiceList() {
        ChoiceListProvider provider = getChoiceListProvider();
        List<String> choiceList = (provider != null) ? provider.getChoiceList() : null;
        return (choiceList != null) ? choiceList : new ArrayList<String>(0);
    }

    /**
     * Constructor instantiating with parameters in the configuration page.
     * 
     * When instantiating from the saved configuration,
     * the object is directly serialized with XStream,
     * and no constructor is used.
     * 
     * @param name the name of this parameter (used as a variable name).
     * @param choiceListProvider the choice provider
     * @param editable whether this parameter can be a value not in choices.
     * @param description the description of this parameter. Used only for the convenience of users.
     */
    @DataBoundConstructor
    public ExtensibleChoiceParameterDefinition(String name, ChoiceListProvider choiceListProvider, boolean editable,
            String description) {
        // There seems no way to forbid invalid values to be submitted.
        // SimpleParameterDefinition seems not to trim name parameter, so trim here.
        super(StringUtils.trim(name), description);

        this.choiceListProvider = choiceListProvider;
        this.editable = editable;
    }

    /**
     * Test passed ParameterValue and return.
     * 
     * Common processing of createValue
     * 
     * @param value a value to test.
     * @return a value tested. same with value.
     */
    protected ParameterValue createValueCommon(StringParameterValue value) {
        if (!isEditable() && !getChoiceList().contains(value.value)) {
            // Something strange!: Not editable and specified a value not in the choices.
            throw new IllegalArgumentException("Illegal choice: " + value.value);
        }
        return value;
    }

    /**
     * Decide a value of this parameter from the user input.
     * 
     * @param request
     * @param jo the user input
     * @return the value of this parameter.
     * @see hudson.model.ParameterDefinition#createValue(org.kohsuke.stapler.StaplerRequest, net.sf.json.JSONObject)
     */
    @Override
    public ParameterValue createValue(StaplerRequest request, JSONObject jo) {
        StringParameterValue value = request.bindJSON(StringParameterValue.class, jo);
        value.setDescription(getDescription());

        return createValueCommon(value);
    }

    /**
     * Decide a value of this parameter from the user input.
     * 
     * @param value the user input
     * @return the value of this parameter.
     * @throws IllegalArgumentException The value is not in choices even the field is not editable.
     * @see hudson.model.SimpleParameterDefinition#createValue(java.lang.String)
     */
    @Override
    public ParameterValue createValue(String value) throws IllegalArgumentException {
        return createValueCommon(new StringParameterValue(getName(), value, getDescription()));
    }

    /**
     * Returns the default value of this parameter.
     * 
     * If not specified by the provider, 
     * the first value in the choice is used.
     * returns null if no choice list is defined.
     * 
     * @return the default value of this parameter.
     * @see hudson.model.ParameterDefinition#getDefaultParameterValue()
     */
    @Override
    public ParameterValue getDefaultParameterValue() {
        String defaultChoice = (getChoiceListProvider() != null) ? getChoiceListProvider().getDefaultChoice()
                : null;
        if (defaultChoice != null) {
            return createValue(defaultChoice);
        }

        List<String> choiceList = getChoiceList();
        return (choiceList.size() <= 0) ? null
                : new StringParameterValue(getName(), choiceList.get(0), getDescription());
    }
}