001/* 002 * (C) Copyright 2006-2013 Nuxeo SAS (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a> 016 * 017 * $Id$ 018 */ 019 020package org.nuxeo.ecm.platform.ui.web.tag.handler; 021 022import java.io.IOException; 023import java.util.ArrayList; 024import java.util.Collections; 025import java.util.List; 026 027import javax.el.ELException; 028import javax.faces.FacesException; 029import javax.faces.component.UIComponent; 030import javax.faces.model.DataModel; 031import javax.faces.model.ListDataModel; 032import javax.faces.view.facelets.ComponentConfig; 033import javax.faces.view.facelets.FaceletContext; 034import javax.faces.view.facelets.FaceletException; 035import javax.faces.view.facelets.FaceletHandler; 036import javax.faces.view.facelets.TagAttribute; 037import javax.faces.view.facelets.TagConfig; 038import javax.faces.view.facelets.TagHandler; 039 040import org.apache.commons.lang.StringUtils; 041 042import com.sun.faces.facelets.tag.TagAttributeImpl; 043import com.sun.faces.facelets.tag.TagAttributesImpl; 044import com.sun.faces.facelets.tag.jstl.core.ForEachHandler; 045 046/** 047 * Repeat handler, similar to the standard ForEach handler. 048 * <p> 049 * This component encapsulates a c:forEach tag inside a nxu:set tag, to be able to control when the sub-components 050 * should be re-created in the case of an ajax re-render. 051 * 052 * @author Anahide Tchertchian 053 */ 054public class RepeatTagHandler extends TagHandler { 055 056 @SuppressWarnings({ "unchecked", "rawtypes" }) 057 protected static final DataModel EMPTY_MODEL = new ListDataModel(Collections.emptyList()); 058 059 /** 060 * @since 5.7 061 */ 062 protected static final String ITERATION_VAR_PREFIX = "nxuRepeat_"; 063 064 /** 065 * @since 5.7 066 */ 067 protected final TagConfig config; 068 069 /** 070 * @deprecated, user {@link #items} instead 071 */ 072 @Deprecated 073 protected final TagAttribute value; 074 075 /** 076 * @since 5.7 077 */ 078 protected final TagAttribute items; 079 080 /** 081 * @since 5.7 082 */ 083 protected final TagAttribute itemsId; 084 085 protected final TagAttribute var; 086 087 protected final TagAttribute index; 088 089 /** 090 * @since 5.7 091 */ 092 protected final TagAttribute status; 093 094 /** 095 * @since 5.7 096 */ 097 protected final TagAttribute begin; 098 099 /** 100 * @since 5.7 101 */ 102 protected final TagAttribute end; 103 104 /** 105 * @since 5.7 106 */ 107 protected final TagAttribute step; 108 109 /** 110 * @since 5.7 111 */ 112 protected final TagAttribute tranzient; 113 114 /** 115 * @since 5.7 116 */ 117 protected final TagAttribute varStatus; 118 119 public RepeatTagHandler(TagConfig config) { 120 super(config); 121 this.config = config; 122 items = getAttribute("items"); 123 itemsId = getAttribute("itemsId"); 124 value = getAttribute("value"); 125 var = getAttribute("var"); 126 index = getAttribute("index"); 127 status = getAttribute("status"); 128 begin = getAttribute("begin"); 129 end = getAttribute("end"); 130 step = getAttribute("step"); 131 tranzient = getAttribute("transient"); 132 varStatus = getAttribute("varStatus"); 133 } 134 135 protected TagAttribute getItemsAttribute() { 136 TagAttribute itemsAttr = items; 137 if (items == null) { 138 // BBB 139 itemsAttr = value; 140 } 141 return itemsAttr; 142 } 143 144 protected String getTagConfigId(FaceletContext ctx) { 145 String refId = null; 146 if (itemsId != null) { 147 refId = itemsId.getValue(ctx); 148 } 149 150 if (StringUtils.isBlank(refId)) { 151 TagAttribute itemsAttr = getItemsAttribute(); 152 Object val = null; 153 if (itemsAttr != null) { 154 val = itemsAttr.getObject(ctx); 155 if (val != null) { 156 refId = val.toString(); 157 } 158 } 159 } 160 161 StringBuilder builder = new StringBuilder(); 162 if (refId != null) { 163 builder.append(refId); 164 } 165 builder.append(";"); 166 167 Integer intValue = new Integer(builder.toString().hashCode()); 168 return intValue.toString(); 169 } 170 171 /** 172 * Encapsulate the call to a c:forEach tag in an SetTagHandler exposing the value and making sure the tagConfigId 173 * changes when the value changes (see NXP-11434). 174 * <p> 175 * See also NXP-15050: since 6.0, anchor handler in component tree to ensure proper ajax refresh when iteration 176 * value changes. 177 */ 178 @Override 179 public void apply(FaceletContext ctx, UIComponent parent) throws IOException, FacesException, FaceletException, 180 ELException { 181 String anchor = String.valueOf(true); 182 FaceletHandler nextHandler = this.nextHandler; 183 TagAttribute varStatusAttr = varStatus; 184 if (index != null) { 185 // wrap the next handler in a set tag handler to expose the index 186 // value from the varStatus attribute. 187 String indexValue = index.getValue(ctx); 188 if (!StringUtils.isBlank(indexValue)) { 189 String varStatusValue = varStatus != null ? varStatus.getValue(ctx) : null; 190 if (StringUtils.isBlank(varStatusValue)) { 191 // need to create and set it as an attribute for the index 192 // to be exposed 193 varStatusAttr = createAttribute(config, "varStatus", 194 getVarName(String.format("%s_%s", indexValue, "varStatus"))); 195 } else { 196 varStatusAttr = createAttribute(config, "varStatus", varStatusValue); 197 } 198 ComponentConfig indexVarConfig = TagConfigFactory.createAliasTagConfig(config, tagId, 199 indexValue, String.format("#{%s.index}", varStatusAttr.getValue()), "false", anchor, 200 this.nextHandler); 201 nextHandler = new SetTagHandler(indexVarConfig); 202 } 203 } 204 205 List<TagAttribute> forEachAttrs = new ArrayList<TagAttribute>(); 206 forEachAttrs.add(createAttribute(config, "items", String.format("#{%s}", getVarName("items")))); 207 forEachAttrs.addAll(copyAttributes(config, var, begin, end, step, varStatusAttr, tranzient)); 208 TagConfig forEachConfig = TagConfigFactory.createTagConfig(config, tagId, new TagAttributesImpl( 209 forEachAttrs.toArray(new TagAttribute[] {})), nextHandler); 210 ForEachHandler forEachHandler = new ForEachHandler(forEachConfig); 211 212 String setTagConfigId = getTagConfigId(ctx); 213 TagAttribute itemsAttr = getItemsAttribute(); 214 ComponentConfig aliasConfig = TagConfigFactory.createAliasTagConfig(config, setTagConfigId, 215 getVarName("items"), itemsAttr != null ? itemsAttr.getValue() : null, "false", anchor, forEachHandler); 216 FaceletHandler handler = new SetTagHandler(aliasConfig); 217 218 // apply 219 handler.apply(ctx, parent); 220 } 221 222 protected String getVarName(String id) { 223 return String.format("%s%s", ITERATION_VAR_PREFIX, id); 224 } 225 226 protected TagAttribute createAttribute(TagConfig tagConfig, String name, String value) { 227 return new TagAttributeImpl(tagConfig.getTag().getLocation(), "", name, name, value); 228 } 229 230 protected TagAttribute copyAttribute(TagConfig tagConfig, TagAttribute attribute) { 231 return new TagAttributeImpl(tagConfig.getTag().getLocation(), "", attribute.getLocalName(), 232 attribute.getLocalName(), attribute.getValue()); 233 } 234 235 protected List<TagAttribute> copyAttributes(TagConfig tagConfig, TagAttribute... attributes) { 236 List<TagAttribute> res = new ArrayList<TagAttribute>(); 237 if (attributes != null) { 238 for (TagAttribute attr : attributes) { 239 if (attr != null) { 240 res.add(copyAttribute(tagConfig, attr)); 241 } 242 } 243 } 244 return res; 245 } 246 247}