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