001    /*
002    // $Id: //open/mondrian/src/main/mondrian/olap/fun/FunTableImpl.java#8 $
003    // This software is subject to the terms of the Common Public License
004    // Agreement, available at the following URL:
005    // http://www.opensource.org/licenses/cpl.html.
006    // Copyright (C) 2002-2006 Julian Hyde
007    // All Rights Reserved.
008    // You must accept the terms of that agreement to use this software.
009    */
010    package mondrian.olap.fun;
011    
012    import mondrian.mdx.UnresolvedFunCall;
013    import mondrian.olap.*;
014    import mondrian.resource.MondrianResource;
015    
016    import java.util.*;
017    
018    /**
019     * Abstract implementation of {@link FunTable}.
020     *
021     * <p>The derived class must implement {@link #defineFunctions()} to define
022     * each function which will be recognized by this table. This method is called
023     * from the constructor, after which point, no further functions can be added.
024     */
025    public abstract class FunTableImpl implements FunTable {
026        /**
027         * Maps the upper-case name of a function plus its
028         * {@link mondrian.olap.Syntax} to an array of
029         * {@link mondrian.olap.Validator} objects for that name.
030         */
031        protected final Map<String, List<Resolver>> mapNameToResolvers =
032            new HashMap<String, List<Resolver>>();
033        private final Set<String> reservedWords = new HashSet<String>();
034        private final Set<String> propertyWords = new HashSet<String>();
035        /** used during initialization */
036        protected final List<Resolver> resolverList = new ArrayList<Resolver>();
037        protected final List<FunInfo> funInfoList = new ArrayList<FunInfo>();
038    
039        protected FunTableImpl() {
040        }
041    
042        /**
043         * Initializes the function table.
044         */
045        public void init() {
046            defineFunctions();
047            organizeFunctions();
048        }
049    
050        protected static String makeResolverKey(String name, Syntax syntax) {
051            return name.toUpperCase() + "$" + syntax;
052        }
053    
054        protected void define(FunDef funDef) {
055            define(new SimpleResolver(funDef));
056        }
057    
058        protected void define(Resolver resolver) {
059            addFunInfo(resolver);
060            if (resolver.getSyntax() == Syntax.Property) {
061                defineProperty(resolver.getName());
062            }
063            resolverList.add(resolver);
064            final String[] reservedWords = resolver.getReservedWords();
065            for (String reservedWord : reservedWords) {
066                defineReserved(reservedWord);
067            }
068        }
069    
070        protected void addFunInfo(Resolver resolver) {
071            this.funInfoList.add(FunInfo.make(resolver));
072        }
073    
074        public FunDef getDef(
075                Exp[] args, Validator validator, String funName, Syntax syntax) {
076            String key = makeResolverKey(funName, syntax);
077    
078            // Resolve function by its upper-case name first.  If there is only one
079            // function with that name, stop immediately.  If there is more than
080            // function, use some custom method, which generally involves looking
081            // at the type of one of its arguments.
082            String signature = syntax.getSignature(funName,
083                    Category.Unknown, ExpBase.getTypes(args));
084            List<Resolver> resolvers = mapNameToResolvers.get(key);
085            if (resolvers == null) {
086                resolvers = Collections.emptyList();
087            }
088    
089            int[] conversionCount = new int[] {0};
090            int minConversions = Integer.MAX_VALUE;
091            int matchCount = 0;
092            FunDef matchDef = null;
093            for (Resolver resolver : resolvers) {
094                conversionCount[0] = 0;
095                FunDef def = resolver.resolve(args, validator, conversionCount);
096                if (def != null) {
097                    int conversions = conversionCount[0];
098                    if (conversions < minConversions) {
099                        minConversions = conversions;
100                        matchCount = 1;
101                        matchDef = def;
102                    } else if (conversions == minConversions) {
103                        matchCount++;
104                    } else {
105                        // ignore this match -- it required more coercions than
106                        // other overloadings we've seen
107                    }
108                }
109            }
110            switch (matchCount) {
111            case 0:
112                throw MondrianResource.instance().NoFunctionMatchesSignature.ex(
113                        signature);
114            case 1:
115                final String matchKey = makeResolverKey(matchDef.getName(),
116                        matchDef.getSyntax());
117                Util.assertTrue(matchKey.equals(key), matchKey);
118                return matchDef;
119            default:
120                throw MondrianResource.instance().MoreThanOneFunctionMatchesSignature.ex(signature);
121            }
122        }
123    
124        public boolean requiresExpression(
125                UnresolvedFunCall call,
126                int k,
127                Validator validator) {
128            // The function call has not been resolved yet. In fact, this method
129            // may have been invoked while resolving the child. Consider this:
130            //   CrossJoin([Measures].[Unit Sales] * [Measures].[Store Sales])
131            //
132            // In order to know whether to resolve '*' to the multiplication
133            // operator (which returns a scalar) or the crossjoin operator (which
134            // returns a set) we have to know what kind of expression is expected.
135            String key = makeResolverKey(call.getFunName(), call.getSyntax());
136            List<Resolver> resolvers = mapNameToResolvers.get(key);
137            if (resolvers == null) {
138                resolvers = Collections.emptyList();
139            }
140            for (Resolver resolver2 : resolvers) {
141                if (!resolver2.requiresExpression(k)) {
142                    // This resolver accepts a set in this argument position,
143                    // therefore we don't REQUIRE a scalar expression.
144                    return false;
145                }
146            }
147            return true;
148        }
149    
150        public List<String> getReservedWords() {
151            return new ArrayList<String>(reservedWords);
152        }
153    
154        public boolean isReserved(String s) {
155            return reservedWords.contains(s.toUpperCase());
156        }
157    
158        /**
159         * Defines a reserved word.
160         * @see #isReserved
161         */
162        protected void defineReserved(String s) {
163            reservedWords.add(s.toUpperCase());
164        }
165    
166        public List<Resolver> getResolvers() {
167            final List<Resolver> list = new ArrayList<Resolver>();
168            for (List<Resolver> resolvers : mapNameToResolvers.values()) {
169                list.addAll(resolvers);
170            }
171            return list;
172        }
173    
174        public boolean isProperty(String s) {
175            return propertyWords.contains(s.toUpperCase());
176        }
177    
178        /**
179         * Defines a word matching a property function name.
180         * @see #isProperty
181         */
182        protected void defineProperty(String s) {
183            propertyWords.add(s.toUpperCase());
184        }
185    
186        public List<FunInfo> getFunInfoList() {
187            return Collections.unmodifiableList(this.funInfoList);
188        }
189    
190        /**
191         * Indexes the collection of functions.
192         */
193        protected void organizeFunctions() {
194            Collections.sort(funInfoList);
195            // Map upper-case function names to resolvers.
196            for (Resolver resolver : resolverList) {
197                String key = makeResolverKey(resolver.getName(),
198                    resolver.getSyntax());
199                List<Resolver> list = mapNameToResolvers.get(key);
200                if (list == null) {
201                    list = new ArrayList<Resolver>();
202                    mapNameToResolvers.put(key, list);
203                }
204                list.add(resolver);
205            }
206        }
207    
208        /**
209         * This method is called from the constructor, to define the set of
210         * functions and reserved words recognized.
211         *
212         * <p>Each function is declared by calling {@link #define}. Each reserved
213         * word is declared by calling {@link #defineReserved(String)}.
214         *
215         * <p>Derived class can override this method to add more functions.
216         */
217        protected abstract void defineFunctions();
218    }
219    
220    // End FunTableImpl.java