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}