/*
* (C) Copyright 2006-2013 Nuxeo SAS (http://nuxeo.com/) and contributors.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Lesser General Public License
* (LGPL) version 2.1 which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/lgpl.html
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* Contributors:
* <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a>
*
* $Id$
*/
package org.nuxeo.ecm.platform.ui.web.tag.handler;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.el.ELException;
import javax.faces.FacesException;
import javax.faces.component.UIComponent;
import javax.faces.model.DataModel;
import javax.faces.model.ListDataModel;
import javax.faces.view.facelets.ComponentConfig;
import javax.faces.view.facelets.FaceletContext;
import javax.faces.view.facelets.FaceletException;
import javax.faces.view.facelets.FaceletHandler;
import javax.faces.view.facelets.TagAttribute;
import javax.faces.view.facelets.TagConfig;
import javax.faces.view.facelets.TagHandler;
import org.apache.commons.lang.StringUtils;
import com.sun.faces.facelets.tag.TagAttributeImpl;
import com.sun.faces.facelets.tag.TagAttributesImpl;
import com.sun.faces.facelets.tag.jstl.core.ForEachHandler;
/**
* Repeat handler, similar to the standard ForEach handler.
* <p>
* This component encapsulates a c:forEach tag inside a nxu:set tag, to be able
* to control when the sub-components should be re-created in the case of an
* ajax re-render.
*
* @author Anahide Tchertchian
*/
public class RepeatTagHandler extends TagHandler {
@SuppressWarnings({ "unchecked", "rawtypes" })
protected static final DataModel EMPTY_MODEL = new ListDataModel(
Collections.emptyList());
/**
* @since 5.7
*/
protected static final String ITERATION_VAR_PREFIX = "nxuRepeat_";
/**
* @since 5.7
*/
protected final TagConfig config;
/**
* @deprecated, user {@link #items} instead
*/
@Deprecated
protected final TagAttribute value;
/**
* @since 5.7
*/
protected final TagAttribute items;
/**
* @since 5.7
*/
protected final TagAttribute itemsId;
protected final TagAttribute var;
protected final TagAttribute index;
/**
* @since 5.7
*/
protected final TagAttribute status;
/**
* @since 5.7
*/
protected final TagAttribute begin;
/**
* @since 5.7
*/
protected final TagAttribute end;
/**
* @since 5.7
*/
protected final TagAttribute step;
/**
* @since 5.7
*/
protected final TagAttribute tranzient;
/**
* @since 5.7
*/
protected final TagAttribute varStatus;
public RepeatTagHandler(TagConfig config) {
super(config);
this.config = config;
items = getAttribute("items");
itemsId = getAttribute("itemsId");
value = getAttribute("value");
var = getAttribute("var");
index = getAttribute("index");
status = getAttribute("status");
begin = getAttribute("begin");
end = getAttribute("end");
step = getAttribute("step");
tranzient = getAttribute("transient");
varStatus = getAttribute("varStatus");
}
protected TagAttribute getItemsAttribute() {
TagAttribute itemsAttr = items;
if (items == null) {
// BBB
itemsAttr = value;
}
return itemsAttr;
}
protected String getTagConfigId(FaceletContext ctx) {
String refId = null;
if (itemsId != null) {
refId = itemsId.getValue(ctx);
}
if (StringUtils.isBlank(refId)) {
TagAttribute itemsAttr = getItemsAttribute();
Object val = null;
if (itemsAttr != null) {
val = itemsAttr.getObject(ctx);
if (val != null) {
refId = val.toString();
}
}
}
StringBuilder builder = new StringBuilder();
if (refId != null) {
builder.append(refId);
}
builder.append(";");
Integer intValue = new Integer(builder.toString().hashCode());
return intValue.toString();
}
/**
* Encapsulate the call to a c:forEach tag in an SetTagHandler exposing the
* value and making sure the tagConfigId changes when the value changes
* (see NXP-11434).
* <p>
* See also NXP-15050: since 6.0, anchor handler in component tree to
* ensure proper ajax refresh when iteration value changes.
*/
public void apply(FaceletContext ctx, UIComponent parent)
throws IOException, FacesException, FaceletException, ELException {
String anchor = String.valueOf(true);
FaceletHandler nextHandler = this.nextHandler;
TagAttribute varStatusAttr = varStatus;
if (index != null) {
// wrap the next handler in a set tag handler to expose the index
// value from the varStatus attribute.
String indexValue = index.getValue(ctx);
if (!StringUtils.isBlank(indexValue)) {
String varStatusValue = varStatus != null ? varStatus.getValue(ctx)
: null;
if (StringUtils.isBlank(varStatusValue)) {
// need to create and set it as an attribute for the index
// to be exposed
varStatusAttr = createAttribute(this.config, "varStatus",
getVarName(String.format("%s_%s", indexValue,
"varStatus")));
} else {
varStatusAttr = createAttribute(this.config, "varStatus",
varStatusValue);
}
ComponentConfig indexVarConfig = TagConfigFactory.createAliasTagConfig(
this.config, this.tagId, indexValue,
String.format("#{%s.index}", varStatusAttr.getValue()),
"false", anchor, this.nextHandler);
nextHandler = new SetTagHandler(indexVarConfig);
}
}
List<TagAttribute> forEachAttrs = new ArrayList<TagAttribute>();
forEachAttrs.add(createAttribute(this.config, "items",
String.format("#{%s}", getVarName("items"))));
forEachAttrs.addAll(copyAttributes(this.config, var, begin, end, step,
varStatusAttr, tranzient));
TagConfig forEachConfig = TagConfigFactory.createTagConfig(
this.config,
this.tagId,
new TagAttributesImpl(
forEachAttrs.toArray(new TagAttribute[] {})),
nextHandler);
ForEachHandler forEachHandler = new ForEachHandler(forEachConfig);
String setTagConfigId = getTagConfigId(ctx);
TagAttribute itemsAttr = getItemsAttribute();
ComponentConfig aliasConfig = TagConfigFactory.createAliasTagConfig(
this.config, setTagConfigId, getVarName("items"),
itemsAttr != null ? itemsAttr.getValue() : null, "false",
anchor, forEachHandler);
FaceletHandler handler = new SetTagHandler(aliasConfig);
// apply
handler.apply(ctx, parent);
}
protected String getVarName(String id) {
return String.format("%s%s", ITERATION_VAR_PREFIX, id);
}
protected TagAttribute createAttribute(TagConfig tagConfig, String name,
String value) {
return new TagAttributeImpl(tagConfig.getTag().getLocation(), "", name,
name, value);
}
protected TagAttribute copyAttribute(TagConfig tagConfig,
TagAttribute attribute) {
return new TagAttributeImpl(tagConfig.getTag().getLocation(), "",
attribute.getLocalName(), attribute.getLocalName(),
attribute.getValue());
}
protected List<TagAttribute> copyAttributes(TagConfig tagConfig,
TagAttribute... attributes) {
List<TagAttribute> res = new ArrayList<TagAttribute>();
if (attributes != null) {
for (TagAttribute attr : attributes) {
if (attr != null) {
res.add(copyAttribute(tagConfig, attr));
}
}
}
return res;
}
}