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.logging.Log; 021 import org.apache.commons.logging.LogFactory; 022 import org.apache.myfaces.tobago.component.ComponentUtil; 023 024 import javax.faces.FacesException; 025 import javax.faces.component.UIComponent; 026 import javax.faces.component.UISelectMany; 027 import javax.faces.context.FacesContext; 028 import javax.faces.convert.Converter; 029 import javax.faces.convert.ConverterException; 030 import javax.faces.el.ValueBinding; 031 import java.lang.reflect.Array; 032 import java.util.ArrayList; 033 import java.util.List; 034 import java.util.Arrays; 035 036 public class SelectManyRendererBase extends LayoutableRendererBase { 037 038 private static final Log LOG = LogFactory.getLog(SelectManyRendererBase.class); 039 040 public void decode(FacesContext facesContext, UIComponent component) { 041 if (ComponentUtil.isOutputOnly(component)) { 042 return; 043 } 044 if (component instanceof UISelectMany) { 045 UISelectMany uiSelectMany = (UISelectMany) component; 046 047 String[] newValues = (String[]) 048 facesContext.getExternalContext().getRequestParameterValuesMap().get(uiSelectMany.getClientId(facesContext)); 049 if (LOG.isDebugEnabled()) { 050 LOG.debug("decode: key='" + component.getClientId(facesContext) 051 + "' value='" + Arrays.toString(newValues) + "'"); 052 LOG.debug("size ... '" + (newValues != null ? newValues.length : -1) + "'"); 053 if (newValues != null) { 054 for (String newValue : newValues) { 055 LOG.debug("newValues[i] = '" + newValue + "'"); 056 } 057 } 058 } 059 060 if (newValues == null) { 061 newValues = new String[0]; // because no selection will not submitted by browsers 062 } 063 uiSelectMany.setSubmittedValue(newValues); 064 } 065 } 066 067 // the following is copied from myfaces shared RendererUtils 068 public Object getConvertedValue(FacesContext facesContext, UIComponent component, Object submittedValue) 069 throws ConverterException { 070 071 if (submittedValue == null) { 072 return null; 073 } else { 074 if (!(submittedValue instanceof String[])) { 075 throw new ConverterException("Submitted value of type String[] for component : " 076 + component.getClientId(facesContext) + "expected"); 077 } 078 } 079 return getConvertedUISelectManyValue(facesContext, (UISelectMany) component, (String[]) submittedValue); 080 } 081 082 private Object getConvertedUISelectManyValue(FacesContext facesContext, 083 UISelectMany component, 084 String[] submittedValue) 085 throws ConverterException { 086 // Attention! 087 // This code is duplicated in jsfapi component package. 088 // If you change something here please do the same in the other class! 089 090 if (submittedValue == null) { 091 throw new NullPointerException("submittedValue"); 092 } 093 094 ValueBinding vb = component.getValueBinding("value"); 095 Class valueType = null; 096 Class arrayComponentType = null; 097 if (vb != null) { 098 valueType = vb.getType(facesContext); 099 if (valueType != null && valueType.isArray()) { 100 arrayComponentType = valueType.getComponentType(); 101 } 102 } 103 104 Converter converter = component.getConverter(); 105 if (converter == null) { 106 if (valueType == null) { 107 // No converter, and no idea of expected type 108 // --> return the submitted String array 109 return submittedValue; 110 } 111 112 if (List.class.isAssignableFrom(valueType)) { 113 // expected type is a List 114 // --> according to javadoc of UISelectMany we assume that the element type 115 // is java.lang.String, and copy the String array to a new List 116 return Arrays.asList(submittedValue); 117 } 118 119 if (arrayComponentType == null) { 120 throw new IllegalArgumentException("ValueBinding for UISelectMany must be of type List or Array"); 121 } 122 123 if (String.class.equals(arrayComponentType)) { 124 return submittedValue; //No conversion needed for String type 125 } 126 if (Object.class.equals(arrayComponentType)) { 127 return submittedValue; //No conversion for Object class 128 } 129 130 try { 131 converter = facesContext.getApplication().createConverter(arrayComponentType); 132 } catch (FacesException e) { 133 LOG.error("No Converter for type " + arrayComponentType.getName() + " found", e); 134 return submittedValue; 135 } 136 } 137 138 // Now, we have a converter... 139 // We determine the type of the component array after converting one of it's elements 140 if (vb != null && arrayComponentType == null 141 && valueType != null && valueType.isArray()) { 142 if (submittedValue.length > 0) { 143 arrayComponentType = converter.getAsObject(facesContext, component, submittedValue[0]).getClass(); 144 } 145 } 146 147 if (valueType == null) { 148 // ...but have no idea of expected type 149 // --> so let's convert it to an Object array 150 int len = submittedValue.length; 151 Object [] convertedValues = (Object []) Array.newInstance( 152 arrayComponentType == null ? Object.class : arrayComponentType, len); 153 for (int i = 0; i < len; i++) { 154 convertedValues[i] 155 = converter.getAsObject(facesContext, component, submittedValue[i]); 156 } 157 return convertedValues; 158 } 159 160 if (List.class.isAssignableFrom(valueType)) { 161 // Curious case: According to specs we should assume, that the element type 162 // of this List is java.lang.String. But there is a Converter set for this 163 // component. Because the user must know what he is doing, we will convert the values. 164 int len = submittedValue.length; 165 List lst = new ArrayList(len); 166 for (int i = 0; i < len; i++) { 167 lst.add(converter.getAsObject(facesContext, component, submittedValue[i])); 168 } 169 return lst; 170 } 171 172 if (arrayComponentType == null) { 173 throw new IllegalArgumentException("ValueBinding for UISelectMany must be of type List or Array"); 174 } 175 176 if (arrayComponentType.isPrimitive()) { 177 //primitive array 178 int len = submittedValue.length; 179 Object convertedValues = Array.newInstance(arrayComponentType, len); 180 for (int i = 0; i < len; i++) { 181 Array.set(convertedValues, i, 182 converter.getAsObject(facesContext, component, submittedValue[i])); 183 } 184 return convertedValues; 185 } else { 186 //Object array 187 int len = submittedValue.length; 188 ArrayList convertedValues = new ArrayList(len); 189 for (int i = 0; i < len; i++) { 190 convertedValues.add(i, converter.getAsObject(facesContext, component, submittedValue[i])); 191 } 192 return convertedValues.toArray((Object[]) Array.newInstance(arrayComponentType, len)); 193 } 194 } 195 } 196 197