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