1 /* 2 * $Id: SelectItemsIterator.java,v 1.18 2008/01/08 22:29:24 rlubke Exp $ 3 */ 4 5 /* 6 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 7 * 8 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. 9 * 10 * The contents of this file are subject to the terms of either the GNU 11 * General Public License Version 2 only ("GPL") or the Common Development 12 * and Distribution License("CDDL") (collectively, the "License"). You 13 * may not use this file except in compliance with the License. You can obtain 14 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html 15 * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific 16 * language governing permissions and limitations under the License. 17 * 18 * When distributing the software, include this License Header Notice in each 19 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt. 20 * Sun designates this particular file as subject to the "Classpath" exception 21 * as provided by Sun in the GPL Version 2 section of the License file that 22 * accompanied this code. If applicable, add the following below the License 23 * Header, with the fields enclosed by brackets [] replaced by your own 24 * identifying information: "Portions Copyrighted [year] 25 * [name of copyright owner]" 26 * 27 * Contributor(s): 28 * 29 * If you wish your version of this file to be governed by only the CDDL or 30 * only the GPL Version 2, indicate your decision by adding "[Contributor] 31 * elects to include this software in this distribution under the [CDDL or GPL 32 * Version 2] license." If you don't indicate a single choice of license, a 33 * recipient has the option to distribute your version of this file under 34 * either the CDDL, the GPL Version 2 or to extend the choice of license to 35 * its licensees as provided above. However, if you add GPL Version 2 code 36 * and therefore, elected the GPL Version 2 license, then the option applies 37 * only if the new code is made subject to such option by the copyright 38 * holder. 39 */ 40 41 package javax.faces.component; 42 43 import java.util.Iterator; 44 import java.util.NoSuchElementException; 45 import java.util.Map; 46 import java.util.ListIterator; 47 import java.io.ObjectOutputStream; 48 import java.io.IOException; 49 import java.io.NotSerializableException; 50 import java.io.ObjectInputStream; 51 import java.lang.reflect.Array; 52 53 import javax.faces.model.SelectItem; 54 import javax.faces.context.FacesContext; 55 import javax.el.ValueExpression; 56 57 58 /** 59 * <p>Package private class for iterating over the set of {@link SelectItem}s 60 * for a parent {@link UISelectMany} or {@link UISelectOne}.</p> 61 * 62 * // RELEASE_PENDING (rlubke,driscoll) performanc review 63 */ 64 final class SelectItemsIterator implements Iterator<SelectItem> { 65 66 67 // ------------------------------------------------------------ Constructors 68 69 70 /** 71 * <p>Construct an iterator instance for the specified parent component.</p> 72 * 73 * @param ctx the {@link FacesContext} for the current request 74 * @param parent The parent {@link UIComponent} whose children will be 75 * processed 76 */ 77 public SelectItemsIterator(FacesContext ctx, UIComponent parent) { 78 79 kids = parent.getChildren().listIterator(); 80 this.ctx = ctx; 81 82 } 83 84 85 // ------------------------------------------------------ Instance Variables 86 87 88 /** 89 * <p>Iterator over the SelectItem elements pointed at by a 90 * <code>UISelectItems</code> component, or <code>null</code>.</p> 91 */ 92 private Iterator<SelectItem> items; 93 94 95 /** 96 * <p>Iterator over the children of the parent component.</p> 97 */ 98 private ListIterator<UIComponent> kids; 99 100 101 /** 102 * Expose single SelectItems via an Iterator. This iterator will be 103 * reset/reused for each individual SelectItem instance encountered. 104 */ 105 private SingleElementIterator singleItemIterator; 106 107 108 /** 109 * The {@link FacesContext} for the current request. 110 */ 111 private FacesContext ctx; 112 113 114 // -------------------------------------------------------- Iterator Methods 115 116 117 /** 118 * <p>Return <code>true</code> if the iteration has more elements.</p> 119 */ 120 public boolean hasNext() { 121 122 if (items != null) { 123 if (items.hasNext()) { 124 return (true); 125 } else { 126 items = null; 127 } 128 } 129 Object next = findNextValidChild(); 130 while (next != null) { 131 initializeItems(next); 132 if (items != null) { 133 return true; 134 } else { 135 next = findNextValidChild(); 136 } 137 } 138 return false; 139 140 } 141 142 143 /** 144 * <p>Return the next element in the iteration.</p> 145 * 146 * @throws NoSuchElementException if there are no more elements 147 */ 148 @SuppressWarnings({"unchecked"}) 149 public SelectItem next() { 150 151 if (!hasNext()) { 152 throw new NoSuchElementException(); 153 } 154 if (items != null) { 155 return (items.next()); 156 } 157 return next(); 158 159 } 160 161 162 /** 163 * <p>Throw UnsupportedOperationException.</p> 164 */ 165 public void remove() { 166 167 throw new UnsupportedOperationException(); 168 169 } 170 171 172 // --------------------------------------------------------- Private Methods 173 174 175 /** 176 * <p> 177 * Initializes the <code>items</code> instance variable with an 178 * <code>Iterator</code> appropriate to the UISelectItem(s) value. 179 * </p> 180 */ 181 private void initializeItems(Object kid) { 182 183 if (kid instanceof UISelectItem) { 184 UISelectItem ui = (UISelectItem) kid; 185 SelectItem item = (SelectItem) ui.getValue(); 186 if (item == null) { 187 item = new SelectItem(ui.getItemValue(), 188 ui.getItemLabel(), 189 ui.getItemDescription(), 190 ui.isItemDisabled(), 191 ui.isItemEscaped(), 192 ui.isNoSelectionOption()); 193 } 194 updateSingeItemIterator(item); 195 items = singleItemIterator; 196 } else if (kid instanceof UISelectItems) { 197 UISelectItems ui = (UISelectItems) kid; 198 Object value = ui.getValue(); 199 if (value != null) { 200 if (value instanceof SelectItem) { 201 updateSingeItemIterator((SelectItem) value); 202 items = singleItemIterator; 203 } else if (value.getClass().isArray()) { 204 items = new ArrayIterator(ctx, (UISelectItems) kid, value); 205 } else if (value instanceof Iterable) { 206 items = new IterableItemIterator(ctx, 207 (UISelectItems) kid, 208 (Iterable<?>) value); 209 } else if (value instanceof Map) { 210 items = new MapIterator((Map) value); 211 } else { 212 throw new IllegalArgumentException(); 213 } 214 } 215 if (items != null && !items.hasNext()) { 216 items = null; 217 } 218 } 219 220 } 221 222 223 /** 224 * @return the next valid child for processing 225 */ 226 private Object findNextValidChild() { 227 228 if (kids.hasNext()) { 229 Object next = kids.next(); 230 while (kids.hasNext() && !(next instanceof UISelectItem || next instanceof UISelectItems)) { 231 next = kids.next(); 232 } 233 if (next instanceof UISelectItem || next instanceof UISelectItems) { 234 return next; 235 } 236 } 237 return null; 238 239 } 240 241 242 /** 243 * Update the <code>singleItemIterator</code> with the provided 244 * <code>item</code> 245 * @param item the {@link SelectItem} to expose as an Iterator 246 */ 247 private void updateSingeItemIterator(SelectItem item) { 248 249 if (singleItemIterator == null) { 250 singleItemIterator = new SingleElementIterator(); 251 } 252 singleItemIterator.updateItem(item); 253 254 } 255 256 257 // ---------------------------------------------------------- Nested Classes 258 259 260 /** 261 * Exposes single {@link SelectItem} instances as an Iterator. 262 */ 263 private static final class SingleElementIterator implements Iterator<SelectItem> { 264 265 private SelectItem item; 266 private boolean nextCalled; 267 268 269 // ----------------------------------------------- Methods from Iterator 270 271 272 public boolean hasNext() { 273 274 return !nextCalled; 275 276 } 277 278 279 public SelectItem next() { 280 281 if (nextCalled) { 282 throw new NoSuchElementException(); 283 } 284 nextCalled = true; 285 return item; 286 287 } 288 289 290 public void remove() { 291 292 throw new UnsupportedOperationException(); 293 294 } 295 296 297 // ----------------------------------------------------- Private Methods 298 299 300 private void updateItem(SelectItem item) { 301 302 this.item = item; 303 nextCalled = false; 304 305 } 306 307 } // END SingleElementIterator 308 309 310 /** 311 * Iterates over a <code>Map</code> of values exposing each entry as a SelectItem. 312 * Note that this will do so re-using the same SelectItem but changing 313 * the value and label as appropriate. 314 */ 315 private static final class MapIterator implements Iterator<SelectItem> { 316 317 private SelectItem item = new SelectItem(); 318 private Iterator iterator; 319 320 321 // -------------------------------------------------------- Constructors 322 323 324 private MapIterator(Map map) { 325 326 this.iterator = map.entrySet().iterator(); 327 328 } 329 330 331 // ----------------------------------------------- Methods from Iterator 332 333 334 public boolean hasNext() { 335 336 return iterator.hasNext(); 337 338 } 339 340 341 public SelectItem next() { 342 343 Map.Entry entry = (Map.Entry) iterator.next(); 344 Object key = entry.getKey(); 345 Object value = entry.getValue(); 346 item.setLabel(((key != null) ? key.toString() : value.toString())); 347 item.setValue(((value != null) ? value : "")); 348 return item; 349 350 } 351 352 353 public void remove() { 354 355 throw new UnsupportedOperationException(); 356 357 } 358 359 } // END MapIterator 360 361 362 /** 363 * <p> 364 * Base class to support iterating over Collections or Arrays that may 365 * or may not contain <code>SelectItem</code> instances. 366 * </p> 367 */ 368 private static abstract class GenericObjectSelectItemIterator implements Iterator<SelectItem> { 369 370 /** 371 * SelectItem that is updated based on the current Object being 372 * iterated over. 373 */ 374 private GenericObjectSelectItem genericObjectSI; 375 376 /** 377 * The source <code>UISelectItems</code>. 378 */ 379 protected UISelectItems sourceComponent; 380 381 382 // -------------------------------------------------------- Constructors 383 384 385 protected GenericObjectSelectItemIterator(UISelectItems sourceComponent) { 386 387 this.sourceComponent = sourceComponent; 388 389 } 390 391 392 // --------------------------------------------------- Protected Methods 393 394 395 protected SelectItem getSelectItemFor(FacesContext ctx, Object value) { 396 397 if (genericObjectSI == null) { 398 genericObjectSI = new GenericObjectSelectItem(sourceComponent); 399 } 400 401 genericObjectSI.updateItem(ctx, value); 402 return genericObjectSI; 403 404 } 405 406 407 // ------------------------------------------------------ Nested Classes 408 409 410 /** 411 * A <code>SelectItem</code> implementation to support generating 412 * unique <code>SelectItem</code> values based on <code>ValueExpressions</code> 413 * from the owning {@link UISelectItems} instance. 414 */ 415 @SuppressWarnings({"serial"}) 416 private static final class GenericObjectSelectItem extends SelectItem { 417 418 419 private static final String VAR = "var"; 420 private static final String ITEM_VALUE = "itemValue"; 421 private static final String ITEM_LABEL = "itemLabel"; 422 private static final String ITEM_DESCRIPTION = "itemDescription"; 423 private static final String ITEM_ESCAPED = "itemLabelEscaped"; 424 private static final String ITEM_DISABLED = "itemDisabled"; 425 private static final String NO_SELECTION_OPTION = "noSelectionOption"; 426 427 /** 428 * Resolves to the value of the <code>SelectItem</code>. 429 */ 430 private ValueExpression itemValue; 431 432 /** 433 * Resolves to the label of the <code>SelectItem</code>. 434 */ 435 private ValueExpression itemLabel; 436 437 /** 438 * Resolves to the description of the <code>SelectItem</code>. 439 */ 440 private ValueExpression itemDescription; 441 442 /** 443 * Determines the value for the escaped property of the <code>SelectItem</code>. 444 */ 445 private ValueExpression itemEscaped; 446 447 /** 448 * Determines the value for the disabled property of the <code>SelectItem</code>/ 449 */ 450 private ValueExpression itemDisabled; 451 452 /** 453 * Determines the value for the noSelectionOption property of the <code>SelectItem</code>/ 454 */ 455 private ValueExpression noSelectionOption; 456 457 /** 458 * The request-scoped variable under which the current object 459 * will be exposed. 460 */ 461 private String var; 462 463 private UISelectItems sourceComponent; 464 465 // -------------------------------------------------------- Constructors 466 467 468 private GenericObjectSelectItem(UISelectItems sourceComponent) { 469 470 var = (String) sourceComponent.getAttributes().get(VAR); 471 this.sourceComponent = sourceComponent; 472 //itemValue = sourceComponent.getValueExpression(ITEM_VALUE); 473 //itemLabel = sourceComponent.getValueExpression(ITEM_LABEL); 474 //itemDescription = sourceComponent.getValueExpression(ITEM_DESCRIPTION); 475 //itemEscaped = sourceComponent.getValueExpression(ITEM_ESCAPED); 476 //itemDisabled = sourceComponent.getValueExpression(ITEM_DISABLED); 477 //noSelectionOption = sourceComponent.getValueExpression(NO_SELECTION_OPTION); 478 479 } 480 481 // ----------------------------------------------------- Private Methods 482 483 484 /** 485 * Updates the <code>SelectItem</code> properties based on the 486 * current value. 487 * 488 * @param ctx the {@link FacesContext} for the current request 489 * @param value the value to build the updated values from 490 */ 491 private void updateItem(FacesContext ctx, Object value) { 492 493 Map<String, Object> reqMap = 494 ctx.getExternalContext().getRequestMap(); 495 Object oldVarValue = null; 496 if (var != null) { 497 oldVarValue = reqMap.put(var, value); 498 } 499 try { 500 Map<String,Object> attrs = sourceComponent.getAttributes(); 501 Object itemValueResult = attrs.get(ITEM_VALUE); 502 Object itemLabelResult = attrs.get(ITEM_LABEL); 503 Object itemDescriptionResult = attrs.get(ITEM_DESCRIPTION); 504 Object itemEscapedResult = attrs.get(ITEM_ESCAPED); 505 Object itemDisabledResult = attrs.get(ITEM_DISABLED); 506 Object noSelectionOptionResult = attrs.get(NO_SELECTION_OPTION); 507 setValue(((itemValueResult != null) ? itemValueResult : value)); 508 setLabel(((itemLabelResult != null) 509 ? itemLabelResult.toString() 510 : value.toString())); 511 setDescription(((itemDescriptionResult != null) 512 ? itemDescriptionResult.toString() 513 : null)); 514 setEscape(((itemEscapedResult != null) 515 ? Boolean.valueOf(itemEscapedResult.toString()) 516 : false)); 517 setDisabled(((itemDisabledResult != null) 518 ? Boolean.valueOf(itemDisabledResult.toString()) 519 : false)); 520 setNoSelectionOption(((noSelectionOptionResult != null) 521 ? Boolean.valueOf(noSelectionOptionResult.toString()) 522 : false)); 523 } finally { 524 if (var != null) { 525 if (oldVarValue != null) { 526 reqMap.put(var, oldVarValue); 527 } else { 528 reqMap.remove(var); 529 } 530 } 531 } 532 533 } 534 535 536 // --------------------------------------- Methods from Serializable 537 538 539 private void writeObject(ObjectOutputStream out) throws IOException { 540 541 throw new NotSerializableException(); 542 543 } 544 545 546 private void readObject(ObjectInputStream in) throws IOException { 547 548 throw new NotSerializableException(); 549 550 } 551 552 } // END GenericObjectSelectItem 553 554 } // END GenericObjectSelectItemIterator 555 556 557 /** 558 * Handles arrays of <code>SelectItem</code>s, generic Objects, 559 * or combintations of both. 560 * 561 * A single <code>GenericObjectSelectItem</code> will be leverage for any 562 * non-<code>SelectItem</code> objects encountered. 563 */ 564 private static final class ArrayIterator extends GenericObjectSelectItemIterator { 565 566 567 private FacesContext ctx; 568 private Object array; 569 private int count; 570 private int index; 571 572 573 // -------------------------------------------------------- Constructors 574 575 576 private ArrayIterator(FacesContext ctx, 577 UISelectItems sourceComponent, 578 Object array) { 579 580 super(sourceComponent); 581 this.ctx = ctx; 582 this.array = array; 583 count = Array.getLength(array); 584 585 } 586 587 588 // ----------------------------------------------- Methods from Iterator 589 590 591 public boolean hasNext() { 592 593 return (index < count); 594 595 } 596 597 public SelectItem next() { 598 599 if (index >= count) { 600 throw new NoSuchElementException(); 601 } 602 603 Object item = Array.get(array, index++); 604 if (item instanceof SelectItem) { 605 return (SelectItem) item; 606 } else { 607 return getSelectItemFor(ctx, item); 608 } 609 610 } 611 612 public void remove() { 613 throw new UnsupportedOperationException(); 614 } 615 616 617 } // END ArrayIterator 618 619 620 /** 621 * Handles Collections of <code>SelectItem</code>s, generic Objects, 622 * or combintations of both. 623 * 624 * A single <code>GenericObjectSelectItem</code> will be leverage for any 625 * non-<code>SelectItem</code> objects encountered. 626 */ 627 private static final class IterableItemIterator extends GenericObjectSelectItemIterator { 628 629 630 private FacesContext ctx; 631 private Iterator<?> iterator; 632 633 634 // -------------------------------------------------------- Constructors 635 636 637 private IterableItemIterator(FacesContext ctx, 638 UISelectItems sourceComponent, 639 Iterable<?> iterable) { 640 641 super(sourceComponent); 642 this.ctx = ctx; 643 this.iterator = iterable.iterator(); 644 645 } 646 647 648 // ----------------------------------------------- Methods from Iterator 649 650 651 public boolean hasNext() { 652 653 return iterator.hasNext(); 654 655 } 656 657 658 public SelectItem next() { 659 660 Object item = iterator.next(); 661 if (item instanceof SelectItem) { 662 return (SelectItem) item; 663 } else { 664 return getSelectItemFor(ctx, item); 665 } 666 667 } 668 669 670 public void remove() { 671 672 throw new UnsupportedOperationException(); 673 674 } 675 676 } // END CollectionItemIterator 677 678 679 }