KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > mondrian > olap > Formula


1 /*
2 // $Id: //open/mondrian/src/main/mondrian/olap/Formula.java#32 $
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) 2000-2002 Kana Software, Inc.
7 // Copyright (C) 2001-2006 Julian Hyde and others
8 // All Rights Reserved.
9 // You must accept the terms of that agreement to use this software.
10 //
11 // jhyde, 1 March, 2000
12 */

13
14 package mondrian.olap;
15 import mondrian.olap.type.*;
16 import mondrian.resource.MondrianResource;
17 import mondrian.mdx.MemberExpr;
18 import mondrian.mdx.MdxVisitor;
19 import mondrian.mdx.MdxVisitorImpl;
20
21 import java.io.PrintWriter JavaDoc;
22 import java.util.Arrays JavaDoc;
23 import java.util.List JavaDoc;
24
25 /**
26  * A <code>Formula</code> is a clause in an MDX query which defines a Set or a
27  * Member.
28  */

29 public class Formula extends QueryPart {
30
31     /** name of set or member */
32     private final String JavaDoc[] names;
33     /** defining expression */
34     private Exp exp;
35     // properties/solve order of member
36
private final MemberProperty[] memberProperties;
37     /**
38      * <code>true</code> is this is a member, <code>false</code> if it is a
39      * set.
40      */

41     private final boolean isMember;
42
43     private Member mdxMember;
44     private NamedSet mdxSet;
45
46     /**
47      * Constructs formula specifying a set.
48      */

49     public Formula(String JavaDoc[] names, Exp exp) {
50         this(false, names, exp, new MemberProperty[0], null, null);
51         createElement(null);
52     }
53
54     /**
55      * Constructs a formula specifying a member.
56      */

57     public Formula(String JavaDoc[] names, Exp exp, MemberProperty[] memberProperties) {
58         this(true, names, exp, memberProperties, null, null);
59     }
60
61     private Formula(
62             boolean isMember,
63             String JavaDoc[] names,
64             Exp exp,
65             MemberProperty[] memberProperties,
66             Member mdxMember,
67             NamedSet mdxSet) {
68         this.isMember = isMember;
69         this.names = names;
70         this.exp = exp;
71         this.memberProperties = memberProperties;
72         this.mdxMember = mdxMember;
73         this.mdxSet = mdxSet;
74         assert !(!isMember && mdxMember != null);
75         assert !(isMember && mdxSet != null);
76     }
77
78     public Object JavaDoc clone() {
79         return new Formula(
80                 isMember,
81                 names,
82                 (Exp) exp.clone(),
83                 MemberProperty.cloneArray(memberProperties),
84                 mdxMember,
85                 mdxSet);
86     }
87
88     static Formula[] cloneArray(Formula[] x) {
89         Formula[] x2 = new Formula[x.length];
90         for (int i = 0; i < x.length; i++) {
91             x2[i] = (Formula) x[i].clone();
92         }
93         return x2;
94     }
95
96     /**
97      * Resolves identifiers into objects.
98      *
99      * @param validator Validation context to resolve the identifiers in this
100      * formula
101      */

102     void accept(Validator validator) {
103         final boolean scalar = isMember;
104         exp = validator.validate(exp, scalar);
105         String JavaDoc id = Util.quoteMdxIdentifier(names);
106         final Type type = exp.getType();
107         if (isMember) {
108             if (!TypeUtil.canEvaluate(type)) {
109                 throw MondrianResource.instance().MdxMemberExpIsSet.ex(exp.toString());
110             }
111         } else {
112             if (!TypeUtil.isSet(type)) {
113                 throw MondrianResource.instance().MdxSetExpNotSet.ex(id);
114             }
115         }
116         for (int i = 0; i < memberProperties.length; i++) {
117             validator.validate(memberProperties[i]);
118         }
119         // Get the format expression from the property list, or derive it from
120
// the formula.
121
if (isMember) {
122             Exp formatExp = getFormatExp();
123             if (formatExp != null) {
124                 mdxMember.setProperty(Property.FORMAT_EXP.name, formatExp);
125             }
126
127             // For each property of the formula, make it a property of the
128
// member.
129
final List JavaDoc formatPropertyList =
130                     Arrays.asList(Property.FORMAT_PROPERTIES);
131             for (MemberProperty memberProperty : memberProperties) {
132                 if (formatPropertyList.contains(memberProperty.getName())) {
133                     continue; // we already dealt with format_string props
134
}
135                 final Exp exp = memberProperty.getExp();
136                 if (exp instanceof Literal) {
137                     String JavaDoc value = String.valueOf(((Literal) exp).getValue());
138                     mdxMember.setProperty(memberProperty.getName(), value);
139                 }
140             }
141         }
142     }
143
144     /**
145      * Creates the {@link Member} or {@link NamedSet} object which this formula
146      * defines.
147      */

148     void createElement(Query q) {
149         // first resolve the name, bit by bit
150
if (isMember) {
151             if (mdxMember != null) {
152                 return;
153             }
154             OlapElement mdxElement = q.getCube();
155             final SchemaReader schemaReader = q.getSchemaReader(true);
156             for (int i = 0; i < names.length; i++) {
157                 OlapElement parent = mdxElement;
158                 mdxElement = schemaReader.getElementChild(parent, names[i]);
159                 // Don't try to look up the member which the formula is
160
// defining. We would only find one if the member is overriding
161
// a member at the cube or schema level, and we don't want to
162
// change that member's properties.
163
if (mdxElement == null || i == names.length - 1) {
164                     // this part of the name was not found... define it
165
Level level;
166                     Member parentMember = null;
167                     if (parent instanceof Member) {
168                         parentMember = (Member) parent;
169                         level = parentMember.getLevel().getChildLevel();
170                     } else {
171                         Hierarchy hierarchy = parent.getHierarchy();
172                         if (hierarchy == null) {
173                             throw MondrianResource.instance().MdxCalculatedHierarchyError.ex(
174                                 Util.quoteMdxIdentifier(names));
175                         }
176                         level = hierarchy.getLevels()[0];
177                     }
178                     Member mdxMember = level.getHierarchy().createMember(
179                         parentMember, level, names[i], this);
180                     mdxElement = mdxMember;
181                 }
182             }
183             this.mdxMember = (Member) mdxElement;
184         } else {
185             // don't need to tell query... it's already in query.formula
186
Util.assertTrue(
187                 names.length == 1, "set names must not be compound");
188             mdxSet = new SetBase(names[0], exp);
189         }
190     }
191
192     public Object JavaDoc[] getChildren() {
193         Object JavaDoc[] children = new Object JavaDoc[1 + memberProperties.length];
194         children[0] = exp;
195         System.arraycopy(memberProperties, 0,
196             children, 1, memberProperties.length);
197         return children;
198     }
199
200
201     public void unparse(PrintWriter JavaDoc pw)
202     {
203         if (isMember) {
204             pw.print("member ");
205             if (mdxMember != null) {
206                 pw.print(mdxMember.getUniqueName());
207             } else {
208                 pw.print(Util.quoteMdxIdentifier(names));
209             }
210         } else {
211             pw.print("set ");
212             pw.print(Util.quoteMdxIdentifier(names));
213         }
214         pw.print(" as '");
215         exp.unparse(pw);
216         pw.print("'");
217         if (memberProperties != null) {
218             for (MemberProperty memberProperty : memberProperties) {
219                 pw.print(", ");
220                 memberProperty.unparse(pw);
221             }
222         }
223     }
224
225     public boolean isMember() {
226         return isMember;
227     }
228
229     public NamedSet getNamedSet() {
230         return mdxSet;
231     }
232
233     String JavaDoc[] getNames() {
234         return names;
235     }
236
237     /** Returns this formula's name. */
238     public String JavaDoc getName() {
239         return (isMember)
240             ? mdxMember.getName()
241             : mdxSet.getName();
242     }
243
244     /** Returns this formula's caption. */
245     public String JavaDoc getCaption() {
246         return (isMember)
247             ? mdxMember.getCaption()
248             : mdxSet.getName();
249     }
250
251     /**
252      * Changes the last part of the name to <code>newName</code>. For example,
253      * <code>[Abc].[Def].[Ghi]</code> becomes <code>[Abc].[Def].[Xyz]</code>;
254      * and the member or set is renamed from <code>Ghi</code> to
255      * <code>Xyz</code>.
256      */

257     void rename(String JavaDoc newName)
258     {
259         String JavaDoc oldName = getElement().getName();
260         Util.assertTrue(
261             this.names[this.names.length - 1].equalsIgnoreCase(oldName));
262         this.names[this.names.length - 1] = newName;
263         if (isMember) {
264             mdxMember.setName(newName);
265         } else {
266             mdxSet.setName(newName);
267         }
268     }
269
270     /** Returns the unique name of the member or set. */
271     String JavaDoc getUniqueName() {
272         return (isMember)
273             ? mdxMember.getUniqueName()
274             : mdxSet.getUniqueName();
275     }
276
277     OlapElement getElement() {
278         return (isMember)
279             ? (OlapElement) mdxMember
280             : (OlapElement) mdxSet;
281     }
282
283     public Exp getExpression() {
284         return exp;
285     }
286
287     private Exp getMemberProperty(String JavaDoc name) {
288         return MemberProperty.get(memberProperties, name);
289     }
290
291     /**
292      * Returns the Member. (Not valid if this formula defines a set.)
293      *
294      * @pre isMember()
295      * @post return != null
296      */

297     public Member getMdxMember() {
298         return mdxMember;
299     }
300
301     /**
302      * Returns the solve order. (Not valid if this formula defines a set.)
303      *
304      * @pre isMember()
305      * @return Solve order, or null if SOLVE_ORDER property is not specified
306      * or is not a number or is not constant
307      */

308     public Number JavaDoc getSolveOrder() {
309         return getIntegerMemberProperty(Property.SOLVE_ORDER.name);
310     }
311
312     /**
313      * Returns the integer value of a given constant.
314      * If the property is not set, or its
315      * value is not an integer, or its value is not a constant,
316      * returns null.
317      *
318      * @param name Property name
319      * @return Value of the property, or null if the property is not set, or its
320      * value is not an integer, or its value is not a constant.
321      */

322     private Number JavaDoc getIntegerMemberProperty(String JavaDoc name) {
323         Exp exp = getMemberProperty(name);
324         if (exp != null && exp.getType() instanceof NumericType) {
325             return quickEval(exp);
326         }
327         return null;
328     }
329
330     /**
331      * Evaluates a constant numeric expression.
332      * @param exp Expression
333      * @return Result as a number, or null if the expression is not a constant
334      * or not a number.
335      */

336     private static Number JavaDoc quickEval(Exp exp) {
337         if (exp instanceof Literal) {
338             Literal literal = (Literal) exp;
339             final Object JavaDoc value = literal.getValue();
340             if (value instanceof Number JavaDoc) {
341                 return (Number JavaDoc) value;
342             } else {
343                 return null;
344             }
345         }
346         if (exp instanceof FunCall) {
347             FunCall call = (FunCall) exp;
348             if (call.getFunName().equals("=") &&
349                 call.getSyntax() == Syntax.Prefix) {
350                 final Number JavaDoc number = quickEval(call.getArg(0));
351                 if (number == null) {
352                     return null;
353                 } else if (number instanceof Integer JavaDoc) {
354                     return -number.intValue();
355                 } else {
356                     return -number.doubleValue();
357                 }
358             }
359         }
360         return null;
361     }
362
363     /**
364      * Deduces a formatting expression for this calculated member. First it
365      * looks for properties called "format", "format_string", etc. Then it looks
366      * inside the expression, and returns the formatting expression for the
367      * first member it finds.
368      */

369     private Exp getFormatExp() {
370         // If they have specified a format string (which they can do under
371
// several names) return that.
372
for (String JavaDoc prop : Property.FORMAT_PROPERTIES) {
373             Exp formatExp = getMemberProperty(prop);
374             if (formatExp != null) {
375                 return formatExp;
376             }
377         }
378
379         // Choose a format appropriate to the expression.
380
// For now, only do it for decimals.
381
final Type type = exp.getType();
382         if (type instanceof DecimalType) {
383             int scale = ((DecimalType) type).getScale();
384             String JavaDoc formatString = "#,##0";
385             if (scale > 0) {
386                 formatString = formatString + ".";
387                 while (scale-- > 0) {
388                     formatString = formatString + "0";
389                 }
390             }
391             return Literal.createString(formatString);
392         }
393
394         if (!mdxMember.isMeasure()) {
395             // Don't try to do any format string inference on non-measure
396
// calculated members; that can hide the correct formatting
397
// from base measures (see TestCalculatedMembers.testFormatString
398
// for an example).
399
return null;
400         }
401
402         // Burrow into the expression. If we find a member, use its format
403
// string.
404
try {
405             exp.accept(
406                 new MdxVisitorImpl() {
407                     public Object JavaDoc visit(MemberExpr memberExpr) {
408                         Exp formatExp = (Exp) memberExpr.getMember().
409                             getPropertyValue(Property.FORMAT_EXP.name);
410                         if (formatExp != null) {
411                             throw new FoundOne(formatExp);
412                         }
413                         return super.visit(memberExpr);
414                     }
415                 }
416             );
417             return null;
418         } catch (FoundOne foundOne) {
419             return foundOne.exp;
420         }
421     }
422
423     public void compile() {
424         // nothing to do
425
}
426
427     /**
428      * Accepts a visitor to this Formula.
429      * The default implementation dispatches to the
430      * {@link MdxVisitor#visit(Formula)} method.
431      *
432      * @param visitor Visitor
433      */

434     public Object JavaDoc accept(MdxVisitor visitor) {
435         final Object JavaDoc o = visitor.visit(this);
436
437         // visit the expression
438
exp.accept(visitor);
439
440         return o;
441     }
442
443     private static class FoundOne extends RuntimeException JavaDoc {
444         private final Exp exp;
445
446         public FoundOne(Exp exp) {
447             super();
448             this.exp = exp;
449         }
450     }
451 }
452
453 // End Formula.java
454
Popular Tags