View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.scxml2.model;
18  
19  import java.lang.reflect.Array;
20  import java.util.ArrayList;
21  import java.util.List;
22  
23  import org.apache.commons.scxml2.ActionExecutionContext;
24  import org.apache.commons.scxml2.Context;
25  import org.apache.commons.scxml2.Evaluator;
26  import org.apache.commons.scxml2.SCXMLExpressionException;
27  
28  /**
29   * The class in this SCXML object model that corresponds to the
30   * <foreach> SCXML element, which allows an SCXML application to iterate through a collection in the data model
31   * and to execute the actions contained within it for each item in the collection.
32   */
33  public class Foreach extends Action implements ActionsContainer {
34  
35      /**
36       * Serial version UID.
37       */
38      private static final long serialVersionUID = 1L;
39  
40      private String array;
41      private String item;
42      private String index;
43  
44      /**
45       * The set of executable elements (those that inheriting from
46       * Action) that are contained in this <if> element.
47       */
48      private List<Action> actions;
49  
50      public Foreach() {
51          super();
52          this.actions = new ArrayList<Action>();
53      }
54  
55      @Override
56      public final String getContainerElementName() {
57          return ELEM_FOREACH;
58      }
59  
60      @Override
61      public final List<Action> getActions() {
62          return actions;
63      }
64  
65      @Override
66      public final void addAction(final Action action) {
67          if (action != null) {
68              this.actions.add(action);
69          }
70      }
71  
72      public String getArray() {
73          return array;
74      }
75  
76      public void setArray(final String array) {
77          this.array = array;
78      }
79  
80      public String getItem() {
81          return item;
82      }
83  
84      public void setItem(final String item) {
85          this.item = item;
86      }
87  
88      public String getIndex() {
89          return index;
90      }
91  
92      public void setIndex(final String index) {
93          this.index = index;
94      }
95  
96      /**
97       * {@inheritDoc}
98       */
99      @Override
100     public void execute(ActionExecutionContext exctx) throws ModelException, SCXMLExpressionException {
101         Context ctx = exctx.getContext(getParentEnterableState());
102         Evaluator eval = exctx.getEvaluator();
103         ctx.setLocal(getNamespacesKey(), getNamespaces());
104         try {
105             Object arrayObject = eval.eval(ctx,array);
106             if (arrayObject != null && (arrayObject instanceof Iterable || arrayObject.getClass().isArray())) {
107                 if (arrayObject.getClass().isArray()) {
108                     for (int currentIndex = 0, size = Array.getLength(arrayObject); currentIndex < size; currentIndex++) {
109                         ctx.setLocal(item, Array.get(arrayObject, currentIndex));
110                         if (index != null) {
111                             ctx.setLocal(index, currentIndex);
112                         }
113                         // The "foreach" statement is a "container"
114                         for (Action aa : actions) {
115                             aa.execute(exctx);
116                         }
117                     }
118                 }
119                 else {
120                     // Spec requires to iterate over a shallow copy of underlying array in a way that modifications to
121                     // the collection during the execution of <foreach> must not affect the iteration behavior.
122                     // For array objects (see above) this isn't needed, but for Iterables we don't have that guarantee
123                     // so we make a copy first
124                     ArrayList<Object> arrayList = new ArrayList<Object>();
125                     for (Object value: (Iterable)arrayObject) {
126                         arrayList.add(value);
127                     }
128                     int currentIndex = 0;
129                     for (Object value : arrayList) {
130                         ctx.setLocal(item, value);
131                         if (index != null) {
132                             ctx.setLocal(index, currentIndex);
133                         }
134                         // The "foreach" statement is a "container"
135                         for (Action aa : actions) {
136                             aa.execute(exctx);
137                         }
138                         currentIndex++;
139                     }
140                 }
141             }
142             // else {} TODO: place the error 'error.execution' in the internal event queue. (section "3.12.2 Errors")
143         }
144         finally {
145             ctx.setLocal(getNamespacesKey(), null);
146         }
147     }
148 }