PageRenderTime 27ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/crank-jsf-support/src/main/java/org/crank/javax/faces/component/MenuRenderer.java

http://krank.googlecode.com/
Java | 835 lines | 608 code | 120 blank | 107 comment | 164 complexity | ca3b1aceb4a116ef2ab54a7f6ff4fbc0 MD5 | raw file
  1. /*
  2. * The contents of this file are subject to the terms
  3. * of the Common Development and Distribution License
  4. * (the License). You may not use this file except in
  5. * compliance with the License.
  6. *
  7. * You can obtain a copy of the License at
  8. * https://javaserverfaces.dev.java.net/CDDL.html or
  9. * legal/CDDLv1.0.txt.
  10. * See the License for the specific language governing
  11. * permission and limitations under the License.
  12. *
  13. * When distributing Covered Code, include this CDDL
  14. * Header Notice in each file and include the License file
  15. * at legal/CDDLv1.0.txt.
  16. * If applicable, add the following below the CDDL Header,
  17. * with the fields enclosed by brackets [] replaced by
  18. * your own identifying information:
  19. * "Portions Copyrighted [year] [name of copyright owner]"
  20. *
  21. * [Name of File] [ver.__] [Date]
  22. *
  23. * Copyright 2005 Sun Microsystems Inc. All Rights Reserved
  24. */
  25. /*
  26. * $Id: MenuRenderer.java,v 1.85 2006/12/13 17:29:14 youngm Exp $
  27. *
  28. * (C) Copyright International Business Machines Corp., 2001,2002
  29. * The source code for this program is not published or otherwise
  30. * divested of its trade secrets, irrespective of what has been
  31. * deposited with the U. S. Copyright Office.
  32. */
  33. // MenuRenderer.java
  34. package org.crank.javax.faces.component;
  35. import javax.el.ValueExpression;
  36. import javax.faces.component.UIComponent;
  37. import javax.faces.component.UISelectMany;
  38. import javax.faces.component.UISelectOne;
  39. import javax.faces.context.FacesContext;
  40. import javax.faces.context.ResponseWriter;
  41. import javax.faces.convert.Converter;
  42. import javax.faces.convert.ConverterException;
  43. import javax.faces.model.SelectItem;
  44. import javax.faces.model.SelectItemGroup;
  45. import org.springframework.beans.BeanWrapper;
  46. import org.springframework.beans.BeanWrapperImpl;
  47. import java.io.IOException;
  48. import java.lang.reflect.Array;
  49. import java.util.Arrays;
  50. import java.util.Collection;
  51. import java.util.Iterator;
  52. import java.util.List;
  53. import java.util.Map;
  54. import java.util.logging.Level;
  55. import com.sun.faces.RIConstants;
  56. import com.sun.faces.application.ConverterPropertyEditorBase;
  57. import com.sun.faces.renderkit.RenderKitUtils;
  58. import com.sun.faces.renderkit.html_basic.HtmlBasicInputRenderer;
  59. import com.sun.faces.util.MessageUtils;
  60. import com.sun.faces.util.Util;
  61. /**
  62. * <B>MenuRenderer</B> is a class that renders the current value of
  63. * <code>UISelectOne<code> or <code>UISelectMany<code> component as a list of
  64. * menu options.
  65. */
  66. public class MenuRenderer extends HtmlBasicInputRenderer {
  67. // ---------------------------------------------------------- Public Methods
  68. public Object convertSelectManyValue(FacesContext context,
  69. UISelectMany uiSelectMany,
  70. String[] newValues)
  71. throws ConverterException {
  72. // if we have no local value, try to get the valueExpression.
  73. ValueExpression valueExpression =
  74. uiSelectMany.getValueExpression("value");
  75. Object result = newValues; // default case, set local value
  76. Class<?> modelType = null;
  77. boolean throwException = false;
  78. // If we have a ValueExpression
  79. if (null != valueExpression) {
  80. modelType = valueExpression.getType(context.getELContext());
  81. // Does the valueExpression resolve properly to something with
  82. // a type?
  83. if(modelType != null) {
  84. result = convertSelectManyValuesForModel(context,
  85. uiSelectMany,
  86. modelType,
  87. newValues);
  88. }
  89. // If it could not be converted, as a fall back try the type of
  90. // the valueExpression's current value covering some edge cases such
  91. // as where the current value came from a Map.
  92. if(result == null) {
  93. Object value = valueExpression.getValue(context.getELContext());
  94. if(value != null) {
  95. result = convertSelectManyValuesForModel(context,
  96. uiSelectMany,
  97. value.getClass(),
  98. newValues);
  99. }
  100. }
  101. if(result == null) {
  102. throwException = true;
  103. }
  104. } else {
  105. // No ValueExpression, just use Object array.
  106. result = convertSelectManyValues(context, uiSelectMany,
  107. Object[].class,
  108. newValues);
  109. }
  110. if (throwException) {
  111. StringBuffer values = new StringBuffer();
  112. if (null != newValues) {
  113. for (int i = 0; i < newValues.length; i++) {
  114. if (i == 0) {
  115. values.append(newValues[i]);
  116. } else {
  117. values.append(' ').append(newValues[i]);
  118. }
  119. }
  120. }
  121. Object[] params = {
  122. values.toString(),
  123. valueExpression.getExpressionString()
  124. };
  125. throw new ConverterException
  126. (MessageUtils.getExceptionMessage(MessageUtils.CONVERSION_ERROR_MESSAGE_ID,
  127. params));
  128. }
  129. // At this point, result is ready to be set as the value
  130. if (logger.isLoggable(Level.FINE)) {
  131. logger.fine("SelectMany Component " + uiSelectMany.getId() +
  132. " convertedValues " + result);
  133. }
  134. return result;
  135. }
  136. /**
  137. * Converts the provided string array and places them into the correct provided model type.
  138. * @param context
  139. * @param uiSelectMany
  140. * @param modelType
  141. * @param newValues
  142. * @return
  143. */
  144. private Object convertSelectManyValuesForModel(FacesContext context,
  145. UISelectMany uiSelectMany,
  146. Class<?> modelType,
  147. String[] newValues) {
  148. Object result = null;
  149. if (modelType.isArray()) {
  150. result = convertSelectManyValues(context,
  151. uiSelectMany,
  152. modelType,
  153. newValues);
  154. } else if (List.class.isAssignableFrom(modelType)) {
  155. result = Arrays.asList((Object[]) convertSelectManyValues(
  156. context,
  157. uiSelectMany,
  158. Object[].class,
  159. newValues));
  160. }
  161. return result;
  162. }
  163. public Object convertSelectOneValue(FacesContext context,
  164. UISelectOne uiSelectOne,
  165. String newValue)
  166. throws ConverterException {
  167. Object convertedValue = null;
  168. if (RIConstants.NO_VALUE.equals(newValue)) {
  169. return null;
  170. }
  171. if (newValue == null) {
  172. if (logger.isLoggable(Level.FINE)) {
  173. logger.fine("No conversion necessary for SelectOne Component "
  174. + uiSelectOne.getId()
  175. + " since the new value is null ");
  176. }
  177. return null;
  178. }
  179. convertedValue =
  180. super.getConvertedValue(context, uiSelectOne, newValue);
  181. if (logger.isLoggable(Level.FINE)) {
  182. logger.fine("SelectOne Component " + uiSelectOne.getId() +
  183. " convertedValue " + convertedValue);
  184. }
  185. return convertedValue;
  186. }
  187. public void decode(FacesContext context, UIComponent component) {
  188. if (context == null) {
  189. throw new NullPointerException(
  190. MessageUtils.getExceptionMessageString(MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID,
  191. "context"));
  192. }
  193. if (component == null) {
  194. throw new NullPointerException(
  195. MessageUtils.getExceptionMessageString(MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID,
  196. "component"));
  197. }
  198. if (logger.isLoggable(Level.FINER)) {
  199. logger.log(Level.FINER,
  200. "Begin decoding component " + component.getId());
  201. }
  202. // If the component is disabled, do not change the value of the
  203. // component, since its state cannot be changed.
  204. if (Util.componentIsDisabledOrReadonly(component)) {
  205. if (logger.isLoggable(Level.FINE)) {
  206. logger.fine("No decoding necessary since the component " +
  207. component.getId() + " is disabled");
  208. }
  209. return;
  210. }
  211. String clientId = component.getClientId(context);
  212. assert(clientId != null);
  213. // currently we assume the model type to be of type string or
  214. // convertible to string and localized by the application.
  215. if (component instanceof UISelectMany) {
  216. Map<String, String[]> requestParameterValuesMap =
  217. context.getExternalContext().
  218. getRequestParameterValuesMap();
  219. if (requestParameterValuesMap.containsKey(clientId)) {
  220. String newValues[] = requestParameterValuesMap.
  221. get(clientId);
  222. setSubmittedValue(component, newValues);
  223. if (logger.isLoggable(Level.FINE)) {
  224. logger.fine("submitted values for UISelectMany component "
  225. +
  226. component.getId()
  227. + " after decoding "
  228. + newValues);
  229. }
  230. } else {
  231. // Use the empty array, not null, to distinguish
  232. // between an deselected UISelectMany and a disabled one
  233. setSubmittedValue(component, new String[0]);
  234. if (logger.isLoggable(Level.FINE)) {
  235. logger.fine("Set empty array for UISelectMany component " +
  236. component.getId() + " after decoding ");
  237. }
  238. }
  239. } else {
  240. // this is a UISelectOne
  241. Map<String, String> requestParameterMap =
  242. context.getExternalContext().
  243. getRequestParameterMap();
  244. if (requestParameterMap.containsKey(clientId)) {
  245. String newValue = requestParameterMap.get(clientId);
  246. setSubmittedValue(component, newValue);
  247. if (logger.isLoggable(Level.FINE)) {
  248. logger.fine("submitted value for UISelectOne component "
  249. +
  250. component.getId()
  251. + " after decoding "
  252. + newValue);
  253. }
  254. } else {
  255. // there is no value, but this is different from a null
  256. // value.
  257. setSubmittedValue(component, RIConstants.NO_VALUE);
  258. }
  259. }
  260. return;
  261. }
  262. public void encodeBegin(FacesContext context, UIComponent component)
  263. throws IOException {
  264. if (context == null) {
  265. throw new NullPointerException(
  266. MessageUtils.getExceptionMessageString(MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID,
  267. "context"));
  268. }
  269. if (component == null) {
  270. throw new NullPointerException(
  271. MessageUtils.getExceptionMessageString(MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID,
  272. "component"));
  273. }
  274. }
  275. public void encodeEnd(FacesContext context, UIComponent component)
  276. throws IOException {
  277. if (context == null) {
  278. throw new NullPointerException(
  279. MessageUtils.getExceptionMessageString(MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID,
  280. "context"));
  281. }
  282. if (component == null) {
  283. throw new NullPointerException(
  284. MessageUtils.getExceptionMessageString(MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID,
  285. "component"));
  286. }
  287. if (logger.isLoggable(Level.FINER)) {
  288. logger.log(Level.FINER,
  289. "Begin encoding component " + component.getId());
  290. }
  291. // suppress rendering if "rendered" property on the component is
  292. // false.
  293. if (!component.isRendered()) {
  294. if (logger.isLoggable(Level.FINE)) {
  295. logger.fine("End encoding component " +
  296. component.getId() + " since " +
  297. "rendered attribute is set to false ");
  298. }
  299. return;
  300. }
  301. renderSelect(context, component);
  302. if (logger.isLoggable(Level.FINER)) {
  303. logger.log(Level.FINER,
  304. "End encoding component " + component.getId());
  305. }
  306. }
  307. public Object getConvertedValue(FacesContext context, UIComponent component,
  308. Object submittedValue)
  309. throws ConverterException {
  310. if (component instanceof UISelectMany) {
  311. // need to set the 'TARGET_COMPONENT_ATTRIBUTE_NAME' request attr so the
  312. // coerce-value call in the jsf-api UISelectMany.matchValue will work
  313. // (need a better way to determine the currently processing UIComponent ...)
  314. Map<String, Object> requestMap = context.getExternalContext().getRequestMap();
  315. requestMap.put(ConverterPropertyEditorBase.TARGET_COMPONENT_ATTRIBUTE_NAME,
  316. component);
  317. return convertSelectManyValue(context,
  318. ((UISelectMany) component),
  319. (String[]) submittedValue);
  320. } else {
  321. return convertSelectOneValue(context,
  322. ((UISelectOne) component),
  323. (String) submittedValue);
  324. }
  325. }
  326. // ------------------------------------------------------- Protected Methods
  327. protected Object convertSelectManyValues(FacesContext context,
  328. UISelectMany uiSelectMany,
  329. Class<?> arrayClass,
  330. String[] newValues)
  331. throws ConverterException {
  332. Object result = null;
  333. Converter converter = null;
  334. int len = (null != newValues ? newValues.length : 0);
  335. Class<?> elementType = arrayClass.getComponentType();
  336. // Optimization: If the elementType is String, we don't need
  337. // conversion. Just return newValues.
  338. if (elementType.equals(String.class)) {
  339. return newValues;
  340. }
  341. try {
  342. result = Array.newInstance(elementType, len);
  343. } catch (Exception e) {
  344. throw new ConverterException(e);
  345. }
  346. // bail out now if we have no new values, returning our
  347. // oh-so-useful zero-length array.
  348. if (null == newValues) {
  349. return result;
  350. }
  351. // obtain a converter.
  352. // attached converter takes priority
  353. if (null == (converter = uiSelectMany.getConverter())) {
  354. // Otherwise, look for a by-type converter
  355. if (null == (converter = Util.getConverterForClass(elementType,
  356. context))) {
  357. // if that fails, and the attached values are of Object type,
  358. // we don't need conversion.
  359. if (elementType.equals(Object.class)) {
  360. return newValues;
  361. }
  362. StringBuffer valueStr = new StringBuffer();
  363. for (int i = 0; i < len; i++) {
  364. if (i == 0) {
  365. valueStr.append(newValues[i]);
  366. } else {
  367. valueStr.append(' ').append(newValues[i]);
  368. }
  369. }
  370. Object[] params = {
  371. valueStr.toString(),
  372. "null Converter"
  373. };
  374. throw new ConverterException(MessageUtils.getExceptionMessage(
  375. MessageUtils.CONVERSION_ERROR_MESSAGE_ID, params));
  376. }
  377. }
  378. assert(null != result);
  379. if (elementType.isPrimitive()) {
  380. for (int i = 0; i < len; i++) {
  381. if (elementType.equals(Boolean.TYPE)) {
  382. Array.setBoolean(result, i,
  383. ((Boolean) converter.getAsObject(context,
  384. uiSelectMany,
  385. newValues[i])));
  386. } else if (elementType.equals(Byte.TYPE)) {
  387. Array.setByte(result, i,
  388. ((Byte) converter.getAsObject(context,
  389. uiSelectMany,
  390. newValues[i])));
  391. } else if (elementType.equals(Double.TYPE)) {
  392. Array.setDouble(result, i,
  393. ((Double) converter.getAsObject(context,
  394. uiSelectMany,
  395. newValues[i])));
  396. } else if (elementType.equals(Float.TYPE)) {
  397. Array.setFloat(result, i,
  398. ((Float) converter.getAsObject(context,
  399. uiSelectMany,
  400. newValues[i])));
  401. } else if (elementType.equals(Integer.TYPE)) {
  402. Array.setInt(result, i,
  403. ((Integer) converter.getAsObject(context,
  404. uiSelectMany,
  405. newValues[i])));
  406. } else if (elementType.equals(Character.TYPE)) {
  407. Array.setChar(result, i,
  408. ((Character) converter.getAsObject(context,
  409. uiSelectMany,
  410. newValues[i])));
  411. } else if (elementType.equals(Short.TYPE)) {
  412. Array.setShort(result, i,
  413. ((Short) converter.getAsObject(context,
  414. uiSelectMany,
  415. newValues[i])));
  416. } else if (elementType.equals(Long.TYPE)) {
  417. Array.setLong(result, i,
  418. ((Long) converter.getAsObject(context,
  419. uiSelectMany,
  420. newValues[i])));
  421. }
  422. }
  423. } else {
  424. for (int i = 0; i < len; i++) {
  425. if (logger.isLoggable(Level.FINE)) {
  426. Object converted = converter.getAsObject(context,
  427. uiSelectMany,
  428. newValues[i]);
  429. logger.fine("String value: " + newValues[i] +
  430. " converts to : " + converted.toString());
  431. }
  432. Array.set(result, i, converter.getAsObject(context,
  433. uiSelectMany,
  434. newValues[i]));
  435. }
  436. }
  437. return result;
  438. }
  439. protected void renderOption(FacesContext context, UIComponent component,
  440. SelectItem curItem) throws IOException {
  441. ResponseWriter writer = context.getResponseWriter();
  442. assert(writer != null);
  443. writer.writeText("\t", component, null);
  444. writer.startElement("option", component);
  445. String valueString = getFormattedValue(context, component,
  446. curItem.getValue());
  447. writer.writeAttribute("value", valueString, "value");
  448. Object submittedValues[] = getSubmittedSelectedValues(context,
  449. component);
  450. //Class type = String.class;
  451. Object valuesArray = null;
  452. Object itemValue = null;
  453. boolean isSelected = false;
  454. boolean containsValue = false;
  455. if (submittedValues != null) {
  456. containsValue = containsaValue(submittedValues);
  457. if (containsValue) {
  458. valuesArray = submittedValues;
  459. itemValue = valueString;
  460. } else {
  461. valuesArray = getCurrentSelectedValues(context, component);
  462. itemValue = curItem.getValue();
  463. }
  464. } else {
  465. valuesArray = getCurrentSelectedValues(context, component);
  466. itemValue = curItem.getValue();
  467. }
  468. if (valuesArray != null) {
  469. //type = valuesArray.getClass().getComponentType();
  470. }
  471. Map<String, Object> requestMap = context.getExternalContext().getRequestMap();
  472. requestMap.put(ConverterPropertyEditorBase.TARGET_COMPONENT_ATTRIBUTE_NAME,
  473. component);
  474. Object newValue = null;
  475. // try {
  476. // newValue = context.getApplication().getExpressionFactory().
  477. // coerceToType(itemValue, type);
  478. // } catch (Exception e) {
  479. // // this should catch an ELException, but there is a bug
  480. // // in ExpressionFactory.coerceToType() in GF
  481. // newValue = null;
  482. // }
  483. newValue = itemValue;
  484. isSelected = isSelected(newValue, valuesArray);
  485. if (isSelected) {
  486. writer.writeAttribute("selected", true, "selected");
  487. }
  488. String labelClass = null;
  489. Boolean disabledAttr =
  490. (Boolean) component.getAttributes().get("disabled");
  491. boolean componentDisabled = false;
  492. if (disabledAttr != null) {
  493. if (disabledAttr.equals(Boolean.TRUE)) {
  494. componentDisabled = true;
  495. }
  496. }
  497. // if the component is disabled, "disabled" attribute would be rendered
  498. // on "select" tag, so don't render "disabled" on every option.
  499. if ((!componentDisabled) && curItem.isDisabled()) {
  500. writer.writeAttribute("disabled", true, "disabled");
  501. }
  502. if (componentDisabled || curItem.isDisabled()) {
  503. labelClass = (String) component.
  504. getAttributes().get("disabledClass");
  505. } else {
  506. labelClass = (String) component.
  507. getAttributes().get("enabledClass");
  508. }
  509. if (labelClass != null) {
  510. writer.writeAttribute("class", labelClass, "labelClass");
  511. }
  512. if (curItem.isEscape()) {
  513. String label = curItem.getLabel();
  514. if (label == null) {
  515. label = curItem.getValue().toString();
  516. }
  517. writer.writeText(label, component, "label");
  518. } else {
  519. writer.write(curItem.getLabel());
  520. }
  521. writer.endElement("option");
  522. writer.writeText("\n", component, null);
  523. }
  524. protected void writeDefaultSize(ResponseWriter writer, int itemCount)
  525. throws IOException {
  526. // if size is not specified default to 1.
  527. writer.writeAttribute("size", "1", "size");
  528. }
  529. // ------------------------------------------------- Package Private Methods
  530. boolean containsaValue(Object valueArray) {
  531. if (null != valueArray) {
  532. int len = Array.getLength(valueArray);
  533. for (int i = 0; i < len; i++) {
  534. Object value = Array.get(valueArray, i);
  535. if (value != null && !(value.equals(RIConstants.NO_VALUE))) {
  536. return true;
  537. }
  538. }
  539. }
  540. return false;
  541. }
  542. @SuppressWarnings("unchecked")
  543. Object getCurrentSelectedValues(FacesContext context,
  544. UIComponent component) {
  545. if (component instanceof UISelectMany) {
  546. UISelectMany select = (UISelectMany) component;
  547. Object value = select.getValue();
  548. if (value instanceof Collection) {
  549. Collection<?> list = (Collection) value;
  550. int size = list.size();
  551. if (size > 0) {
  552. String [] values = new String[list.size()];
  553. int index = 0;
  554. for (Object valueObject : list) {
  555. BeanWrapper wrapper = new BeanWrapperImpl(valueObject);
  556. String propertyValue = wrapper.getPropertyValue( "id" ).toString();
  557. values[index] = propertyValue;
  558. }
  559. return values;
  560. } else {
  561. return ((Collection) value).toArray();
  562. }
  563. }
  564. else if (value != null && !value.getClass().isArray()) {
  565. logger.warning(
  566. "The UISelectMany value should be an array or a collection type, the actual type is " +
  567. value.getClass().getName());
  568. }
  569. return value;
  570. }
  571. UISelectOne select = (UISelectOne) component;
  572. Object returnObject;
  573. if (null != (returnObject = select.getValue())) {
  574. String[] ret = new String[1];
  575. BeanWrapper wrapper = new BeanWrapperImpl(returnObject);
  576. String propertyValue = wrapper.getPropertyValue( "id" ).toString();
  577. ret[0] = propertyValue;
  578. return ret;
  579. }
  580. return null;
  581. }
  582. // To derive a selectOne type component from this, override
  583. // these methods.
  584. String getMultipleText(UIComponent component) {
  585. if (component instanceof UISelectMany) {
  586. return " multiple ";
  587. }
  588. return "";
  589. }
  590. @SuppressWarnings("unchecked")
  591. int getOptionNumber(FacesContext context, UIComponent component) {
  592. Iterator items = RenderKitUtils.getSelectItems(context, component);
  593. int itemCount = 0;
  594. while (items.hasNext()) {
  595. itemCount++;
  596. SelectItem item = (SelectItem) items.next();
  597. if (item instanceof SelectItemGroup) {
  598. int optionsLength =
  599. ((SelectItemGroup) item).getSelectItems().length;
  600. itemCount = itemCount + optionsLength;
  601. }
  602. }
  603. return itemCount;
  604. }
  605. Object[] getSubmittedSelectedValues(FacesContext context,
  606. UIComponent component) {
  607. if (component instanceof UISelectMany) {
  608. UISelectMany select = (UISelectMany) component;
  609. return (Object[]) select.getSubmittedValue();
  610. }
  611. UISelectOne select = (UISelectOne) component;
  612. Object returnObject;
  613. if (null != (returnObject = select.getSubmittedValue())) {
  614. return new Object[]{returnObject};
  615. }
  616. return null;
  617. }
  618. boolean isSelected(Object itemValue, Object valueArray) {
  619. if (null != valueArray) {
  620. if (!valueArray.getClass().isArray()) {
  621. logger.warning("valueArray is not an array, the actual type is " +
  622. valueArray.getClass());
  623. return valueArray.equals(itemValue);
  624. }
  625. int len = Array.getLength(valueArray);
  626. for (int i = 0; i < len; i++) {
  627. Object value = Array.get(valueArray, i);
  628. if (value == null) {
  629. if (itemValue == null) {
  630. return true;
  631. }
  632. } else if (value.equals(itemValue)) {
  633. return true;
  634. }
  635. }
  636. }
  637. return false;
  638. }
  639. @SuppressWarnings("unchecked")
  640. void renderOptions(FacesContext context, UIComponent component)
  641. throws IOException {
  642. ResponseWriter writer = context.getResponseWriter();
  643. assert(writer != null);
  644. Iterator items = RenderKitUtils.getSelectItems(context, component);
  645. SelectItem curItem = null;
  646. while (items.hasNext()) {
  647. curItem = (SelectItem) items.next();
  648. if (curItem instanceof SelectItemGroup) {
  649. // render OPTGROUP
  650. writer.startElement("optgroup", component);
  651. writer.writeAttribute("label", curItem.getLabel(), "label");
  652. // render options of this group.
  653. SelectItem[] itemsArray =
  654. ((SelectItemGroup) curItem).getSelectItems();
  655. for (int i = 0; i < itemsArray.length; ++i) {
  656. renderOption(context, component, itemsArray[i]);
  657. }
  658. writer.endElement("optgroup");
  659. } else {
  660. renderOption(context, component, curItem);
  661. }
  662. }
  663. }
  664. // Render the "select" portion..
  665. //
  666. void renderSelect(FacesContext context,
  667. UIComponent component) throws IOException {
  668. ResponseWriter writer = context.getResponseWriter();
  669. assert(writer != null);
  670. if (logger.isLoggable(Level.FINER)) {
  671. logger.log(Level.FINER, "Rendering 'select'");
  672. }
  673. writer.startElement("select", component);
  674. writeIdAttributeIfNecessary(context, writer, component);
  675. writer.writeAttribute("name", component.getClientId(context),
  676. "clientId");
  677. // render styleClass attribute if present.
  678. String styleClass = null;
  679. if (null !=
  680. (styleClass =
  681. (String) component.getAttributes().get("styleClass"))) {
  682. writer.writeAttribute("class", styleClass, "styleClass");
  683. }
  684. if (!getMultipleText(component).equals("")) {
  685. writer.writeAttribute("multiple", true, "multiple");
  686. }
  687. // Determine how many option(s) we need to render, and update
  688. // the component's "size" attribute accordingly; The "size"
  689. // attribute will be rendered as one of the "pass thru" attributes
  690. int itemCount = getOptionNumber(context, component);
  691. if (logger.isLoggable(Level.FINE)) {
  692. logger.fine("Rendering " + itemCount + " options");
  693. }
  694. // If "size" is *not* set explicitly, we have to default it correctly
  695. Integer size = Integer.getInteger((String)component.getAttributes().get("size"));
  696. if (size == null || size == Integer.MIN_VALUE) {
  697. //TODO: HACK... need to 'cifer why the size isn't getting processed correctly from the tag - Paul T.
  698. if (itemCount > 20) {
  699. size = 20;
  700. } else {
  701. size = itemCount;
  702. }
  703. }
  704. writeDefaultSize(writer, size);
  705. RenderKitUtils.renderPassThruAttributes(context,
  706. writer,
  707. component,
  708. new String[]{"size"});
  709. RenderKitUtils.renderXHTMLStyleBooleanAttributes(writer,
  710. component);
  711. // Now, render the "options" portion...
  712. renderOptions(context, component);
  713. writer.endElement("select");
  714. }
  715. } // end of class MenuRenderer