KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*
2 // $Id: //open/mondrian/src/main/mondrian/olap/fun/StrToSetFunDef.java#4 $
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) 2006-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.olap.*;
13 import mondrian.olap.type.*;
14 import mondrian.calc.Calc;
15 import mondrian.calc.ExpCompiler;
16 import mondrian.calc.StringCalc;
17 import mondrian.calc.impl.AbstractListCalc;
18 import mondrian.mdx.ResolvedFunCall;
19 import mondrian.mdx.DimensionExpr;
20 import mondrian.mdx.HierarchyExpr;
21 import mondrian.resource.MondrianResource;
22
23 import java.util.ArrayList JavaDoc;
24 import java.util.List JavaDoc;
25
26 /**
27  * Definition of the <code>StrToSet</code> MDX builtin function.
28  *
29  * @author jhyde
30  * @version $Id: //open/mondrian/src/main/mondrian/olap/fun/StrToSetFunDef.java#4 $
31  * @since Mar 23, 2006
32  */

33 class StrToSetFunDef extends FunDefBase {
34     static final ResolverImpl Resolver = new ResolverImpl();
35
36     private StrToSetFunDef(int[] parameterTypes) {
37         super("StrToSet", "<Set> StrToSet(<String>[, <Dimension>...])",
38                 "Constructs a set from a string expression.",
39                 Syntax.Function, Category.Set, parameterTypes);
40     }
41
42     public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) {
43         final StringCalc stringCalc = compiler.compileString(call.getArg(0));
44         SetType type = (SetType) call.getType();
45         Type elementType = type.getElementType();
46         if (elementType instanceof MemberType) {
47             final Hierarchy hierarchy = elementType.getHierarchy();
48             return new AbstractListCalc(call, new Calc[] {stringCalc}) {
49                 public List JavaDoc evaluateList(Evaluator evaluator) {
50                     String JavaDoc string = stringCalc.evaluateString(evaluator);
51                     return parseMemberList(evaluator, string, hierarchy);
52                 }
53             };
54         } else {
55             TupleType tupleType = (TupleType) elementType;
56             final Hierarchy[] hierarchies =
57                 new Hierarchy[tupleType.elementTypes.length];
58             for (int i = 0; i < tupleType.elementTypes.length; i++) {
59                 hierarchies[i] = tupleType.elementTypes[i].getHierarchy();
60             }
61             return new AbstractListCalc(call, new Calc[] {stringCalc}) {
62                 public List JavaDoc evaluateList(Evaluator evaluator) {
63                     String JavaDoc string = stringCalc.evaluateString(evaluator);
64                     return parseTupleList(evaluator, string, hierarchies);
65                 }
66             };
67         }
68     }
69
70     private static List JavaDoc<Member[]> parseTupleList(
71         Evaluator evaluator,
72         String JavaDoc string,
73         Hierarchy[] hierarchies)
74     {
75         List JavaDoc<Member[]> tupleList = new ArrayList JavaDoc<Member[]>();
76         Member[] members = new Member[hierarchies.length];
77         int i = 0;
78         char c;
79         while ((c = string.charAt(i++)) == ' ') {
80         }
81         if (c != '{') {
82             throw fail(string, i, "{");
83         }
84         while (true) {
85             i = parseTuple(evaluator, string, i, members, hierarchies);
86             tupleList.add(members.clone());
87             while ((c = string.charAt(i++)) == ' ') {
88             }
89             if (c == ',') {
90                 // fine
91
} else if (c == '}') {
92                 // we're done
93
return tupleList;
94             } else {
95                 throw fail(string, i, ", or }");
96             }
97         }
98     }
99
100     /**
101      * Parses a tuple, of the form '(member, member, ...)'.
102      * There must be precisely one member for each hierarchy.
103      *
104      * @param evaluator Evaluator, provides a {@link mondrian.olap.SchemaReader}
105      * and {@link Cube}
106      * @param string String to parse
107      * @param i Position to start parsing in string
108      * @param members Output array of members
109      * @param hierarchies Hierarchies of the members
110      * @return Position where parsing ended in string
111      */

