    001/*
    002    Licensed to the Apache Software Foundation (ASF) under one
    003    or more contributor license agreements.  See the NOTICE file
    004    distributed with this work for additional information
    005    regarding copyright ownership.  The ASF licenses this file
    006    to you under the Apache License, Version 2.0 (the
    007    "License"); you may not use this file except in compliance
    008    with the License.  You may obtain a copy of the License at
    009
    010       http://www.apache.org/licenses/LICENSE-2.0
    011
    012    Unless required by applicable law or agreed to in writing,
    013    software distributed under the License is distributed on an
    014    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    015    KIND, either express or implied.  See the License for the
    016    specific language governing permissions and limitations
    017    under the License.
    018 */
    019
    020package org.apache.wiki.forms;
    021
    022import org.apache.wiki.api.core.Context;
    023import org.apache.wiki.api.exceptions.PluginException;
    024import org.apache.wiki.api.plugin.Plugin;
    025import org.apache.wiki.preferences.Preferences;
    026import org.apache.wiki.util.XHTML;
    027import org.apache.wiki.util.XhtmlUtil;
    028import org.jdom2.Element;
    029
    030import java.util.HashMap;
    031import java.util.Map;
    032import java.util.ResourceBundle;
    033
    034/**
    035 *  Creates a Form select field.
    036 */
    037public class FormSelect extends FormElement {
    038
    039    /**
    040     *  {@inheritDoc}
    041     */
    042    @Override
    043    public String execute( final Context ctx, final Map< String, String > params ) throws PluginException {
    044        // Don't render if no error and error-only-rendering is on.
    045        final FormInfo info = getFormInfo( ctx );
    046        final ResourceBundle rb = Preferences.getBundle( ctx, Plugin.CORE_PLUGINS_RESOURCEBUNDLE );
    047        Map< String, String > previousValues = null;
    048
    049        if( info != null ) {
    050            if( info.hide() ) {
    051                return "<p>" + rb.getString( "forminput.noneedtoshow" ) + "</p>";
    052            }
    053            previousValues = info.getSubmission();
    054        }
    055
    056        if( previousValues == null ) {
    057            previousValues = new HashMap<>();
    058        }
    059
    060        final Element field = buildSelect( params, previousValues, rb );
    061
    062        // We should look for extra params, e.g. width, ..., here.
    063        return XhtmlUtil.serialize(field); // ctx.getEngine().getContentEncoding()
    064    }
    065
    066
    067    /**
    068     * Builds a Select element.
    069     */
    070    private Element buildSelect(
    071            final Map< String, String > pluginParams,
    072            final Map< String, String > ctxValues,
    073            final ResourceBundle rb )
    074            throws PluginException {
    075        final String inputName = pluginParams.get( PARAM_INPUTNAME );
    076        if ( inputName == null ) {
    077            throw new PluginException( rb.getString( "formselect.namemissing" ) );
    078        }
    079
    080        String inputValue = pluginParams.get( PARAM_VALUE );
    081        String previousValue = ctxValues.get( inputName );
    082        //
    083        // We provide several ways to override the separator, in case some input application the default value.
    084        //
    085        String optionSeparator = pluginParams.get( "separator" );
    086        if ( optionSeparator == null ) {
    087            optionSeparator = ctxValues.get( "separator." + inputName);
    088        }
    089        if ( optionSeparator == null ) {
    090            optionSeparator = ctxValues.get( "select.separator" );
    091        }
    092        if ( optionSeparator == null ) {
    093            optionSeparator = ";";
    094        }
    095
    096        String optionSelector = pluginParams.get( "selector" );
    097        if ( optionSelector == null ) {
    098            optionSelector = ctxValues.get( "selector." + inputName );
    099        }
    100        if ( optionSelector == null ) {
    101            optionSelector = ctxValues.get( "select.selector" );
    102        }
    103        if ( optionSelector == null ) {
    104            optionSelector = "*";
    105        }
    106        if ( optionSelector.equals( optionSeparator ) ) {
    107            optionSelector = null;
    108        }
    109        if ( inputValue == null ) {
    110            inputValue = "";
    111        }
    112
    113        // If values from the context contain the separator, we assume that the plugin or something else has given us a better
    114        // list to display.
    115        boolean contextValueOverride = false;
    116        if ( previousValue != null ) {
    117            if ( previousValue.contains( optionSeparator ) ) {
    118                inputValue = previousValue;
    119                previousValue = null;
    120            } else {
    121                // If a context value exists, but it's not a list, it'll just override any existing selector indications.
    122                contextValueOverride = true;
    123            }
    124        }
    125
    126        final String[] options = inputValue.split( optionSeparator );
    127        int previouslySelected = -1;
    128
    129        final Element[] optionElements = new Element[options.length];
    130
    131        //
    132        //  Figure out which one of the options to select: prefer the one that was previously selected, otherwise try to find the one
    133        //  with the "select" marker.
    134        //
    135        for( int i = 0; i < options.length; i++ ) {
    136            int indicated = -1;
    137            options[i] = options[i].trim();
    138
    139            if ( optionSelector != null && options[i].startsWith( optionSelector ) ) {
    140                options[i] = options[i].substring( optionSelector.length() );
    141                indicated = i;
    142            }
    143            if ( previouslySelected == -1 ) {
    144                if ( !contextValueOverride && indicated > 0 ) {
    145                    previouslySelected = indicated;
    146                } else if ( options[ i ].equals( previousValue ) ) {
    147                    previouslySelected = i;
    148                }
    149            }
    150
    151            // huh?
    152//          optionElements[i] = new option( options[i] );
    153//          optionElements[i].addElement( options[i] );
    154
    155            optionElements[i] = XhtmlUtil.element(XHTML.option,options[i]);
    156        }
    157
    158        if ( previouslySelected > -1 ) {
    159            optionElements[previouslySelected].setAttribute(XHTML.ATTR_selected,"true");
    160        }
    161
    162        final Element select = XhtmlUtil.element(XHTML.select);
    163        select.setAttribute(XHTML.ATTR_name,HANDLERPARAM_PREFIX + inputName);
    164        for ( final Element option : optionElements ) {
    165            select.addContent(option);
    166        }
    167        return select;
    168    }
    169
    170}