001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.scxml2.model;
018
019import java.lang.reflect.Array;
020import java.util.ArrayList;
021import java.util.List;
022
023import org.apache.commons.scxml2.ActionExecutionContext;
024import org.apache.commons.scxml2.Context;
025import org.apache.commons.scxml2.Evaluator;
026import org.apache.commons.scxml2.SCXMLExpressionException;
027
028/**
029 * The class in this SCXML object model that corresponds to the
030 * <foreach> SCXML element, which allows an SCXML application to iterate through a collection in the data model
031 * and to execute the actions contained within it for each item in the collection.
032 */
033public class Foreach extends Action implements ActionsContainer {
034
035    /**
036     * Serial version UID.
037     */
038    private static final long serialVersionUID = 1L;
039
040    private String array;
041    private String item;
042    private String index;
043
044    /**
045     * The set of executable elements (those that inheriting from
046     * Action) that are contained in this <if> element.
047     */
048    private List<Action> actions;
049
050    public Foreach() {
051        super();
052        this.actions = new ArrayList<Action>();
053    }
054
055    @Override
056    public final String getContainerElementName() {
057        return ELEM_FOREACH;
058    }
059
060    @Override
061    public final List<Action> getActions() {
062        return actions;
063    }
064
065    @Override
066    public final void addAction(final Action action) {
067        if (action != null) {
068            this.actions.add(action);
069        }
070    }
071
072    public String getArray() {
073        return array;
074    }
075
076    public void setArray(final String array) {
077        this.array = array;
078    }
079
080    public String getItem() {
081        return item;
082    }
083
084    public void setItem(final String item) {
085        this.item = item;
086    }
087
088    public String getIndex() {
089        return index;
090    }
091
092    public void setIndex(final String index) {
093        this.index = index;
094    }
095
096    /**
097     * {@inheritDoc}
098     */
099    @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}