001 package org.apache.myfaces.tobago.renderkit; 002 003 /* 004 * Licensed to the Apache Software Foundation (ASF) under one or more 005 * contributor license agreements. See the NOTICE file distributed with 006 * this work for additional information regarding copyright ownership. 007 * The ASF licenses this file to You under the Apache License, Version 2.0 008 * (the "License"); you may not use this file except in compliance with 009 * the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 */ 019 020 import org.apache.commons.lang.ArrayUtils; 021 import org.slf4j.Logger; 022 import org.slf4j.LoggerFactory; 023 import org.apache.myfaces.tobago.util.ComponentUtils; 024 025 import javax.faces.FacesException; 026 import javax.faces.component.UIComponent; 027 import javax.faces.component.UISelectMany; 028 import javax.faces.context.FacesContext; 029 import javax.faces.convert.Converter; 030 import javax.faces.convert.ConverterException; 031 import javax.faces.el.ValueBinding; 032 import java.lang.reflect.Array; 033 import java.util.ArrayList; 034 import java.util.Arrays; 035 import java.util.List; 036 037 public class SelectManyRendererBase extends LayoutComponentRendererBase { 038 039 private static final Logger LOG = LoggerFactory.getLogger(SelectManyRendererBase.class); 040 041 public void decode(FacesContext facesContext, UIComponent component) { 042 if (ComponentUtils.isOutputOnly(component)) { 043 return; 044 } 045 if (component instanceof UISelectMany) { 046 UISelectMany uiSelectMany = (UISelectMany) component; 047 048 String[] newValues = (String[]) 049 facesContext.getExternalContext().getRequestParameterValuesMap().get(uiSelectMany.getClientId(facesContext)); 050 if (LOG.isDebugEnabled()) { 051 LOG.debug("decode: key='" + component.getClientId(facesContext) 052 + "' value='" + Arrays.toString(newValues) + "'"); 053 LOG.debug("size ... '" + (newValues != null ? newValues.length : -1) + "'"); 054 if (newValues != null) { 055 for (String newValue : newValues) { 056 LOG.debug("newValues[i] = '" + newValue + "'"); 057 } 058 } 059 } 060 061 if (newValues == null) { 062 newValues = ArrayUtils.EMPTY_STRING_ARRAY; // because no selection will not submitted by browsers 063 } 064 uiSelectMany.setSubmittedValue(newValues); 065 } 066 } 067 068 // the following is copied from myfaces shared RendererUtils 069 public Object getConvertedValue(FacesContext facesContext, UIComponent component, Object submittedValue) 070 throws ConverterException { 071 072 if (submittedValue == null) { 073 return null; 074 } else { 075 if (!(submittedValue instanceof String[])) { 076 throw new ConverterException("Submitted value of type String[] for component : " 077 + component.getClientId(facesContext) + "expected"); 078 } 079 } 080 return getConvertedUISelectManyValue(facesContext, (UISelectMany) component, (String[]) submittedValue); 081 } 082 083 private Object getConvertedUISelectManyValue(FacesContext facesContext, 084 UISelectMany component, 085 String[] submittedValue) 086 throws ConverterException { 087 // Attention! 088 // This code is duplicated in jsfapi component package. 089 // If you change something here please do the same in the other class! 090 091 if (submittedValue == null) { 092 throw new NullPointerException("submittedValue"); 093 } 094 095 ValueBinding vb = component.getValueBinding("value"); 096 Class valueType = null; 097 Class arrayComponentType = null; 098 if (vb != null) { 099 valueType = vb.getType(facesContext); 100 if (valueType != null && valueType.isArray()) { 101 arrayComponentType = valueType.getComponentType(); 102 } 103 } 104 105 Converter converter = component.getConverter(); 106 if (converter == null) { 107 if (valueType == null) { 108 // No converter, and no idea of expected type 109 // --> return the submitted String array 110 return submittedValue; 111 } 112 113 if (List.class.isAssignableFrom(valueType)) { 114 // expected type is a List 115 // --> according to javadoc of UISelectMany we assume that the element type 116 // is java.lang.String, and copy the String array to a new List 117 return Arrays.asList(submittedValue); 118 } 119 120 if (arrayComponentType == null) { 121 throw new IllegalArgumentException("ValueBinding for UISelectMany must be of type List or Array"); 122 } 123 124 if (String.class.equals(arrayComponentType)) { 125 return submittedValue; //No conversion needed for String type 126 } 127 if (Object.class.equals(arrayComponentType)) { 128 return submittedValue; //No conversion for Object class 129 } 130 131 try { 132 converter = facesContext.getApplication().createConverter(arrayComponentType); 133 } catch (FacesException e) { 134 LOG.error("No Converter for type " + arrayComponentType.getName() + " found", e); 135 return submittedValue; 136 } 137 } 138 139 // Now, we have a converter... 140 // We determine the type of the component array after converting one of it's elements 141 if (vb != null && arrayComponentType == null 142 && valueType != null && valueType.isArray()) { 143 if (submittedValue.length > 0) { 144 arrayComponentType = converter.getAsObject(facesContext, component, submittedValue[0]).getClass(); 145 } 146 } 147 148 if (valueType == null) { 149 // ...but have no idea of expected type 150 // --> so let's convert it to an Object array 151 int len = submittedValue.length; 152 Object[] convertedValues = (Object[]) Array.newInstance( 153 arrayComponentType == null ? Object.class : arrayComponentType, len); 154 for (int i = 0; i < len; i++) { 155 convertedValues[i] 156 = converter.getAsObject(facesContext, component, submittedValue[i]); 157 } 158 return convertedValues; 159 } 160 161 if (List.class.isAssignableFrom(valueType)) { 162 // Curious case: According to specs we should assume, that the element type 163 // of this List is java.lang.String. But there is a Converter set for this 164 // component. Because the user must know what he is doing, we will convert the values. 165 int length = submittedValue.length; 166 List<Object> list = new ArrayList<Object>(length); 167 for (int i = 0; i < length; i++) { 168 list.add(converter.getAsObject(facesContext, component, submittedValue[i])); 169 } 170 return list; 171 } 172 173 if (arrayComponentType == null) { 174 throw new IllegalArgumentException("ValueBinding for UISelectMany must be of type List or Array"); 175 } 176 177 if (arrayComponentType.isPrimitive()) { 178 // primitive array 179 int len = submittedValue.length; 180 Object convertedValues = Array.newInstance(arrayComponentType, len); 181 for (int i = 0; i < len; i++) { 182 Array.set(convertedValues, i, 183 converter.getAsObject(facesContext, component, submittedValue[i])); 184 } 185 return convertedValues; 186 } else { 187 // Object array 188 int length = submittedValue.length; 189 List<Object> convertedValues = new ArrayList<Object>(length); 190 for (int i = 0; i < length; i++) { 191 convertedValues.add(i, converter.getAsObject(facesContext, component, submittedValue[i])); 192 } 193 return convertedValues.toArray((Object[]) Array.newInstance(arrayComponentType, length)); 194 } 195 } 196 197 }