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}