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 }