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 }