001/* 002 * (C) Copyright 2013 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Anahide Tchertchian 018 */ 019package org.nuxeo.ecm.platform.ui.web.renderer; 020 021import java.io.IOException; 022import java.util.Collection; 023import java.util.Iterator; 024 025import javax.el.ELException; 026import javax.faces.component.NamingContainer; 027import javax.faces.component.UIComponent; 028import javax.faces.component.UINamingContainer; 029import javax.faces.component.UISelectOne; 030import javax.faces.context.FacesContext; 031import javax.faces.context.ResponseWriter; 032import javax.faces.convert.Converter; 033import javax.faces.model.SelectItem; 034 035import com.sun.faces.renderkit.Attribute; 036import com.sun.faces.renderkit.AttributeManager; 037import com.sun.faces.renderkit.RenderKitUtils; 038import com.sun.faces.renderkit.html_basic.RadioRenderer; 039import com.sun.faces.util.RequestStateManager; 040 041/** 042 * Renderer that does not ignore the converter set on the component on submit 043 * 044 * @since 5.8 045 */ 046public class NXRadioRenderer extends RadioRenderer { 047 048 private static final Attribute[] ATTRIBUTES = AttributeManager.getAttributes(AttributeManager.Key.SELECTONERADIO); 049 050 @Override 051 protected void renderOption(FacesContext context, UIComponent component, Converter converter, SelectItem curItem, 052 Object currentSelections, Object[] submittedValues, boolean alignVertical, int itemNumber, 053 OptionComponentInfo optionInfo) throws IOException { 054 055 ResponseWriter writer = context.getResponseWriter(); 056 assert (writer != null); 057 058 UISelectOne selectOne = (UISelectOne) component; 059 Object curValue = selectOne.getSubmittedValue(); 060 if (curValue == null) { 061 curValue = selectOne.getValue(); 062 // XXX added for NXRadioRenderer 063 if (converter != null) { 064 curValue = converter.getAsString(context, component, curValue); 065 } 066 } 067 068 Class type = String.class; 069 if (curValue != null) { 070 type = curValue.getClass(); 071 if (type.isArray()) { 072 curValue = ((Object[]) curValue)[0]; 073 if (null != curValue) { 074 type = curValue.getClass(); 075 } 076 } else if (Collection.class.isAssignableFrom(type)) { 077 Iterator valueIter = ((Collection) curValue).iterator(); 078 if (null != valueIter && valueIter.hasNext()) { 079 curValue = valueIter.next(); 080 if (null != curValue) { 081 type = curValue.getClass(); 082 } 083 } 084 } 085 } 086 Object itemValue = curItem.getValue(); 087 RequestStateManager.set(context, RequestStateManager.TARGET_COMPONENT_ATTRIBUTE_NAME, component); 088 Object newValue; 089 try { 090 newValue = context.getApplication().getExpressionFactory().coerceToType(itemValue, type); 091 } catch (ELException ele) { 092 newValue = itemValue; 093 } catch (IllegalArgumentException iae) { 094 // If coerceToType fails, per the docs it should throw 095 // an ELException, however, GF 9.0 and 9.0u1 will throw 096 // an IllegalArgumentException instead (see GF issue 1527). 097 newValue = itemValue; 098 } 099 100 boolean checked = null != newValue && newValue.equals(curValue); 101 102 // FIXME: curItem.isNoSelectionOption() method not found 103 // if (optionInfo.isHideNoSelection() && curItem.isNoSelectionOption() 104 // && curValue != null && !checked) { 105 // return; 106 // } 107 if (optionInfo.isHideNoSelection() && curValue != null && !checked) { 108 return; 109 } 110 111 if (alignVertical) { 112 writer.writeText("\t", component, null); 113 writer.startElement("tr", component); 114 writer.writeText("\n", component, null); 115 } 116 117 String labelClass; 118 if (optionInfo.isDisabled() || curItem.isDisabled()) { 119 labelClass = optionInfo.getDisabledClass(); 120 } else { 121 labelClass = optionInfo.getEnabledClass(); 122 } 123 writer.startElement("td", component); 124 writer.writeText("\n", component, null); 125 126 writer.startElement("input", component); 127 writer.writeAttribute("type", "radio", "type"); 128 129 if (checked) { 130 writer.writeAttribute("checked", Boolean.TRUE, null); 131 } 132 writer.writeAttribute("name", component.getClientId(context), "clientId"); 133 String idString = component.getClientId(context) + NamingContainer.SEPARATOR_CHAR 134 + Integer.toString(itemNumber); 135 writer.writeAttribute("id", idString, "id"); 136 137 writer.writeAttribute("value", (getFormattedValue(context, component, curItem.getValue(), converter)), "value"); 138 139 // Don't render the disabled attribute twice if the 'parent' 140 // component is already marked disabled. 141 if (!optionInfo.isDisabled()) { 142 if (curItem.isDisabled()) { 143 writer.writeAttribute("disabled", true, "disabled"); 144 } 145 } 146 // Apply HTML 4.x attributes specified on UISelectMany component to all 147 // items in the list except styleClass and style which are rendered as 148 // attributes of outer most table. 149 RenderKitUtils.renderPassThruAttributes(context, writer, component, ATTRIBUTES, 150 getNonOnClickSelectBehaviors(component)); 151 RenderKitUtils.renderXHTMLStyleBooleanAttributes(writer, component); 152 153 RenderKitUtils.renderSelectOnclick(context, component, false); 154 155 writer.endElement("input"); 156 writer.startElement("label", component); 157 writer.writeAttribute("for", idString, "for"); 158 // if enabledClass or disabledClass attributes are specified, apply 159 // it on the label. 160 if (labelClass != null) { 161 writer.writeAttribute("class", labelClass, "labelClass"); 162 } 163 String itemLabel = curItem.getLabel(); 164 if (itemLabel != null) { 165 writer.writeText(" ", component, null); 166 if (!curItem.isEscape()) { 167 // It seems the ResponseWriter API should 168 // have a writeText() with a boolean property 169 // to determine if it content written should 170 // be escaped or not. 171 writer.write(itemLabel); 172 } else { 173 writer.writeText(itemLabel, component, "label"); 174 } 175 } 176 writer.endElement("label"); 177 writer.endElement("td"); 178 writer.writeText("\n", component, null); 179 if (alignVertical) { 180 writer.writeText("\t", component, null); 181 writer.endElement("tr"); 182 writer.writeText("\n", component, null); 183 } 184 } 185 186}