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