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 }