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