001/* 002 * (C) Copyright 2006-2013 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a> 018 * 019 * $Id$ 020 */ 021 022package org.nuxeo.ecm.platform.ui.web.tag.handler; 023 024import java.io.IOException; 025import java.util.ArrayList; 026import java.util.List; 027 028import javax.el.ELException; 029import javax.faces.FacesException; 030import javax.faces.component.UIComponent; 031import javax.faces.view.facelets.ComponentConfig; 032import javax.faces.view.facelets.ComponentHandler; 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.component.UIRepeat; 043import com.sun.faces.facelets.tag.TagAttributeImpl; 044import com.sun.faces.facelets.tag.TagAttributesImpl; 045import com.sun.faces.facelets.tag.jstl.core.ForEachHandler; 046 047/** 048 * Repeat handler, similar to the standard ForEach handler. 049 * <p> 050 * This component encapsulates a c:forEach tag inside a nxu:set tag, to be able to control when the sub-components 051 * should be re-created in the case of an ajax re-render. 052 * 053 * @author Anahide Tchertchian 054 */ 055public class RepeatTagHandler extends TagHandler { 056 057 /** 058 * @since 5.7 059 */ 060 protected static final String ITERATION_VAR_PREFIX = "nxuRepeat_"; 061 062 /** 063 * @since 5.7 064 */ 065 protected final TagConfig config; 066 067 /** 068 * @deprecated, user {@link #items} instead 069 */ 070 @Deprecated 071 protected final TagAttribute value; 072 073 /** 074 * @since 5.7 075 */ 076 protected final TagAttribute items; 077 078 /** 079 * @since 5.7 080 */ 081 protected final TagAttribute itemsId; 082 083 protected final TagAttribute var; 084 085 protected final TagAttribute index; 086 087 /** 088 * @since 5.7 089 */ 090 protected final TagAttribute status; 091 092 /** 093 * @since 5.7 094 */ 095 protected final TagAttribute begin; 096 097 /** 098 * @since 5.7 099 */ 100 protected final TagAttribute end; 101 102 /** 103 * @since 5.7 104 */ 105 protected final TagAttribute step; 106 107 /** 108 * @since 5.7 109 */ 110 protected final TagAttribute tranzient; 111 112 /** 113 * @since 5.7 114 */ 115 protected final TagAttribute varStatus; 116 117 /** 118 * @since 8.2 119 */ 120 protected final TagAttribute id; 121 122 /** 123 * @since 8.2 124 */ 125 protected final TagAttribute renderTime; 126 127 public RepeatTagHandler(TagConfig config) { 128 super(config); 129 this.config = config; 130 id = getAttribute("id"); 131 items = getAttribute("items"); 132 itemsId = getAttribute("itemsId"); 133 value = getAttribute("value"); 134 var = getAttribute("var"); 135 index = getAttribute("index"); 136 status = getAttribute("status"); 137 begin = getAttribute("begin"); 138 end = getAttribute("end"); 139 step = getAttribute("step"); 140 tranzient = getAttribute("transient"); 141 varStatus = getAttribute("varStatus"); 142 renderTime = getAttribute("renderTime"); 143 } 144 145 protected TagAttribute getItemsAttribute() { 146 TagAttribute itemsAttr = items; 147 if (items == null) { 148 // BBB 149 itemsAttr = value; 150 } 151 return itemsAttr; 152 } 153 154 protected String getTagConfigId(FaceletContext ctx) { 155 String refId = null; 156 if (itemsId != null) { 157 refId = itemsId.getValue(ctx); 158 } 159 160 if (StringUtils.isBlank(refId)) { 161 TagAttribute itemsAttr = getItemsAttribute(); 162 Object val = null; 163 if (itemsAttr != null) { 164 val = itemsAttr.getObject(ctx); 165 if (val != null) { 166 refId = val.toString(); 167 } 168 } 169 } 170 171 StringBuilder builder = new StringBuilder(); 172 if (refId != null) { 173 builder.append(refId); 174 } 175 builder.append(";"); 176 177 Integer intValue = new Integer(builder.toString().hashCode()); 178 return intValue.toString(); 179 } 180 181 /** 182 * Encapsulate the call to a c:forEach tag in an SetTagHandler exposing the value and making sure the tagConfigId 183 * changes when the value changes (see NXP-11434). 184 * <p> 185 * See also NXP-15050: since 6.0, anchor handler in component tree to ensure proper ajax refresh when iteration 186 * value changes. 187 */ 188 @Override 189 public void apply(FaceletContext ctx, UIComponent parent) 190 throws IOException, FacesException, FaceletException, ELException { 191 String anchor = String.valueOf(true); 192 FaceletHandler nextHandler = this.nextHandler; 193 TagAttribute varStatusAttr = varStatus; 194 if (index != null) { 195 // wrap the next handler in a set tag handler to expose the index 196 // value from the varStatus attribute. 197 String indexValue = index.getValue(ctx); 198 if (!StringUtils.isBlank(indexValue)) { 199 String varStatusValue = varStatus != null ? varStatus.getValue(ctx) : null; 200 if (StringUtils.isBlank(varStatusValue)) { 201 // need to create and set it as an attribute for the index 202 // to be exposed 203 varStatusAttr = createAttribute(config, "varStatus", getVarName(indexValue + "_varStatus")); 204 } else { 205 varStatusAttr = createAttribute(config, "varStatus", varStatusValue); 206 } 207 ComponentConfig indexVarConfig = TagConfigFactory.createAliasTagConfig(config, tagId, indexValue, 208 "#{" + varStatusAttr.getValue() + ".index}", "false", anchor, this.nextHandler); 209 nextHandler = new SetTagHandler(indexVarConfig); 210 } 211 } 212 213 FaceletHandler handler; 214 if (renderTime(ctx)) { 215 List<TagAttribute> repeatAttrs = new ArrayList<TagAttribute>(); 216 TagAttribute itemsAttr = getItemsAttribute(); 217 repeatAttrs.add(createAttribute(config, "value", itemsAttr != null ? itemsAttr.getValue() : null)); 218 repeatAttrs.addAll(copyAttributes(config, id, var, begin, end, step, varStatusAttr, tranzient)); 219 ComponentConfig repeatConfig = TagConfigFactory.createComponentConfig(config, tagId, 220 new TagAttributesImpl(repeatAttrs.toArray(new TagAttribute[] {})), nextHandler, 221 UIRepeat.COMPONENT_TYPE, null); 222 handler = new ComponentHandler(repeatConfig); 223 } else { 224 List<TagAttribute> forEachAttrs = new ArrayList<TagAttribute>(); 225 forEachAttrs.add(createAttribute(config, "items", "#{" + getVarName("items") + "}")); 226 forEachAttrs.addAll(copyAttributes(config, var, begin, end, step, varStatusAttr, tranzient)); 227 TagConfig forEachConfig = TagConfigFactory.createTagConfig(config, tagId, 228 new TagAttributesImpl(forEachAttrs.toArray(new TagAttribute[] {})), nextHandler); 229 ForEachHandler forEachHandler = new ForEachHandler(forEachConfig); 230 231 String setTagConfigId = getTagConfigId(ctx); 232 TagAttribute itemsAttr = getItemsAttribute(); 233 ComponentConfig aliasConfig = TagConfigFactory.createAliasTagConfig(config, setTagConfigId, 234 getVarName("items"), itemsAttr != null ? itemsAttr.getValue() : null, "false", anchor, 235 forEachHandler); 236 handler = new SetTagHandler(aliasConfig); 237 } 238 239 // apply 240 handler.apply(ctx, parent); 241 } 242 243 protected boolean renderTime(FaceletContext ctx) { 244 if (renderTime != null) { 245 return renderTime.getBoolean(ctx); 246 } 247 return false; 248 } 249 250 protected String getVarName(String id) { 251 return ITERATION_VAR_PREFIX + id; 252 } 253 254 protected TagAttribute createAttribute(TagConfig tagConfig, String name, String value) { 255 return new TagAttributeImpl(tagConfig.getTag().getLocation(), "", name, name, value); 256 } 257 258 protected TagAttribute copyAttribute(TagConfig tagConfig, TagAttribute attribute) { 259 return new TagAttributeImpl(tagConfig.getTag().getLocation(), "", attribute.getLocalName(), 260 attribute.getLocalName(), attribute.getValue()); 261 } 262 263 protected List<TagAttribute> copyAttributes(TagConfig tagConfig, TagAttribute... attributes) { 264 List<TagAttribute> res = new ArrayList<TagAttribute>(); 265 if (attributes != null) { 266 for (TagAttribute attr : attributes) { 267 if (attr != null) { 268 res.add(copyAttribute(tagConfig, attr)); 269 } 270 } 271 } 272 return res; 273 } 274 275}