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}