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.WikiContext;
023import org.apache.wiki.api.exceptions.PluginException;
024import org.apache.wiki.api.plugin.WikiPlugin;
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
038    extends FormElement
039{
040    /**
041     *  {@inheritDoc}
042     */
043    public String execute( WikiContext ctx, Map< String, String > params )
044        throws PluginException
045    {
046        // Don't render if no error and error-only-rendering is on.
047        FormInfo info = getFormInfo( ctx );
048
049        ResourceBundle rb = Preferences.getBundle( ctx, WikiPlugin.CORE_PLUGINS_RESOURCEBUNDLE );
050        Map< String, String > previousValues = null;
051        
052        if ( info != null )
053        {
054            if ( info.hide() )
055            {
056                return "<p>" + rb.getString( "forminput.noneedtoshow" ) + "</p>";
057            }
058            previousValues = info.getSubmission();
059        }
060
061        if ( previousValues == null )
062        {
063            previousValues = new HashMap< String, String >();
064        }
065
066        Element field = buildSelect( params, previousValues, rb );
067
068        // We should look for extra params, e.g. width, ..., here.
069        return XhtmlUtil.serialize(field); // ctx.getEngine().getContentEncoding()
070    }
071
072
073    /**
074     * Builds a Select element.
075     */
076    private Element buildSelect(
077            Map<String,String> pluginParams,
078            Map<String,String> ctxValues, 
079            ResourceBundle rb )
080            throws PluginException
081    {
082        String inputName = pluginParams.get( PARAM_INPUTNAME );
083        if ( inputName == null ) {
084            throw new PluginException( rb.getString( "formselect.namemissing" ) );
085        }
086    
087        String inputValue = pluginParams.get( PARAM_VALUE );
088        String previousValue = ctxValues.get( inputName );
089        //
090        // We provide several ways to override the separator, in case
091        // some input application the default value.
092        //
093        String optionSeparator = pluginParams.get( "separator" );
094        if ( optionSeparator == null ) {
095            optionSeparator = ctxValues.get( "separator." + inputName);
096        }
097        if ( optionSeparator == null ) {
098            optionSeparator = ctxValues.get( "select.separator" );
099        }
100        if ( optionSeparator == null ) {
101            optionSeparator = ";";
102        }
103        
104        String optionSelector = pluginParams.get( "selector" );
105        if ( optionSelector == null ) {
106            optionSelector = ctxValues.get( "selector." + inputName );
107        }
108        if ( optionSelector == null ) {
109            optionSelector = ctxValues.get( "select.selector" );
110        }
111        if ( optionSelector == null ) {
112            optionSelector = "*";
113        }
114        if ( optionSelector.equals( optionSeparator ) ) {
115            optionSelector = null;
116        }
117        if ( inputValue == null ) {
118            inputValue = "";
119        }
120
121        // If values from the context contain the separator, we assume
122        // that the plugin or something else has given us a better
123        // list to display.
124        boolean contextValueOverride = false;
125        if ( previousValue != null ) {
126            if ( previousValue.indexOf( optionSeparator ) != -1 ) {
127                inputValue = previousValue;
128                previousValue = null;
129            } else {
130                // If a context value exists, but it's not a list,
131                // it'll just override any existing selector
132                // indications.
133                contextValueOverride = true;
134            }
135        }
136
137        String[] options = inputValue.split( optionSeparator );
138        int previouslySelected = -1;
139        
140        Element[] optionElements = new Element[options.length];
141        
142        //
143        //  Figure out which one of the options to select: prefer the one
144        //  that was previously selected, otherwise try to find the one
145        //  with the "select" marker.
146        //
147        for( int i = 0; i < options.length; i++ ) {
148            int indicated = -1;
149            options[i] = options[i].trim();
150            
151            if ( optionSelector != null && options[i].startsWith( optionSelector ) ) {
152                options[i] = options[i].substring( optionSelector.length() );
153                indicated = i;
154            }
155            if ( previouslySelected == -1 ) {
156                if ( !contextValueOverride && indicated > 0 ) {
157                    previouslySelected = indicated;
158                } else if ( previousValue != null && options[i].equals( previousValue ) ) {
159                    previouslySelected = i;
160                }
161            }
162            
163            // huh?
164//          optionElements[i] = new option( options[i] );
165//          optionElements[i].addElement( options[i] );
166            
167            optionElements[i] = XhtmlUtil.element(XHTML.option,options[i]);
168        }
169
170        if ( previouslySelected > -1 ) {
171            optionElements[previouslySelected].setAttribute(XHTML.ATTR_selected,"true");
172        }
173
174        Element select = XhtmlUtil.element(XHTML.select);
175        select.setAttribute(XHTML.ATTR_name,HANDLERPARAM_PREFIX + inputName);
176        for ( Element option : optionElements ) {
177            select.addContent(option);
178        }
179        return select;
180    }
181}