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.util.Map;
020import java.util.StringTokenizer;
021
022import org.apache.commons.scxml2.ActionExecutionContext;
023import org.apache.commons.scxml2.Context;
024import org.apache.commons.scxml2.Evaluator;
025import org.apache.commons.scxml2.SCXMLExpressionException;
026import org.apache.commons.scxml2.semantics.ErrorConstants;
027
028/**
029 * A <code>NamelistHolder</code> represents an element in the SCXML
030 * document that may have a namelist attribute to
031 * produce payload for events or external communication.
032 */
033public abstract class NamelistHolder extends ParamsContainer {
034
035    /**
036     * The namelist.
037     */
038    private String namelist;
039
040    /**
041     * Get the namelist.
042     *
043     * @return String Returns the namelist.
044     */
045    public final String getNamelist() {
046        return namelist;
047    }
048
049    /**
050     * Set the namelist.
051     *
052     * @param namelist The namelist to set.
053     */
054    public final void setNamelist(final String namelist) {
055        this.namelist = namelist;
056    }
057
058    /**
059     * Adds data to the payload data map based on the namelist which names are location expressions
060     * (typically data ids or for example XPath variables). The names and the values they 'point' at
061     * are added to the payload data map.
062     * @param exctx The ActionExecutionContext
063     * @param payload the payload data map to be updated
064     * @throws ModelException if this action has not an EnterableState as parent
065     * @throws SCXMLExpressionException if a malformed or invalid expression is evaluated
066     * @see PayloadProvider#addToPayload(String, Object, java.util.Map)
067     */
068    protected void addNamelistDataToPayload(ActionExecutionContext exctx, Map<String, Object> payload)
069            throws ModelException, SCXMLExpressionException {
070        if (namelist != null) {
071            EnterableState parentState = getParentEnterableState();
072            Context ctx = exctx.getContext(parentState);
073            try {
074                ctx.setLocal(getNamespacesKey(), getNamespaces());
075                Evaluator evaluator = exctx.getEvaluator();
076                StringTokenizer tkn = new StringTokenizer(namelist);
077                boolean xpathEvaluator = Evaluator.XPATH_DATA_MODEL.equals(evaluator.getSupportedDatamodel());
078                while (tkn.hasMoreTokens()) {
079                    String varName = tkn.nextToken();
080                    Object varObj = evaluator.eval(ctx, varName);
081                    if (varObj == null) {
082                        //considered as a warning here
083                        exctx.getErrorReporter().onError(ErrorConstants.UNDEFINED_VARIABLE,
084                                varName + " = null", parentState);
085                    }
086                    if (xpathEvaluator && varName.startsWith("$")) {
087                        varName = varName.substring(1);
088                    }
089                    addToPayload(varName, varObj, payload);
090                }
091            }
092            finally {
093                ctx.setLocal(getNamespacesKey(), null);
094            }
095        }
096    }
097}