/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package javax.faces.component;
import java.util.
Iterator;
import java.util.
NoSuchElementException;
import java.util.
Map;
import java.util.
ListIterator;
import java.io.
ObjectOutputStream;
import java.io.
IOException;
import java.io.
NotSerializableException;
import java.io.
ObjectInputStream;
import java.lang.reflect.
Array;
import javax.faces.model.
SelectItem;
import javax.faces.context.
FacesContext;
import javax.el.
ValueExpression;
/**
* <p>Package private class for iterating over the set of {@link SelectItem}s
* for a parent {@link UISelectMany} or {@link UISelectOne}.</p>
*
* // RELEASE_PENDING (rlubke,driscoll) performanc review
*/
final class
SelectItemsIterator implements
Iterator<
SelectItem> {
// ------------------------------------------------------------ Constructors
/**
* <p>Construct an iterator instance for the specified parent component.</p>
*
* @param ctx the {@link FacesContext} for the current request
* @param parent The parent {@link UIComponent} whose children will be
* processed
*/
public
SelectItemsIterator(
FacesContext ctx,
UIComponent parent) {
kids =
parent.
getChildren().
listIterator();
this.
ctx =
ctx;
}
// ------------------------------------------------------ Instance Variables
/**
* <p>Iterator over the SelectItem elements pointed at by a
* <code>UISelectItems</code> component, or <code>null</code>.</p>
*/
private
Iterator<
SelectItem>
items;
/**
* <p>Iterator over the children of the parent component.</p>
*/
private
ListIterator<
UIComponent>
kids;
/**
* Expose single SelectItems via an Iterator. This iterator will be
* reset/reused for each individual SelectItem instance encountered.
*/
private
SingleElementIterator singleItemIterator;
/**
* The {@link FacesContext} for the current request.
*/
private
FacesContext ctx;
// -------------------------------------------------------- Iterator Methods
/**
* <p>Return <code>true</code> if the iteration has more elements.</p>
*/
public boolean
hasNext() {
if (
items != null) {
if (
items.
hasNext()) {
return (true);
} else {
items = null;
}
}
Object next =
findNextValidChild();
while (
next != null) {
initializeItems(
next);
if (
items != null) {
return true;
} else {
next =
findNextValidChild();
}
}
return false;
}
/**
* <p>Return the next element in the iteration.</p>
*
* @throws NoSuchElementException if there are no more elements
*/
@
SuppressWarnings({"unchecked"})
public
SelectItem next() {
if (!
hasNext()) {
throw new
NoSuchElementException();
}
if (
items != null) {
return (
items.
next());
}
return
next();
}
/**
* <p>Throw UnsupportedOperationException.</p>
*/
public void
remove() {
throw new
UnsupportedOperationException();
}
// --------------------------------------------------------- Private Methods
/**
* <p>
* Initializes the <code>items</code> instance variable with an
* <code>Iterator</code> appropriate to the UISelectItem(s) value.
* </p>
*/
private void
initializeItems(
Object kid) {
if (
kid instanceof
UISelectItem) {
UISelectItem ui = (
UISelectItem)
kid;
SelectItem item = (
SelectItem)
ui.
getValue();
if (
item == null) {
item = new
SelectItem(
ui.
getItemValue(),
ui.
getItemLabel(),
ui.
getItemDescription(),
ui.
isItemDisabled(),
ui.
isItemEscaped(),
ui.
isNoSelectionOption());
}
updateSingeItemIterator(
item);
items =
singleItemIterator;
} else if (
kid instanceof
UISelectItems) {
UISelectItems ui = (
UISelectItems)
kid;
Object value =
ui.
getValue();
if (
value != null) {
if (
value instanceof
SelectItem) {
updateSingeItemIterator((
SelectItem)
value);
items =
singleItemIterator;
} else if (
value.
getClass().
isArray()) {
items = new
ArrayIterator(
ctx, (
UISelectItems)
kid,
value);
} else if (
value instanceof
Iterable) {
items = new
IterableItemIterator(
ctx,
(
UISelectItems)
kid,
(
Iterable<?>)
value);
} else if (
value instanceof
Map) {
items = new
MapIterator((
Map)
value);
} else {
throw new
IllegalArgumentException();
}
}
if (
items != null && !
items.
hasNext()) {
items = null;
}
}
}
/**
* @return the next valid child for processing
*/
private
Object findNextValidChild() {
if (
kids.
hasNext()) {
Object next =
kids.
next();
while (
kids.
hasNext() && !(
next instanceof
UISelectItem ||
next instanceof
UISelectItems)) {
next =
kids.
next();
}
if (
next instanceof
UISelectItem ||
next instanceof
UISelectItems) {
return
next;
}
}
return null;
}
/**
* Update the <code>singleItemIterator</code> with the provided
* <code>item</code>
* @param item the {@link SelectItem} to expose as an Iterator
*/
private void
updateSingeItemIterator(
SelectItem item) {
if (
singleItemIterator == null) {
singleItemIterator = new
SingleElementIterator();
}
singleItemIterator.
updateItem(
item);
}
// ---------------------------------------------------------- Nested Classes
/**
* Exposes single {@link SelectItem} instances as an Iterator.
*/
private static final class
SingleElementIterator implements
Iterator<
SelectItem> {
private
SelectItem item;
private boolean
nextCalled;
// ----------------------------------------------- Methods from Iterator
public boolean
hasNext() {
return !
nextCalled;
}
public
SelectItem next() {
if (
nextCalled) {
throw new
NoSuchElementException();
}
nextCalled = true;
return
item;
}
public void
remove() {
throw new
UnsupportedOperationException();
}
// ----------------------------------------------------- Private Methods
private void
updateItem(
SelectItem item) {
this.
item =
item;
nextCalled = false;
}
} // END SingleElementIterator
/**
* Iterates over a <code>Map</code> of values exposing each entry as a SelectItem.
* Note that this will do so re-using the same SelectItem but changing
* the value and label as appropriate.
*/
private static final class
MapIterator implements
Iterator<
SelectItem> {
private
SelectItem item = new
SelectItem();
private
Iterator iterator;
// -------------------------------------------------------- Constructors
private
MapIterator(
Map map) {
this.
iterator =
map.
entrySet().
iterator();
}
// ----------------------------------------------- Methods from Iterator
public boolean
hasNext() {
return
iterator.
hasNext();
}
public
SelectItem next() {
Map.
Entry entry = (
Map.
Entry)
iterator.
next();
Object key =
entry.
getKey();
Object value =
entry.
getValue();
item.
setLabel(((
key != null) ?
key.
toString() :
value.
toString()));
item.
setValue(((
value != null) ?
value : ""));
return
item;
}
public void
remove() {
throw new
UnsupportedOperationException();
}
} // END MapIterator
/**
* <p>
* Base class to support iterating over Collections or Arrays that may
* or may not contain <code>SelectItem</code> instances.
* </p>
*/
private static abstract class
GenericObjectSelectItemIterator implements
Iterator<
SelectItem> {
/**
* SelectItem that is updated based on the current Object being
* iterated over.
*/
private
GenericObjectSelectItem genericObjectSI;
/**
* The source <code>UISelectItems</code>.
*/
protected
UISelectItems sourceComponent;
// -------------------------------------------------------- Constructors
protected
GenericObjectSelectItemIterator(
UISelectItems sourceComponent) {
this.
sourceComponent =
sourceComponent;
}
// --------------------------------------------------- Protected Methods
protected
SelectItem getSelectItemFor(
FacesContext ctx,
Object value) {
if (
genericObjectSI == null) {
genericObjectSI = new
GenericObjectSelectItem(
sourceComponent);
}
genericObjectSI.
updateItem(
ctx,
value);
return
genericObjectSI;
}
// ------------------------------------------------------ Nested Classes
/**
* A <code>SelectItem</code> implementation to support generating
* unique <code>SelectItem</code> values based on <code>ValueExpressions</code>
* from the owning {@link UISelectItems} instance.
*/
@
SuppressWarnings({"serial"})
private static final class
GenericObjectSelectItem extends
SelectItem {
private static final
String VAR = "var";
private static final
String ITEM_VALUE = "itemValue";
private static final
String ITEM_LABEL = "itemLabel";
private static final
String ITEM_DESCRIPTION = "itemDescription";
private static final
String ITEM_ESCAPED = "itemLabelEscaped";
private static final
String ITEM_DISABLED = "itemDisabled";
private static final
String NO_SELECTION_OPTION = "noSelectionOption";
/**
* Resolves to the value of the <code>SelectItem</code>.
*/
private
ValueExpression itemValue;
/**
* Resolves to the label of the <code>SelectItem</code>.
*/
private
ValueExpression itemLabel;
/**
* Resolves to the description of the <code>SelectItem</code>.
*/
private
ValueExpression itemDescription;
/**
* Determines the value for the escaped property of the <code>SelectItem</code>.
*/
private
ValueExpression itemEscaped;
/**
* Determines the value for the disabled property of the <code>SelectItem</code>/
*/
private
ValueExpression itemDisabled;
/**
* Determines the value for the noSelectionOption property of the <code>SelectItem</code>/
*/
private
ValueExpression noSelectionOption;
/**
* The request-scoped variable under which the current object
* will be exposed.
*/
private
String var;
private
UISelectItems sourceComponent;
// -------------------------------------------------------- Constructors
private
GenericObjectSelectItem(
UISelectItems sourceComponent) {
var = (
String)
sourceComponent.
getAttributes().
get(
VAR);
this.
sourceComponent =
sourceComponent;
//itemValue = sourceComponent.getValueExpression(ITEM_VALUE);
//itemLabel = sourceComponent.getValueExpression(ITEM_LABEL);
//itemDescription = sourceComponent.getValueExpression(ITEM_DESCRIPTION);
//itemEscaped = sourceComponent.getValueExpression(ITEM_ESCAPED);
//itemDisabled = sourceComponent.getValueExpression(ITEM_DISABLED);
//noSelectionOption = sourceComponent.getValueExpression(NO_SELECTION_OPTION);
}
// ----------------------------------------------------- Private Methods
/**
* Updates the <code>SelectItem</code> properties based on the
* current value.
*
* @param ctx the {@link FacesContext} for the current request
* @param value the value to build the updated values from
*/
private void
updateItem(
FacesContext ctx,
Object value) {
Map<
String,
Object>
reqMap =
ctx.
getExternalContext().
getRequestMap();
Object oldVarValue = null;
if (
var != null) {
oldVarValue =
reqMap.
put(
var,
value);
}
try {
Map<
String,
Object>
attrs =
sourceComponent.
getAttributes();
Object itemValueResult =
attrs.
get(
ITEM_VALUE);
Object itemLabelResult =
attrs.
get(
ITEM_LABEL);
Object itemDescriptionResult =
attrs.
get(
ITEM_DESCRIPTION);
Object itemEscapedResult =
attrs.
get(
ITEM_ESCAPED);
Object itemDisabledResult =
attrs.
get(
ITEM_DISABLED);
Object noSelectionOptionResult =
attrs.
get(
NO_SELECTION_OPTION);
setValue(((
itemValueResult != null) ?
itemValueResult :
value));
setLabel(((
itemLabelResult != null)
?
itemLabelResult.
toString()
:
value.
toString()));
setDescription(((
itemDescriptionResult != null)
?
itemDescriptionResult.
toString()
: null));
setEscape(((
itemEscapedResult != null)
?
Boolean.
valueOf(
itemEscapedResult.
toString())
: false));
setDisabled(((
itemDisabledResult != null)
?
Boolean.
valueOf(
itemDisabledResult.
toString())
: false));
setNoSelectionOption(((
noSelectionOptionResult != null)
?
Boolean.
valueOf(
noSelectionOptionResult.
toString())
: false));
} finally {
if (
var != null) {
if (
oldVarValue != null) {
reqMap.
put(
var,
oldVarValue);
} else {
reqMap.
remove(
var);
}
}
}
}
// --------------------------------------- Methods from Serializable
private void
writeObject(
ObjectOutputStream out) throws
IOException {
throw new
NotSerializableException();
}
private void
readObject(
ObjectInputStream in) throws
IOException {
throw new
NotSerializableException();
}
} // END GenericObjectSelectItem
} // END GenericObjectSelectItemIterator
/**
* Handles arrays of <code>SelectItem</code>s, generic Objects,
* or combintations of both.
*
* A single <code>GenericObjectSelectItem</code> will be leverage for any
* non-<code>SelectItem</code> objects encountered.
*/
private static final class
ArrayIterator extends
GenericObjectSelectItemIterator {
private
FacesContext ctx;
private
Object array;
private int
count;
private int
index;
// -------------------------------------------------------- Constructors
private
ArrayIterator(
FacesContext ctx,
UISelectItems sourceComponent,
Object array) {
super(
sourceComponent);
this.
ctx =
ctx;
this.
array =
array;
count =
Array.
getLength(
array);
}
// ----------------------------------------------- Methods from Iterator
public boolean
hasNext() {
return (
index <
count);
}
public
SelectItem next() {
if (
index >=
count) {
throw new
NoSuchElementException();
}
Object item =
Array.
get(
array,
index++);
if (
item instanceof
SelectItem) {
return (
SelectItem)
item;
} else {
return
getSelectItemFor(
ctx,
item);
}
}
public void
remove() {
throw new
UnsupportedOperationException();
}
} // END ArrayIterator
/**
* Handles Collections of <code>SelectItem</code>s, generic Objects,
* or combintations of both.
*
* A single <code>GenericObjectSelectItem</code> will be leverage for any
* non-<code>SelectItem</code> objects encountered.
*/
private static final class
IterableItemIterator extends
GenericObjectSelectItemIterator {
private
FacesContext ctx;
private
Iterator<?>
iterator;
// -------------------------------------------------------- Constructors
private
IterableItemIterator(
FacesContext ctx,
UISelectItems sourceComponent,
Iterable<?>
iterable) {
super(
sourceComponent);
this.
ctx =
ctx;
this.
iterator =
iterable.
iterator();
}
// ----------------------------------------------- Methods from Iterator
public boolean
hasNext() {
return
iterator.
hasNext();
}
public
SelectItem next() {
Object item =
iterator.
next();
if (
item instanceof
SelectItem) {
return (
SelectItem)
item;
} else {
return
getSelectItemFor(
ctx,
item);
}
}
public void
remove() {
throw new
UnsupportedOperationException();
}
} // END CollectionItemIterator
}