/* // This software is subject to the terms of the Eclipse Public License v1.0 // Agreement, available at the following URL: // http://www.eclipse.org/legal/epl-v10.html. // You must accept the terms of that agreement to use this software. // // Copyright (C) 2002-2016 Pentaho and others // All Rights Reserved. */ package mondrian.spi.impl; import mondrian.olap.Util; import mondrian.spi.*; /** * Provides implementations of a variety of SPIs using scripting. * * @author jhyde */ public class Scripts { /** * Creates an implementation of the {@link PropertyFormatter} SPI based on * a script. * * @param scriptText Script text * @param scriptLanguage Script language * @return property formatter */ public static PropertyFormatter propertyFormatter( String scriptText, String scriptLanguage) { return create( PropertyFormatter.class, scriptText, scriptLanguage, "formatProperty(member,propertyName,propertyValue)"); } /** * Creates an implementation of the {@link MemberFormatter} SPI based on * a script. * * @param scriptText Script text * @param scriptLanguage Script language * @return member formatter */ public static MemberFormatter memberFormatter( String scriptText, String scriptLanguage) { return create( MemberFormatter.class, scriptText, scriptLanguage, "formatMember(member)"); } /** * Creates an implementation of the {@link CellFormatter} SPI based on * a script. * * @param scriptText Script text * @param scriptLanguage Script language * @return cell formatter */ public static CellFormatter cellFormatter( String scriptText, String scriptLanguage) { return create( CellFormatter.class, scriptText, scriptLanguage, "formatCell(value)"); } /** * Creates an implementation of the {@link DataSourceChangeListener} SPI * based on a script. * * @param script Script * @return data source change listener */ public static DataSourceChangeListener dataSourceChangeListener( ScriptDefinition script) { final String code; switch (script.language) { case JAVASCRIPT: code = "function isHierarchyChanged(hierarchy) {\n" + " return false;\n" + "}\n" + "function isAggregationChanged(aggregation) {\n" + " return false;\n" + "}\n"; break; default: throw Util.unexpected(script.language); } return create( script, DataSourceChangeListener.class, code); } /** * Creates an implementation of the {@link DataSourceResolver} SPI based on * a script. * * @param script Script * @return data source resolver */ public static DataSourceResolver dataSourceResolver( ScriptDefinition script) { return create( script, DataSourceResolver.class, simple(script, "lookup(dataSourceName)")); } /** * Creates an implementation of the {@link DynamicSchemaProcessor} SPI based * on a script. * * @param script Script * @return dynamic schema processor */ public static DynamicSchemaProcessor dynamicSchemaProcessor( ScriptDefinition script) { return create( script, DynamicSchemaProcessor.class, simple(script, "processSchema(schemaUrl, connectInfo)")); } /** * Creates an implementation of the {@link UserDefinedFunction} SPI based on * a script. * * <p>The script must declare an object called "obj" that must have a method * "evaluate(evaluator, arguments)" and may have fields "name", * "description", "syntax", "parameterTypes" and method * "getReturnType(parameterTypes)".</p> * * @param script Script * @return user-defined function */ public static UserDefinedFunction userDefinedFunction( ScriptDefinition script, String name) { final String code; switch (script.language) { case JAVASCRIPT: code = "var mondrian = Packages.mondrian;\n" + "function getName() {\n" + " return " + Util.quoteJavaString(name) + ";\n" + "}\n" + "function getDescription() {\n" + " return this.getName();\n" + "}\n" + "function getSyntax() {\n" + " return mondrian.olap.Syntax.Function;\n" + "}\n" + "function getParameterTypes() {\n" + " return new Array();\n" + "}\n" + "function getReturnType(parameterTypes) {\n" + " return new mondrian.olap.type.ScalarType();\n" + "}\n" + "function getReservedWords() {\n" + " return null;\n" + "}\n" + "function execute(evaluator, arguments) {\n" + " return null;\n" + "}\n" + script.script; break; default: throw Util.unexpected(script.language); } return create( script, UserDefinedFunction.class, code); } private static Scripts.ScriptDefinition toScriptDef( String script, String language) { final Scripts.ScriptLanguage scriptLanguage = Scripts.ScriptLanguage.lookup(language); if (scriptLanguage == null) { throw Util.newError( "Invalid script language '" + language + "'"); } return new Scripts.ScriptDefinition(script, scriptLanguage); } private static <T> T create( ScriptDefinition script, Class<T> iface, String script2) { final String engineName = script.language.engineName; return Util.compileScript(iface, script2, engineName); } private static <T> T create( Class<T> iface, String scriptText, String scriptLanguage, String functionSignature) { ScriptDefinition scriptDef = toScriptDef(scriptText, scriptLanguage); String script = simple(scriptDef, functionSignature); return create(scriptDef, iface, script); } private static String simple(ScriptDefinition script, String decl) { switch (script.language) { case JAVASCRIPT: return "function " + decl + " { " + script.script + " }"; default: throw Util.unexpected(script.language); } } public static class ScriptDefinition { public final String script; public final ScriptLanguage language; public ScriptDefinition( String script, ScriptLanguage language) { this.script = script; this.language = language; assert script != null; assert language != null; } } public enum ScriptLanguage { JAVASCRIPT("JavaScript"); final String engineName; ScriptLanguage(String engineName) { this.engineName = engineName; } public static ScriptLanguage lookup(String languageName) { for (ScriptLanguage scriptLanguage : values()) { if (scriptLanguage.engineName.equals(languageName)) { return scriptLanguage; } } return null; } } } // End Scripts.java