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) 2006-2011 Pentaho and others 008// All Rights Reserved. 009*/ 010package mondrian.olap.fun; 011 012import mondrian.olap.*; 013import mondrian.olap.type.Type; 014import mondrian.spi.UserDefinedFunction; 015import mondrian.util.ServiceDiscovery; 016 017import java.util.List; 018 019/** 020 * Global function table contains builtin functions and global user-defined 021 * functions. 022 * 023 * @author Gang Chen 024 */ 025public class GlobalFunTable extends FunTableImpl { 026 027 private static GlobalFunTable instance = new GlobalFunTable(); 028 static { 029 instance.init(); 030 } 031 032 public static GlobalFunTable instance() { 033 return instance; 034 } 035 036 private GlobalFunTable() { 037 } 038 039 public void defineFunctions(Builder builder) { 040 final FunTable builtinFunTable = BuiltinFunTable.instance(); 041 final List<String> reservedWords = builtinFunTable.getReservedWords(); 042 for (String reservedWord : reservedWords) { 043 builder.defineReserved(reservedWord); 044 } 045 final List<Resolver> resolvers = builtinFunTable.getResolvers(); 046 for (Resolver resolver : resolvers) { 047 builder.define(resolver); 048 } 049 050 for (Class<UserDefinedFunction> udfClass : lookupUdfImplClasses()) { 051 defineUdf( 052 builder, 053 new UdfResolver.ClassUdfFactory(udfClass, null)); 054 } 055 } 056 057 private List<Class<UserDefinedFunction>> lookupUdfImplClasses() { 058 final ServiceDiscovery<UserDefinedFunction> serviceDiscovery = 059 ServiceDiscovery.forClass(UserDefinedFunction.class); 060 return serviceDiscovery.getImplementor(); 061 } 062 063 /** 064 * Defines a user-defined function in this table. 065 * 066 * <p>If the function is not valid, throws an error. 067 * 068 * @param builder Builder 069 * @param udfFactory Factory for UDF 070 */ 071 private void defineUdf( 072 Builder builder, 073 UdfResolver.UdfFactory udfFactory) 074 { 075 // Instantiate class with default constructor. 076 final UserDefinedFunction udf = udfFactory.create(); 077 078 // Validate function. 079 validateFunction(udf); 080 081 // Define function. 082 builder.define(new UdfResolver(udfFactory)); 083 } 084 085 /** 086 * Throws an error if a user-defined function does not adhere to the 087 * API. 088 * 089 * @param udf User defined function 090 */ 091 private void validateFunction(final UserDefinedFunction udf) { 092 // Check that the name is not null or empty. 093 final String udfName = udf.getName(); 094 if (udfName == null || udfName.equals("")) { 095 throw Util.newInternal( 096 "User-defined function defined by class '" 097 + udf.getClass() + "' has empty name"); 098 } 099 // It's OK for the description to be null. 100 //final String description = udf.getDescription(); 101 102 final Type[] parameterTypes = udf.getParameterTypes(); 103 for (int i = 0; i < parameterTypes.length; i++) { 104 Type parameterType = parameterTypes[i]; 105 if (parameterType == null) { 106 throw Util.newInternal( 107 "Invalid user-defined function '" + udfName 108 + "': parameter type #" + i + " is null"); 109 } 110 } 111 112 // It's OK for the reserved words to be null or empty. 113 //final String[] reservedWords = udf.getReservedWords(); 114 115 // Test that the function returns a sensible type when given the FORMAL 116 // types. It may still fail when we give it the ACTUAL types, but it's 117 // impossible to check that now. 118 final Type returnType = udf.getReturnType(parameterTypes); 119 if (returnType == null) { 120 throw Util.newInternal( 121 "Invalid user-defined function '" + udfName 122 + "': return type is null"); 123 } 124 final Syntax syntax = udf.getSyntax(); 125 if (syntax == null) { 126 throw Util.newInternal( 127 "Invalid user-defined function '" + udfName 128 + "': syntax is null"); 129 } 130 } 131} 132 133// End GlobalFunTable.java