001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019 020 package org.apache.myfaces.tobago.taglib.component; 021 022 import org.apache.commons.logging.Log; 023 import org.apache.commons.logging.LogFactory; 024 import org.apache.myfaces.tobago.apt.annotation.Tag; 025 import org.apache.myfaces.tobago.apt.annotation.TagAttribute; 026 import org.apache.myfaces.tobago.apt.annotation.UIComponentTagAttribute; 027 import org.apache.myfaces.tobago.component.ComponentUtil; 028 import org.apache.myfaces.tobago.taglib.decl.HasVar; 029 030 import javax.faces.context.FacesContext; 031 import javax.faces.el.ValueBinding; 032 import javax.faces.webapp.UIComponentTag; 033 import javax.servlet.jsp.JspException; 034 import javax.servlet.jsp.tagext.BodyTagSupport; 035 import java.util.ArrayList; 036 import java.util.Iterator; 037 import java.util.List; 038 import java.util.Map; 039 import java.util.regex.Pattern; 040 041 /** 042 * Replacement for the JSTL <c:foreach> tag. <br /> 043 * This tags iterates over the body content without setting up an exported 044 * scope variable, but replaces all occurrence's of <code>var</code> in 045 * <code>TobagoTag's ValueBinding</code> attributes.<br /> 046 * All non TobagoTags are treated as they are, no 047 * replacement is done, and so no ability to use the <code>var</code> in el. 048 */ 049 @Tag(name = "forEach") 050 @Deprecated() 051 052 public class ForEachTag extends BodyTagSupport implements HasVar { 053 054 private static final Log LOG = LogFactory.getLog(ForEachTag.class); 055 056 public static final String ALL = "-1"; 057 058 private String forEachItems; 059 060 private String var; 061 062 private String begin = "0"; 063 064 private String end = ALL; 065 066 private String step = "1"; 067 068 private IterationHelper helper; 069 070 public int doStartTag() throws JspException { 071 super.doStartTag(); 072 073 final FacesContext facesContext = FacesContext.getCurrentInstance(); 074 075 if (helper == null) { 076 helper = new IterationHelper(); 077 } 078 079 080 String replacement = forEachItems.trim(); 081 if (replacement.startsWith("#{") && replacement.endsWith("}")) { 082 replacement = replacement.substring(2, replacement.length() - 1); 083 } 084 085 int position = getIntValue(begin); 086 int stop = getIntValue(end); 087 Object[] keys = null; 088 if (stop == IterationHelper.ALL) { 089 if (UIComponentTag.isValueReference(forEachItems)) { 090 final Object items 091 = ComponentUtil.createValueBinding(this.forEachItems).getValue(facesContext); 092 if (items instanceof List) { 093 stop = ((List) items).size(); 094 } else if (items instanceof Object[]) { 095 stop = ((Object[]) items).length; 096 } else if (items instanceof Map) { 097 List keyList = new ArrayList(); 098 for (Iterator i = ((Map) items).keySet().iterator(); i.hasNext();) { 099 keyList.add(i.next()); 100 } 101 keys = keyList.toArray(new Object[keyList.size()]); 102 stop = keys.length; 103 } else if (items == null) { 104 if (LOG.isInfoEnabled()) { 105 LOG.info("No Elements to render!"); 106 } 107 } else { 108 LOG.error("Illegal items object : " + items.getClass().getName()); 109 } 110 } else { 111 LOG.error("Not a ValueBinding : \"" + forEachItems + "\""); 112 } 113 if (stop == IterationHelper.ALL) { 114 stop = 0; 115 } 116 } 117 118 119 helper.init(replacement, var, position, stop, getIntValue(step), keys); 120 121 return position < stop ? EVAL_BODY_INCLUDE : SKIP_BODY; 122 } 123 124 public int doAfterBody() throws JspException { 125 return helper.next() ? EVAL_BODY_AGAIN : SKIP_BODY; 126 } 127 128 129 private int getIntValue(String value) { 130 int result; 131 if (UIComponentTag.isValueReference(value)) { 132 ValueBinding valueBinding = FacesContext.getCurrentInstance() 133 .getApplication().createValueBinding(value); 134 result = ComponentUtil.getIntValue(valueBinding); 135 } else { 136 result = Integer.parseInt(value); 137 } 138 return result; 139 } 140 141 142 public void release() { 143 super.release(); 144 forEachItems = null; 145 var = null; 146 begin = "0"; 147 end = ALL; 148 if (helper != null) { 149 helper.reset(); 150 } 151 } 152 153 /** 154 * <strong>ValueBindingExpression</strong> pointing to a 155 * <code>java.util.List</code>, <code>java.util.Map</code> or 156 * <code>Object[]</code> of items to iterate over. 157 */ 158 @TagAttribute 159 @UIComponentTagAttribute(type = {"java.util.List", "java.util.Map", "java.lang.Object[]"}) 160 public void setItems(String items) { 161 this.forEachItems = items; 162 } 163 164 public void setVar(String var) { 165 this.var = var; 166 } 167 168 169 /** 170 * Index at which the iteration begins. 171 */ 172 @TagAttribute 173 @UIComponentTagAttribute(type = {"java.lang.Integer"}, defaultValue = "0") 174 public void setBegin(String begin) { 175 this.begin = begin; 176 } 177 178 179 /** 180 * Index at which the iteration stops. 181 * Defaults to <code>items.length()</code>. 182 */ 183 @TagAttribute 184 @UIComponentTagAttribute(type = {"java.lang.Integer"}, defaultValue = "items.lenght()") 185 public void setEnd(String end) { 186 this.end = end; 187 } 188 189 190 /** 191 * Index increments every iteration by this value. 192 */ 193 @TagAttribute 194 @UIComponentTagAttribute(type = {"java.lang.Integer"}, defaultValue = "1") 195 public void setStep(String step) { 196 this.step = step; 197 } 198 199 public IterationHelper getIterationHelper() { 200 return helper; 201 } 202 203 public static class IterationHelper { 204 205 public static final int ALL = -1; 206 207 private int helperPosition; 208 private int helperStop; 209 private int helperStep; 210 private String helperReplacement; 211 private Object[] helperKeys; 212 213 private Pattern pattern; 214 215 public IterationHelper() { 216 reset(); 217 } 218 219 public boolean next() { 220 helperPosition += helperStep; 221 return helperPosition < helperStop; 222 } 223 224 public String replace(String binding) { 225 final String result = pattern.matcher(binding).replaceAll( 226 "$1" + helperReplacement + "[" 227 + (helperKeys == null ? Integer.toString(helperPosition) : "'" + helperKeys[helperPosition] + "'") 228 + "]$2"); 229 if (LOG.isDebugEnabled()) { 230 LOG.debug("transform \"" + binding + "\" to \"" + result + "\""); 231 } 232 return result; 233 } 234 235 public void reset() { 236 helperPosition = 0; 237 helperStop = ALL; 238 helperStep = 1; 239 helperReplacement = null; 240 helperKeys = null; 241 } 242 243 244 public void init(String replacement, String var, int position, int stop, 245 int step, Object[] keys) { 246 this.helperReplacement = replacement; 247 this.helperPosition = position; 248 this.helperStop = stop; 249 this.helperStep = step; 250 this.helperKeys = keys; 251 pattern = Pattern.compile("(\\W *?[^\\.] *?)" + var + "( *?\\W)"); 252 } 253 } 254 }