112     static int parseTuple(
113         Evaluator evaluator,
114         String JavaDoc string,
115         int i,
116         Member[] members,
117         Hierarchy[] hierarchies)
118     {
119         char c;
120         while ((c = string.charAt(i++)) == ' ') {
121         }
122         if (c != '(') {
123             throw fail(string, i, "(");
124         }
125         int j = 0;
126         while (true) {
127             i = parseMember(evaluator, string, i, members, hierarchies, j);
128             while ((c = string.charAt(i++)) == ' ') {
129             }
130             ++j;
131             if (j < hierarchies.length) {
132                 if (c == ',') {
133                     // fine
134
} else if (c == ')') {
135                         // saw ')' before we saw enough members
136
throw Util.newInternal("too few members");
137                 } else {
138                 }
139             } else {
140                 if (c == ')') {
141                     break;
142                 } else {
143                     throw Util.newInternal("expected ')");
144                 }
145             }
146         }
147         return i;
148     }
149
150     private static List JavaDoc<Member> parseMemberList(
151         Evaluator evaluator,
152         String JavaDoc string,
153         Hierarchy hierarchy)
154     {
155         Hierarchy[] hierarchies = new Hierarchy[] {hierarchy};
156         List JavaDoc<Member> memberList = new ArrayList JavaDoc<Member>();
157         Member[] members = {null};
158         int i = 0;
159         char c;
160         while ((c = string.charAt(i++)) == ' ') {
161         }
162         if (c != '{') {
163             throw fail(string, i, "{");
164         }
165         while (true) {
166             i = parseMember(evaluator, string, i, members, hierarchies, 0);
167             memberList.add(members[0]);
168             while ((c = string.charAt(i++)) == ' ') {
169             }
170             if (c == ',') {
171                 // fine
172
} else if (c == '}') {
173                 // we're done
174
return memberList;
175             } else {
176                 throw fail(string, i, ", or }");
177             }
178         }
179     }
180
181     // State values
182
private static final int BEFORE_SEG = 0;
183     private static final int IN_BRACKET_SEG = 1;
184     private static final int AFTER_SEG = 2;
185     private static final int IN_SEG = 3;
186
187     static int parseMember(
188         Evaluator evaluator,
189         String JavaDoc string,
190         int i,
191         Member[] members,
192         Hierarchy[] hierarchies,
193         int j)
194     {
195         int k = string.length();
196         List JavaDoc<String JavaDoc> nameList = new ArrayList JavaDoc<String JavaDoc>();
197         int state = BEFORE_SEG;
198         int start = 0;
199         char c;
200
201         loop:
202         while (i < k) {
203             switch (state) {
204             case BEFORE_SEG:
205                 c = string.charAt(i);
206                 switch (c) {
207                 case '[':
208                     ++i;
209                     start = i;
210                     state = IN_BRACKET_SEG;
211                     break;
212
213                 case ' ':
214                     // Skip whitespace, don't change state.
215
++i;
216                     break;
217
218                 case ',':
219                 case '}':
220                     break loop;
221
222                 case '.':
223                     // TODO: test this, case: ".abc"
224
throw Util.newInternal("unexpected: '.'");
225
226                 default:
227                     // Carry on reading.
228
state = IN_SEG;
229                     start = i;
230                     break;
231                 }
232                 break;
233
234             case IN_SEG:
235                 c = string.charAt(i);
236                 switch (c) {
237                 case '.':
238                     nameList.add(string.substring(start, i));
239                     state = BEFORE_SEG;
240                     ++i;
241                 default:
242                     ++i;
243                 }
244                 break;
245
246             case IN_BRACKET_SEG:
247                 c = string.charAt(i);
248                 switch (c) {
249                 case ']':
250                     nameList.add(string.substring(start, i));
251                     ++i;
252                     state = AFTER_SEG;
253                     break;
254
255                 default:
256                     // Carry on reading.
257
++i;
258                 }
259                 break;
260
261             case AFTER_SEG:
262                 c = string.charAt(i);
263                 switch (c) {
264                 case ' ':
265                     // Skip over any spaces
266
// TODO: test this case: '[foo] . [bar]'
267
++i;
268                     break;
269                 case '.':
270                     state = BEFORE_SEG;
271                     ++i;
272                     break;
273
274                 default:
275                     // We're not looking at the start of a segment. Parse
276
// the member we've seen so far, then return.
277
break loop;
278                 }
279                 break;
280
281             default:
282                 throw Util.newInternal("unexpected state: " + state);
283             }
284         }
285
286         // End of member.
287
final String JavaDoc[] names = nameList.toArray(new String JavaDoc[nameList.size()]);
288         Member member =
289             (Member)
290                 Util.lookupCompound(
291                     evaluator.getSchemaReader(),
292                     evaluator.getCube(),
293                     names, true, Category.Member);
294         members[j] = member;
295         if (member.getHierarchy() != hierarchies[j]) {
296             // TODO: better error
297
throw Util.newInternal("member is of wrong hierarchy");
298         }
299         return i;
300     }
301
302     private static RuntimeException JavaDoc fail(String JavaDoc string, int i, String JavaDoc expecting) {
303         throw Util.newInternal("expected '" + expecting + "' at position " + i + " in '" + string + "'");
304     }
305
306     public Exp createCall(Validator validator, Exp[] args) {
307         final int argCount = args.length;
308         if (argCount <= 1) {
309             throw MondrianResource.instance().MdxFuncArgumentsNum.ex(getName());
310         }
311         for (int i = 1; i < argCount; i++) {
312             final Exp arg = args[i];
313             if (arg instanceof DimensionExpr) {
314                 // if arg is a dimension, switch to dimension's default
315
// hierarchy
316
DimensionExpr dimensionExpr = (DimensionExpr) arg;
317                 Dimension dimension = dimensionExpr.getDimension();
318                 args[i] = new HierarchyExpr(dimension.getHierarchy());
319             } else if (arg instanceof HierarchyExpr) {
320                 // nothing
321
} else {
322                 throw MondrianResource.instance().MdxFuncNotHier.ex(
323                     i + 1, getName());
324             }
325         }
326         return super.createCall(validator, args);
327     }
328
329     public Type getResultType(Validator validator, Exp[] args) {
330         switch (args.length) {
331         case 1:
332             // This is a call to the standard version of StrToSet,
333
// which doesn't give us any hints about type.
334
return new SetType(null);
335
336         case 2: {
337             final Type argType = args[1].getType();
338             return new SetType(
339                 new MemberType(
340                     argType.getDimension(),
341                     argType.getHierarchy(),
342                     argType.getLevel(),
343                     null));
344         }
345
346         default: {
347             // This is a call to Mondrian's extended version of
348
// StrToSet, of the form
349
// StrToSet(s, <Hier1>, ... , <HierN>)
350
//
351
// The result is a set of tuples
352
// (<Hier1>, ... , <HierN>)
353
final List JavaDoc<Type> list = new ArrayList JavaDoc<Type>();
354             for (int i = 1; i < args.length; i++) {
355                 Exp arg = args[i];
356                 final Type argType = arg.getType();
357                 list.add(
358                     new MemberType(
359                         argType.getDimension(),
360                         argType.getHierarchy(),
361                         argType.getLevel(),
362                         null)
363                 );
364             }
365             final Type[] types = list.toArray(new Type[list.size()]);
366             return new SetType(new TupleType(types));
367         }
368         }
369     }
370
371     private static class ResolverImpl extends ResolverBase {
372         ResolverImpl() {
373             super(
374                     "StrToSet",
375                     "StrToSet(<String Expression>)",
376                     "Constructs a set from a string expression.",
377                     Syntax.Function);
378         }
379
380         public FunDef resolve(
381                 Exp[] args, Validator validator, int[] conversionCount) {
382             if (args.length < 1) {
383                 return null;
384             }
385             Type type = args[0].getType();
386             if (!(type instanceof StringType)) {
387                 return null;
388             }
389             for (int i = 1; i < args.length; i++) {
390                 Exp exp = args[i];
391                 if (!(exp instanceof DimensionExpr)) {
392                     return null;
393                 }
394             }
395             int[] argTypes = new int[args.length];
396             argTypes[0] = Category.String;
397             for (int i = 1; i < argTypes.length; i++) {
398                 argTypes[i] = Category.Hierarchy;
399             }
400             return new StrToSetFunDef(argTypes);
401         }
402
403         public FunDef getFunDef() {
404             return new StrToSetFunDef(new int[] {Category.String});
405         }
406     }
407 }
408
409 // End StrToSetFunDef.java
410
Popular Tags