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;
018
019import java.util.Map;
020import java.util.concurrent.ConcurrentHashMap;
021
022import org.apache.commons.scxml2.env.groovy.GroovyEvaluator;
023import org.apache.commons.scxml2.env.javascript.JSEvaluator;
024import org.apache.commons.scxml2.env.jexl.JexlEvaluator;
025import org.apache.commons.scxml2.env.minimal.MinimalEvaluator;
026import org.apache.commons.scxml2.env.xpath.XPathEvaluator;
027import org.apache.commons.scxml2.model.ModelException;
028import org.apache.commons.scxml2.model.SCXML;
029import static org.apache.commons.scxml2.Evaluator.DEFAULT_DATA_MODEL;
030
031/**
032 * A static singleton factory for {@link EvaluatorProvider}s by supported SCXML datamodel type.
033 * <p>
034 *  The EvaluatorFactory is used to automatically create an {@link Evaluator} instance for an SCXML
035 *  statemachine when none has been pre-defined and configured for the {@link SCXMLExecutor}.
036 * </p>
037 * <p>
038 *  The builtin supported providers are:
039 *  <ul>
040 *      <li>no or empty datamodel (default) or datamodel="jexl": {@link JexlEvaluator.JexlEvaluatorProvider}</li>
041 *      <li>datamodel="ecmascript": {@link JSEvaluator.JSEvaluatorProvider}</li>
042 *      <li>datamodel="groovy": {@link GroovyEvaluator.GroovyEvaluatorProvider}</li>
043 *      <li>datamodel="xpath": {@link XPathEvaluator.XPathEvaluatorProvider}</li>
044 *      <li>datamodel="null": {@link MinimalEvaluator.MinimalEvaluatorProvider}</li>
045 *  </ul>
046 *  </p>
047 *  <p>
048 *  For adding additional or overriding the builtin Evaluator implementations use
049 *  {@link #registerEvaluatorProvider(EvaluatorProvider)} or {@link #unregisterEvaluatorProvider(String)}.
050 *  </p>
051 *  <p>
052 *  The default provider can be overridden using the {@link #setDefaultProvider(EvaluatorProvider)} which will
053 *  register the provider under the {@link Evaluator#DEFAULT_DATA_MODEL} ("") value for the datamodel.<br/>
054 *  Note: this is <em>not</em> the same as datamodel="null"!
055 * </p>
056 */
057public class EvaluatorFactory {
058
059    private static EvaluatorFactory INSTANCE = new EvaluatorFactory();
060
061    private final Map<String, EvaluatorProvider> providers = new ConcurrentHashMap<String, EvaluatorProvider>();
062
063    private EvaluatorFactory() {
064        providers.put(XPathEvaluator.SUPPORTED_DATA_MODEL, new XPathEvaluator.XPathEvaluatorProvider());
065        providers.put(JSEvaluator.SUPPORTED_DATA_MODEL, new JSEvaluator.JSEvaluatorProvider());
066        providers.put(GroovyEvaluator.SUPPORTED_DATA_MODEL, new GroovyEvaluator.GroovyEvaluatorProvider());
067        providers.put(JexlEvaluator.SUPPORTED_DATA_MODEL, new JexlEvaluator.JexlEvaluatorProvider());
068        providers.put(MinimalEvaluator.SUPPORTED_DATA_MODEL, new MinimalEvaluator.MinimalEvaluatorProvider());
069        providers.put(DEFAULT_DATA_MODEL, providers.get(JexlEvaluator.SUPPORTED_DATA_MODEL));
070    }
071
072    public static void setDefaultProvider(EvaluatorProvider defaultProvider) {
073        INSTANCE.providers.put(DEFAULT_DATA_MODEL, defaultProvider);
074    }
075
076    @SuppressWarnings("unused")
077    public static EvaluatorProvider getDefaultProvider() {
078        return INSTANCE.providers.get(DEFAULT_DATA_MODEL);
079    }
080
081    @SuppressWarnings("unused")
082    public static EvaluatorProvider getEvaluatorProvider(String datamodelName) {
083        return INSTANCE.providers.get(datamodelName == null ? DEFAULT_DATA_MODEL : datamodelName);
084    }
085
086    @SuppressWarnings("unused")
087    public static void registerEvaluatorProvider(EvaluatorProvider provider) {
088        INSTANCE.providers.put(provider.getSupportedDatamodel(), provider);
089    }
090
091    @SuppressWarnings("unused")
092    public static void unregisterEvaluatorProvider(String datamodelName) {
093        INSTANCE.providers.remove(datamodelName == null ? DEFAULT_DATA_MODEL : datamodelName);
094    }
095
096    /**
097     * Returns a dedicated Evaluator instance for a specific SCXML document its documentmodel.
098     * <p>If no SCXML document is provided a default Evaluator will be returned.</p>
099     * @param document The document to return a dedicated Evaluator for. May be null to retrieve the default Evaluator.
100     * @return a new and not sharable Evaluator instance for the provided document, or a default Evaluator otherwise
101     * @throws ModelException If the SCXML document datamodel is not supported.
102     */
103    public static Evaluator getEvaluator(SCXML document) throws ModelException {
104        String datamodelName = document != null ? document.getDatamodelName() : null;
105        EvaluatorProvider provider = INSTANCE.providers.get(datamodelName == null ? DEFAULT_DATA_MODEL : datamodelName);
106        if (provider == null) {
107            throw new ModelException("Unsupported SCXML document datamodel \""+(datamodelName)+"\"");
108        }
109        return document != null ? provider.getEvaluator(document) : provider.getEvaluator();
110    }
111}