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