All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.jsftemplating.layout.descriptors.LayoutForEach Maven / Gradle / Ivy

/*
 * The contents of this file are subject to the terms 
 * of the Common Development and Distribution License 
 * (the License).  You may not use this file except in
 * compliance with the License.
 * 
 * You can obtain a copy of the license at 
 * https://jsftemplating.dev.java.net/cddl1.html or
 * jsftemplating/cddl1.txt.
 * See the License for the specific language governing 
 * permissions and limitations under the License.
 * 
 * When distributing Covered Code, include this CDDL 
 * Header Notice in each file and include the License file 
 * at jsftemplating/cddl1.txt.  
 * If applicable, add the following below the CDDL Header, 
 * with the fields enclosed by brackets [] replaced by
 * you own identifying information: 
 * "Portions Copyrighted [year] [name of copyright owner]"
 * 
 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
 */
package com.sun.jsftemplating.layout.descriptors;

import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;

import com.sun.jsftemplating.layout.LayoutDefinitionManager;
import com.sun.jsftemplating.layout.descriptors.handler.Handler;
import com.sun.jsftemplating.layout.event.AfterLoopEvent;
import com.sun.jsftemplating.layout.event.BeforeLoopEvent;


/**
 *  

This class defines a LayoutForEach {@link LayoutElement}. The * LayoutForEach provides the functionality necessary to iteratively * display a portion of the layout tree. The list property contains * the List of items to iterate over.

* * @author Ken Paulsen ([email protected]) */ public class LayoutForEach extends LayoutComponent { private static final long serialVersionUID = 1L; /** *

Constructor.

* * @param parent The parent {@link LayoutElement} * @param listBinding The List to iterate over * @param key The ServletRequest attribute key * used to store the object being processed */ public LayoutForEach(LayoutElement parent, String listBinding, String key) { super(parent, null, LayoutDefinitionManager.getGlobalComponentType(null, "foreach")); if ((listBinding == null) || listBinding.equals("")) { throw new IllegalArgumentException("'listBinding' is required!"); } if ((key == null) || key.equals("")) { throw new IllegalArgumentException("'key' is required!"); } addOption("list", listBinding); addOption("key", key); if (listBinding.equals("$property{list}")) { _doubleEval = true; } } /** *

This method always returns true. The condition is based on an * Iterator.hasNext() call instead of here because * the {@link #encode(FacesContext, UIComponent)} method * evaluates this and then calls the super. Performing the check * here would cause the condition to be evaluated twice.

* * @param context The FacesContext. * @param component The UIComponent. * * @return true */ public boolean encodeThis(FacesContext context, UIComponent component) { return true; } /** *

This method evaluates the list binding for this * LayoutForEach. This is expected to evaulate to a * List object. If it doesn't, this method will throw a * NullPointerException (if it evaulates to * null), or an IllegalArgumentException if * it doesn't evaluate to a List.

* * @param context The FacesContext * * return The List of objects to iterate over */ protected List getList(FacesContext context, UIComponent comp) { Object value = resolveValue( context, comp, getOption("list")); if (_doubleEval) { // FIXME: Generalize double evaluation... all $property{} calls from inside a component?? value = resolveValue(context, comp, value); } // Make sure we found something... if (value == null) { throw new NullPointerException("List not found via expression: '" + getOption("list") + "'."); } // Make sure we have a List... if (!(value instanceof List)) { throw new IllegalArgumentException("Expression '" + getOption("list") + "' did not resolve to a List! Found: '" + value.getClass().getName() + "'"); } // Return the List return (List) value; } /** *

This method sets the Object that is currently being * processed by this LayoutForEach. This implementation * stores this value in the request attribute map undert the key * provided to this LayoutForEach.

* *

As an added convenience, this method will also set an attribute * that contains the current index number. The attribute key will be * the same key the Object is stored under plus "-index". * The index is stored as a String.

* * @param context The FacesContext * @param value The Object to store * @param index The current index number of the Object */ private void setCurrentForEachValue(FacesContext context, Object value, int index, String key) { Map map = context.getExternalContext().getRequestMap(); map.put(key, value); map.put(key + "-index", "" + index); } /** *

This implementation overrides the parent encode * method. It does this to cause the encode process to loop as long * as there are more List entries to process.

* * @param context The FacesContext * @param component The UIComponent */ public void encode(FacesContext context, UIComponent component) throws IOException { // Before events.. dispatchHandlers(context, BEFORE_LOOP, new BeforeLoopEvent(component)); String key = resolveValue( context, component, getOption("key")).toString(); // Get the List List list = getList(context, component); // Save the list size in case it is needed. context.getExternalContext().getRequestMap().put( key + "-size", list.size()); // Iterate over the values in the list and perform the requested // action(s) per the body of the LayoutForEach Iterator it = list.iterator(); for (int index = 1; it.hasNext(); index++) { setCurrentForEachValue(context, it.next(), index, key); super.encode(context, component); } // Invoke any "after" handlers dispatchHandlers(context, AFTER_LOOP, new AfterLoopEvent(component)); } /** *

This method retrieves the Handlers for the requested type. But * does *NOT* includes any handlers that are associated with the * instance (i.e. the UIComponent). This is desired behavior when * this is *not* a component. I am not sure if this is correct if * we support a foreach() component. FIXME: think about this.

*/ public List getHandlers(String type, UIComponent comp) { return super.getHandlers(type, null); } /** *

This is the event "type" for * {@link com.sun.jsftemplating.layout.descriptors.handler.Handler} * elements to be invoked after this LayoutForEach is processed * (outside loop).

*/ public static final String AFTER_LOOP = "afterLoop"; /** *

This is the event "type" for * {@link com.sun.jsftemplating.layout.descriptors.handler.Handler} * elements to be invoked before this LayoutForEach is processed * (outside loop).

*/ public static final String BEFORE_LOOP = "beforeLoop"; /** *

This flag is set to true when the condition equals * "$property{condition}". This is a special case where the value to * be evaluated is not $property{condition}, but rather the value of * this expression. This requires double evaluation to correct * interpret the expression. For now this is a hack for this case * only. In the future we may want to support an $eval{} or something * more general syntax for doing this declaratively.

* * See LayoutIf also. */ private boolean _doubleEval = false; }