001/*
002// This software is subject to the terms of the Eclipse Public License v1.0
003// Agreement, available at the following URL:
004// http://www.eclipse.org/legal/epl-v10.html.
005// You must accept the terms of that agreement to use this software.
006//
007// Copyright (C) 2011-2011 Pentaho
008// All Rights Reserved.
009*/
010package mondrian.spi.impl;
011
012import mondrian.olap.Util;
013import mondrian.spi.*;
014
015/**
016 * Provides implementations of a variety of SPIs using scripting.
017 *
018 * @author jhyde
019 */
020public class Scripts {
021
022    private static <T> T create(
023        ScriptDefinition script,
024        Class<T> iface,
025        String script2)
026    {
027        final String engineName = script.language.engineName;
028        return Util.compileScript(iface, script2, engineName);
029    }
030
031    private static String simple(ScriptDefinition script, String decl) {
032        switch (script.language) {
033        case JAVASCRIPT:
034            return "function " + decl + " { " + script.script + " }";
035        default:
036            throw Util.unexpected(script.language);
037        }
038    }
039
040    /**
041     * Creates an implementation of the {@link PropertyFormatter} SPI based on
042     * a script.
043     *
044     * @param script Script
045     * @return property formatter
046     */
047    public static PropertyFormatter propertyFormatter(
048        ScriptDefinition script)
049    {
050        return create(
051            script,
052            PropertyFormatter.class,
053            simple(
054                script,
055                "formatProperty(member,propertyName,propertyValue)"));
056    }
057
058    /**
059     * Creates an implementation of the {@link MemberFormatter} SPI based on
060     * a script.
061     *
062     * @param script Script
063     * @return member formatter
064     */
065    public static MemberFormatter memberFormatter(
066        ScriptDefinition script)
067    {
068        return create(
069            script,
070            MemberFormatter.class,
071            simple(script, "formatMember(member)"));
072    }
073
074    /**
075     * Creates an implementation of the {@link CellFormatter} SPI based on
076     * a script.
077     *
078     * @param script Script
079     * @return cell formatter
080     */
081    public static CellFormatter cellFormatter(
082        ScriptDefinition script)
083    {
084        return create(
085            script,
086            CellFormatter.class,
087            simple(script, "formatCell(value)"));
088    }
089
090    /**
091     * Creates an implementation of the {@link DataSourceChangeListener} SPI
092     * based on a script.
093     *
094     * @param script Script
095     * @return data source change listener
096     */
097    public static DataSourceChangeListener dataSourceChangeListener(
098        ScriptDefinition script)
099    {
100        final String code;
101        switch (script.language) {
102        case JAVASCRIPT:
103            code =
104                "function isHierarchyChanged(hierarchy) {\n"
105                + "  return false;\n"
106                + "}\n"
107                + "function isAggregationChanged(aggregation) {\n"
108                + "  return false;\n"
109                + "}\n";
110            break;
111        default:
112            throw Util.unexpected(script.language);
113        }
114        return create(
115            script,
116            DataSourceChangeListener.class,
117            code);
118    }
119
120    /**
121     * Creates an implementation of the {@link DataSourceResolver} SPI based on
122     * a script.
123     *
124     * @param script Script
125     * @return data source resolver
126     */
127    public static DataSourceResolver dataSourceResolver(
128        ScriptDefinition script)
129    {
130        return create(
131            script,
132            DataSourceResolver.class,
133            simple(script, "lookup(dataSourceName)"));
134    }
135
136    /**
137     * Creates an implementation of the {@link DynamicSchemaProcessor} SPI based
138     * on a script.
139     *
140     * @param script Script
141     * @return dynamic schema processor
142     */
143    public static DynamicSchemaProcessor dynamicSchemaProcessor(
144        ScriptDefinition script)
145    {
146        return create(
147            script,
148            DynamicSchemaProcessor.class,
149            simple(script, "processSchema(schemaUrl, connectInfo)"));
150    }
151
152    /**
153     * Creates an implementation of the {@link UserDefinedFunction} SPI based on
154     * a script.
155     *
156     * <p>The script must declare an object called "obj" that must have a method
157     * "evaluate(evaluator, arguments)" and may have fields "name",
158     * "description", "syntax", "parameterTypes" and method
159     * "getReturnType(parameterTypes)".</p>
160     *
161     * @param script Script
162     * @return user-defined function
163     */
164    public static UserDefinedFunction userDefinedFunction(
165        ScriptDefinition script,
166        String name)
167    {
168        final String code;
169        switch (script.language) {
170        case JAVASCRIPT:
171            code =
172                "var mondrian = Packages.mondrian;\n"
173                + "function getName() {\n"
174                + "  return " + Util.quoteJavaString(name) + ";\n"
175                + "}\n"
176                + "function getDescription() {\n"
177                + "  return this.getName();\n"
178                + "}\n"
179                + "function getSyntax() {\n"
180                + "  return mondrian.olap.Syntax.Function;\n"
181                + "}\n"
182                + "function getParameterTypes() {\n"
183                + "  return new Array();\n"
184                + "}\n"
185                + "function getReturnType(parameterTypes) {\n"
186                + "  return new mondrian.olap.type.ScalarType();\n"
187                + "}\n"
188                + "function getReservedWords() {\n"
189                + "  return null;\n"
190                + "}\n"
191                + "function execute(evaluator, arguments) {\n"
192                + "  return null;\n"
193                + "}\n"
194                + script.script;
195            break;
196        default:
197            throw Util.unexpected(script.language);
198        }
199        return create(
200            script,
201            UserDefinedFunction.class,
202            code);
203    }
204
205    public static class ScriptDefinition {
206        public final String script;
207        public final ScriptLanguage language;
208
209        public ScriptDefinition(
210            String script,
211            ScriptLanguage language)
212        {
213            this.script = script;
214            this.language = language;
215
216            assert script != null;
217            assert language != null;
218        }
219    }
220
221    public enum ScriptLanguage {
222        JAVASCRIPT("JavaScript");
223
224        final String engineName;
225
226        ScriptLanguage(String engineName) {
227            this.engineName = engineName;
228        }
229
230        public static ScriptLanguage lookup(String languageName) {
231            for (ScriptLanguage scriptLanguage : values()) {
232                if (scriptLanguage.engineName.equals(languageName)) {
233                    return scriptLanguage;
234                }
235            }
236            return null;
237        }
238    }
239}
240
241// End Scripts.java