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 &lt;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    }