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.Collections; 027import java.util.List; 028 029import javax.el.ELException; 030import javax.faces.FacesException; 031import javax.faces.component.UIComponent; 032import javax.faces.model.DataModel; 033import javax.faces.model.ListDataModel; 034import javax.faces.view.facelets.ComponentConfig; 035import javax.faces.view.facelets.FaceletContext; 036import javax.faces.view.facelets.FaceletException; 037import javax.faces.view.facelets.FaceletHandler; 038import javax.faces.view.facelets.TagAttribute; 039import javax.faces.view.facelets.TagConfig; 040import javax.faces.view.facelets.TagHandler; 041 042import org.apache.commons.lang.StringUtils; 043 044import com.sun.faces.facelets.tag.TagAttributeImpl; 045import com.sun.faces.facelets.tag.TagAttributesImpl; 046import com.sun.faces.facelets.tag.jstl.core.ForEachHandler; 047 048/** 049 * Repeat handler, similar to the standard ForEach handler. 050 * <p> 051 * This component encapsulates a c:forEach tag inside a nxu:set tag, to be able to control when the sub-components 052 * should be re-created in the case of an ajax re-render. 053 * 054 * @author Anahide Tchertchian 055 */ 056public class RepeatTagHandler extends TagHandler { 057 058 @SuppressWarnings({ "unchecked", "rawtypes" }) 059 protected static final DataModel EMPTY_MODEL = new ListDataModel(Collections.emptyList()); 060 061 /** 062 * @since 5.7 063 */ 064 protected static final String ITERATION_VAR_PREFIX = "nxuRepeat_"; 065 066 /** 067 * @since 5.7 068 */ 069 protected final TagConfig config; 070 071 /** 072 * @deprecated, user {@link #items} instead 073 */ 074 @Deprecated 075 protected final TagAttribute value; 076 077 /** 078 * @since 5.7 079 */ 080 protected final TagAttribute items; 081 082 /** 083 * @since 5.7 084 */ 085 protected final TagAttribute itemsId; 086 087 protected final TagAttribute var; 088 089 protected final TagAttribute index; 090 091 /** 092 * @since 5.7 093 */ 094 protected final TagAttribute status; 095 096 /** 097 * @since 5.7 098 */ 099 protected final TagAttribute begin; 100 101 /** 102 * @since 5.7 103 */ 104 protected final TagAttribute end; 105 106 /** 107 * @since 5.7 108 */ 109 protected final TagAttribute step; 110 111 /** 112 * @since 5.7 113 */ 114 protected final TagAttribute tranzient; 115 116 /** 117 * @since 5.7 118 */ 119 protected final TagAttribute varStatus; 120 121 public RepeatTagHandler(TagConfig config) { 122 super(config); 123 this.config = config; 124 items = getAttribute("items"); 125 itemsId = getAttribute("itemsId"); 126 value = getAttribute("value"); 127 var = getAttribute("var"); 128 index = getAttribute("index"); 129 status = getAttribute("status"); 130 begin = getAttribute("begin"); 131 end = getAttribute("end"); 132 step = getAttribute("step"); 133 tranzient = getAttribute("transient"); 134 varStatus = getAttribute("varStatus"); 135 } 136 137 protected TagAttribute getItemsAttribute() { 138 TagAttribute itemsAttr = items; 139 if (items == null) { 140 // BBB 141 itemsAttr = value; 142 } 143 return itemsAttr; 144 } 145 146 protected String getTagConfigId(FaceletContext ctx) { 147 String refId = null; 148 if (itemsId != null) { 149 refId = itemsId.getValue(ctx); 150 } 151 152 if (StringUtils.isBlank(refId)) { 153 TagAttribute itemsAttr = getItemsAttribute(); 154 Object val = null; 155 if (itemsAttr != null) { 156 val = itemsAttr.getObject(ctx); 157 if (val != null) { 158 refId = val.toString(); 159 } 160 } 161 } 162 163 StringBuilder builder = new StringBuilder(); 164 if (refId != null) { 165 builder.append(refId); 166 } 167 builder.append(";"); 168 169 Integer intValue = new Integer(builder.toString().hashCode()); 170 return intValue.toString(); 171 } 172 173 /** 174 * Encapsulate the call to a c:forEach tag in an SetTagHandler exposing the value and making sure the tagConfigId 175 * changes when the value changes (see NXP-11434). 176 * <p> 177 * See also NXP-15050: since 6.0, anchor handler in component tree to ensure proper ajax refresh when iteration 178 * value changes. 179 */ 180 @Override 181 public void apply(FaceletContext ctx, UIComponent parent) throws IOException, FacesException, FaceletException, 182 ELException { 183 String anchor = String.valueOf(true); 184 FaceletHandler nextHandler = this.nextHandler; 185 TagAttribute varStatusAttr = varStatus; 186 if (index != null) { 187 // wrap the next handler in a set tag handler to expose the index 188 // value from the varStatus attribute. 189 String indexValue = index.getValue(ctx); 190 if (!StringUtils.isBlank(indexValue)) { 191 String varStatusValue = varStatus != null ? varStatus.getValue(ctx) : null; 192 if (StringUtils.isBlank(varStatusValue)) { 193 // need to create and set it as an attribute for the index 194 // to be exposed 195 varStatusAttr = createAttribute(config, "varStatus", getVarName(indexValue + "_varStatus")); 196 } else { 197 varStatusAttr = createAttribute(config, "varStatus", varStatusValue); 198 } 199 ComponentConfig indexVarConfig = TagConfigFactory.createAliasTagConfig(config, tagId, indexValue, 200 "#{" + varStatusAttr.getValue() + ".index}", "false", anchor, this.nextHandler); 201 nextHandler = new SetTagHandler(indexVarConfig); 202 } 203 } 204 205 List<TagAttribute> forEachAttrs = new ArrayList<TagAttribute>(); 206 forEachAttrs.add(createAttribute(config, "items", "#{" + 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 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}