KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > mondrian > olap > fun > FunTableImpl


1 /*
2 // $Id: //open/mondrian/src/main/mondrian/olap/fun/FunTableImpl.java#8 $
3 // This software is subject to the terms of the Common Public License
4 // Agreement, available at the following URL:
5 // http://www.opensource.org/licenses/cpl.html.
6 // Copyright (C) 2002-2006 Julian Hyde
7 // All Rights Reserved.
8 // You must accept the terms of that agreement to use this software.
9 */

10 package mondrian.olap.fun;
11
12 import mondrian.mdx.UnresolvedFunCall;
13 import mondrian.olap.*;
14 import mondrian.resource.MondrianResource;
15
16 import java.util.*;
17
18 /**
19  * Abstract implementation of {@link FunTable}.
20  *
21  * <p>The derived class must implement {@link #defineFunctions()} to define
22  * each function which will be recognized by this table. This method is called
23  * from the constructor, after which point, no further functions can be added.
24  */

25 public abstract class FunTableImpl implements FunTable {
26     /**
27      * Maps the upper-case name of a function plus its
28      * {@link mondrian.olap.Syntax} to an array of
29      * {@link mondrian.olap.Validator} objects for that name.
30      */

31     protected final Map<String JavaDoc, List<Resolver>> mapNameToResolvers =
32         new HashMap<String JavaDoc, List<Resolver>>();
33     private final Set<String JavaDoc> reservedWords = new HashSet<String JavaDoc>();
34     private final Set<String JavaDoc> propertyWords = new HashSet<String JavaDoc>();
35     /** used during initialization */
36     protected final List<Resolver> resolverList = new ArrayList<Resolver>();
37     protected final List<FunInfo> funInfoList = new ArrayList<FunInfo>();
38
39     protected FunTableImpl() {
40     }
41
42     /**
43      * Initializes the function table.
44      */

45     public void init() {
46         defineFunctions();
47         organizeFunctions();
48     }
49
50     protected static String JavaDoc makeResolverKey(String JavaDoc name, Syntax syntax) {
51         return name.toUpperCase() + "$" + syntax;
52     }
53
54     protected void define(FunDef funDef) {
55         define(new SimpleResolver(funDef));
56     }
57
58     protected void define(Resolver resolver) {
59         addFunInfo(resolver);
60         if (resolver.getSyntax() == Syntax.Property) {
61             defineProperty(resolver.getName());
62         }
63         resolverList.add(resolver);
64         final String JavaDoc[] reservedWords = resolver.getReservedWords();
65         for (String JavaDoc reservedWord : reservedWords) {
66             defineReserved(reservedWord);
67         }
68     }
69
70     protected void addFunInfo(Resolver resolver) {
71         this.funInfoList.add(FunInfo.make(resolver));
72     }
73
74     public FunDef getDef(
75             Exp[] args, Validator validator, String JavaDoc funName, Syntax syntax) {
76         String JavaDoc key = makeResolverKey(funName, syntax);
77
78         // Resolve function by its upper-case name first. If there is only one
79
// function with that name, stop immediately. If there is more than
80
// function, use some custom method, which generally involves looking
81
// at the type of one of its arguments.
82
String JavaDoc signature = syntax.getSignature(funName,
83                 Category.Unknown, ExpBase.getTypes(args));
84         List<Resolver> resolvers = mapNameToResolvers.get(key);
85         if (resolvers == null) {
86             resolvers = Collections.emptyList();
87         }
88
89         int[] conversionCount = new int[] {0};
90         int minConversions = Integer.MAX_VALUE;
91         int matchCount = 0;
92         FunDef matchDef = null;
93         for (Resolver resolver : resolvers) {
94             conversionCount[0] = 0;
95             FunDef def = resolver.resolve(args, validator, conversionCount);
96             if (def != null) {
97                 int conversions = conversionCount[0];
98                 if (conversions < minConversions) {
99                     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 JavaDoc 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 JavaDoc 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 JavaDoc> getReservedWords() {
151         return new ArrayList<String JavaDoc>(reservedWords);
152     }
153
154     public boolean isReserved(String JavaDoc s) {
155         return reservedWords.contains(s.toUpperCase());
156     }
157
158     /**
159      * Defines a reserved word.
160      * @see #isReserved
161      */

162     protected void defineReserved(String JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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
221
Popular Tags