KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > mondrian > olap > Query


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

13
14 package mondrian.olap;
15 import mondrian.calc.Calc;
16 import mondrian.calc.ExpCompiler;
17 import mondrian.calc.ExpCompiler.ResultStyle;
18 import mondrian.calc.impl.BetterExpCompiler;
19 import mondrian.mdx.*;
20 import mondrian.olap.fun.FunUtil;
21 import mondrian.olap.fun.ParameterFunDef;
22 import mondrian.olap.type.*;
23 import mondrian.resource.MondrianResource;
24 import mondrian.rolap.*;
25
26 import java.io.*;
27 import java.util.*;
28
29 /**
30  * <code>Query</code> is an MDX query.
31  *
32  * <p>It is created by calling {@link Connection#parseQuery},
33  * and executed by calling {@link Connection#execute},
34  * to return a {@link Result}.</p>
35  *
36  * <h3>Query control</h3>
37  *
38  * <p>Most queries are model citizens, executing quickly (often using cached
39  * results from previous queries), but some queries take more time, or more
40  * database resources, or more results, than is reasonable. Mondrian offers
41  * three ways to control rogue queries:<ul>
42  *
43  * <li>You can set a query timeout by setting the
44  * {@link MondrianProperties#QueryTimeout} parameter. If the query
45  * takes longer to execute than the value of this parameter, the system
46  * will kill it.</li>
47  *
48  * <li>The {@link MondrianProperties#QueryLimit} parameter limits the number
49  * of cells returned by a query.</li>
50  *
51  * <li>At any time while a query is executing, another thread can call the
52  * {@link #cancel()} method. The call to {@link Connection#execute(Query)}
53  * will throw an exception.</li>
54  *
55  * </ul>
56  *
57  * @author jhyde
58  * @version $Id: //open/mondrian/src/main/mondrian/olap/Query.java#85 $
59  */

60 public class Query extends QueryPart {
61
62     /**
63      * public-private: This must be public because it is still accessed in rolap.RolapCube
64      */

65     public Formula[] formulas;
66
67     /**
68      * public-private: This must be public because it is still accessed in rolap.RolapConnection
69      */

70     public QueryAxis[] axes;
71
72     /**
73      * public-private: This must be public because it is still accessed in rolap.RolapResult
74      */

75     public QueryAxis slicerAxis;
76
77     /**
78      * Definitions of all parameters used in this query.
79      */

80     private final List<Parameter> parameters = new ArrayList<Parameter>();
81
82     private final Map<String JavaDoc, Parameter> parametersByName =
83         new HashMap<String JavaDoc, Parameter>();
84
85     /**
86      * Cell properties. Not currently used.
87      */

88     private final QueryPart[] cellProps;
89
90     /**
91      * Cube this query belongs to.
92      */

93     private final Cube cube;
94
95     private final Connection connection;
96     public Calc[] axisCalcs;
97     public Calc slicerCalc;
98
99     /**
100      * Set of FunDefs for which alerts about non-native evaluation
101      * have already been posted.
102      */

103     Set<FunDef> alertedNonNativeFunDefs;
104
105     /**
106      * Start time of query execution
107      */

108     private long startTime;
109
110     /**
111      * Query timeout, in milliseconds
112      */

113     private final int queryTimeout;
114
115     /**
116      * If true, cancel this query
117      */

118     private boolean isCanceled;
119
120     /**
121      * If not <code>null</code>, this query was notified that it
122      * might cause an OutOfMemoryError.
123      */

124     private String JavaDoc outOfMemoryMsg;
125
126     /**
127      * If true, query is in the middle of execution
128      */

129     private boolean isExecuting;
130
131     /**
132      * Unique list of members referenced from the measures dimension.
133      * Will be used to determine if cross joins can be processed natively
134      * for virtual cubes.
135      */

136     private Set<Member> measuresMembers;
137
138     /**
139      * If true, virtual cubes can be processed using native cross joins.
140      * It defaults to true, unless functions are applied on measures.
141      */

142     private boolean nativeCrossJoinVirtualCube;
143
144     /**
145      * Used for virtual cubes. Contains the set of maps used to map the
146      * levels referenced in a virtual cube to the columns in the underlying
147      * base cubes.
148      */

149     private Set<Map<RolapLevel, RolapStar.Column>> virtualCubeBaseCubeMaps;
150
151     /**
152      * Maps one of the level-to-column maps stored in
153      * virtualCubeBaseCubeMaps to a measure corresponding to the underlying
154      * cube that the level-to-column map corrsponds to
155      */

156     private Map<Map<RolapLevel, RolapStar.Column>, RolapMember> levelMapToMeasureMap;
157
158     /**
159      * If true, loading schema
160      */

161     private boolean load;
162
163     /**
164      * How should the query be returned? Valid values are:
165      * ResultStyle.ITERABLE
166      * ResultStyle.LIST
167      * ResultStyle.MUTABLE_LIST
168      * For java4, use LIST
169      */

170     private ResultStyle resultStyle = (Util.PreJdk15)
171                 ? ResultStyle.LIST : ResultStyle.ITERABLE;
172
173
174     private Map<String JavaDoc, Object JavaDoc> evalCache = new HashMap<String JavaDoc, Object JavaDoc>();
175
176     /**
177      * Creates a Query.
178      */

179     public Query(
180             Connection connection,
181             Formula[] formulas,
182             QueryAxis[] axes,
183             String JavaDoc cube,
184             QueryAxis slicerAxis,
185             QueryPart[] cellProps,
186             boolean load) {
187         this(connection,
188                 connection.getSchema().lookupCube(cube, true),
189                 formulas,
190                 axes,
191                 slicerAxis,
192                 cellProps,
193                 new Parameter[0],
194                 load);
195     }
196
197     /**
198      * Creates a Query.
199      */

200     public Query(
201             Connection connection,
202             Cube mdxCube,
203             Formula[] formulas,
204             QueryAxis[] axes,
205             QueryAxis slicerAxis,
206             QueryPart[] cellProps,
207             Parameter[] parameters,
208             boolean load) {
209         this.connection = connection;
210         this.cube = mdxCube;
211         this.formulas = formulas;
212         this.axes = axes;
213         normalizeAxes();
214         this.slicerAxis = slicerAxis;
215         this.cellProps = cellProps;
216         this.parameters.addAll(Arrays.asList(parameters));
217         this.isExecuting = false;
218         this.queryTimeout =
219             MondrianProperties.instance().QueryTimeout.get() * 1000;
220         this.measuresMembers = new HashSet<Member>();
221         // assume, for now, that cross joins on virtual cubes can be
222
// processed natively; as we parse the query, we'll know otherwise
223
this.nativeCrossJoinVirtualCube = true;
224         this.load = load;
225         this.alertedNonNativeFunDefs = new HashSet<FunDef>();
226         resolve();
227     }
228
229     /**
230      * Adds a new formula specifying a set
231      * to an existing query.
232      */

233     public void addFormula(String JavaDoc[] names, Exp exp) {
234         Formula newFormula = new Formula(names, exp);
235         int formulaCount = 0;
236         if (formulas.length > 0) {
237             formulaCount = formulas.length;
238         }
239         Formula[] newFormulas = new Formula[formulaCount + 1];
240         System.arraycopy(formulas, 0, newFormulas, 0, formulaCount);
241         newFormulas[formulaCount] = newFormula;
242         formulas = newFormulas;
243         resolve();
244     }
245
246     /**
247      * Adds a new formula specifying a member
248      * to an existing query.
249      */

250     public void addFormula(
251             String JavaDoc[] names,
252             Exp exp,
253             MemberProperty[] memberProperties) {
254         Formula newFormula = new Formula(names, exp, memberProperties);
255         int formulaCount = 0;
256         if (formulas.length > 0) {
257             formulaCount = formulas.length;
258         }
259         Formula[] newFormulas = new Formula[formulaCount + 1];
260         System.arraycopy(formulas, 0, newFormulas, 0, formulaCount);
261         newFormulas[formulaCount] = newFormula;
262         formulas = newFormulas;
263         resolve();
264     }
265
266     public Validator createValidator() {
267         return new StackValidator(connection.getSchema().getFunTable());
268     }
269
270     public Object JavaDoc clone() {
271         return new Query(
272                 connection,
273                 cube,
274                 Formula.cloneArray(formulas),
275                 QueryAxis.cloneArray(axes),
276                 (slicerAxis == null) ? null : (QueryAxis) slicerAxis.clone(),
277                 cellProps,
278                 parameters.toArray(new Parameter[parameters.size()]),
279                 load);
280     }
281
282     public Query safeClone() {
283         return (Query) clone();
284     }
285
286     public Connection getConnection() {
287         return connection;
288     }
289
290     /**
291      * Returns the MDX query string. If the query was created by parsing an
292      * MDX string, the string returned by this method may not be identical, but
293      * it will have the same meaning. If the query's parse tree has been
294      * manipulated (for instance, the rows and columns axes have been
295      * interchanged) the returned string represents the current parse tree.
296      *
297      * @deprecated Use {@link Util#unparse(Query)}; deprecated since 2.1.2
298      */

299     public String JavaDoc getQueryString() {
300         return toMdx();
301     }
302
303     /**
304      * Issues a cancel request on this Query object. Once the thread
305      * running the query detects the cancel request, the query execution will
306      * throw an exception. See <code>BasicQueryTest.testCancel</code> for an
307      * example of usage of this method.
308      */

309     public void cancel() {
310         isCanceled = true;
311     }
312
313     void setOutOfMemory(String JavaDoc msg) {
314         outOfMemoryMsg = msg;
315     }
316
317     /**
318      * Checks if either a cancel request has been issued on the query or
319      * the execution time has exceeded the timeout value (if one has been
320      * set). Exceptions are raised if either of these two conditions are
321      * met. This method should be called periodically during query execution
322      * to ensure timely detection of these events, particularly before/after
323      * any potentially long running operations.
324      */

325     public void checkCancelOrTimeout() {
326         if (!isExecuting) {
327             return;
328         }
329         if (isCanceled) {
330             throw MondrianResource.instance().QueryCanceled.ex();
331         }
332         if (queryTimeout > 0) {
333             long currTime = System.currentTimeMillis();
334             if ((currTime - startTime) >= queryTimeout) {
335                 throw MondrianResource.instance().QueryTimeout.ex(
336                     (long) queryTimeout / 1000);
337             }
338         }
339         if (outOfMemoryMsg != null) {
340             throw new MemoryLimitExceededException(outOfMemoryMsg);
341         }
342     }
343
344     /**
345      * Sets the start time of query execution. Used to detect timeout for
346      * queries.
347      */

348     public void setQueryStartTime() {
349         startTime = System.currentTimeMillis();
350         isExecuting = true;
351     }
352
353     /**
354      * Called when query execution has completed. Once query execution has
355      * ended, it is not possible to cancel or timeout the query until it
356      * starts executing again.
357      */

358     public void setQueryEndExecution() {
359         isExecuting = false;
360     }
361
362     /**
363      * Determines whether an alert for non-native evaluation needs
364      * to be posted.
365      *
366      * @param funDef function type to alert for
367      *
368      * @return true if alert should be raised
369      */

370     public boolean shouldAlertForNonNative(FunDef funDef) {
371         return alertedNonNativeFunDefs.add(funDef);
372     }
373
374     private void normalizeAxes() {
375         for (int i = 0; i < axes.length; i++) {
376             AxisOrdinal correctOrdinal = AxisOrdinal.forLogicalOrdinal(i);
377             if (axes[i].getAxisOrdinal() != correctOrdinal) {
378                 for (int j = i + 1; j < axes.length; j++) {
379                     if (axes[j].getAxisOrdinal() == correctOrdinal) {
380                         // swap axes
381
QueryAxis temp = axes[i];
382                         axes[i] = axes[j];
383                         axes[j] = temp;
384                         break;
385                     }
386                 }
387             }
388         }
389     }
390
391     /**
392      * Performs type-checking and validates internal consistency of a query,
393      * using the default resolver.
394      *
395      * <p>This method is called automatically when a query is created; you need
396      * to call this method manually if you have modified the query's expression
397      * tree in any way.
398      */

399     public void resolve() {
400         final Validator validator = createValidator();
401         resolve(validator); // resolve self and children
402
// Create a dummy result so we can use its evaluator
403
final Evaluator evaluator = RolapUtil.createEvaluator(this);
404         ExpCompiler compiler = createCompiler(evaluator, validator);
405         compile(compiler);
406     }
407
408     /**
409      * @return true if Query object is being accessed during schema load
410      * and the property to ignore invalid members is set to true
411      */

412     public boolean ignoreInvalidMembers()
413     {
414         return load &&
415             MondrianProperties.instance().IgnoreInvalidMembers.get();
416     }
417
418     /**
419      * A Query's ResultStyle can only be one of the following:
420      * ResultStyle.ITERABLE
421      * ResultStyle.LIST
422      * ResultStyle.MUTABLE_LIST
423      *
424      * @param resultStyle
425      */

426     public void setResultStyle(ResultStyle resultStyle) {
427         switch (resultStyle) {
428         case ITERABLE :
429             // For java4, use LIST
430
resultStyle = (Util.PreJdk15)
431                 ? ResultStyle.LIST : ResultStyle.ITERABLE;
432             break;
433         case LIST :
434         case MUTABLE_LIST:
435             this.resultStyle = resultStyle;
436             break;
437         default :
438             throw ResultStyleException.generateBadType(
439                     new ResultStyle[] {
440                         ResultStyle.ITERABLE,
441                         ResultStyle.LIST,
442                         ResultStyle.MUTABLE_LIST
443                     },
444                     resultStyle
445                 );
446         }
447     }
448     public ResultStyle getResultStyle() {
449         return resultStyle;
450     }
451
452     /**
453      * Generates compiled forms of all expressions.
454      *
455      * @param compiler Compiler
456      */

457     private void compile(ExpCompiler compiler) {
458         if (formulas != null) {
459             for (Formula formula : formulas) {
460                 formula.compile();
461             }
462         }
463
464         if (axes != null) {
465             axisCalcs = new Calc[axes.length];
466             for (int i = 0; i < axes.length; i++) {
467                 axisCalcs[i] = axes[i].compile(compiler,
468                                     new ResultStyle[] { resultStyle });
469             }
470         }
471         if (slicerAxis != null) {
472             slicerCalc = slicerAxis.compile(compiler,
473                                 new ResultStyle[] { resultStyle });
474         }
475     }
476
477     /**
478      * Performs type-checking and validates internal consistency of a query.
479      *
480      * @param validator Validator
481      */

482     void resolve(Validator validator) {
483         // Before commencing validation, create all calculated members,
484
// calculated sets, and parameters.
485
if (formulas != null) {
486             // Resolving of formulas should be done in two parts
487
// because formulas might depend on each other, so all calculated
488
// mdx elements have to be defined during resolve.
489
for (Formula formula : formulas) {
490                 formula.createElement(validator.getQuery());
491             }
492         }
493
494         // Register all parameters.
495
parameters.clear();
496         parametersByName.clear();
497         accept(
498             new MdxVisitorImpl() {
499                 public Object JavaDoc visit(ParameterExpr parameterExpr) {
500                     Parameter parameter = parameterExpr.getParameter();
501                     if (!parameters.contains(parameter)) {
502                         parameters.add(parameter);
503                         parametersByName.put(parameter.getName(), parameter);
504                     }
505                     return null;
506                 }
507
508                 public Object JavaDoc visit(UnresolvedFunCall call) {
509                     if (call.getFunName().equals("Parameter")) {
510                         // Is there already a parameter with this name?
511
String JavaDoc parameterName =
512                             ParameterFunDef.getParameterName(call.getArgs());
513                         if (parametersByName.get(parameterName) != null) {
514                             throw MondrianResource.instance().
515                                 ParameterDefinedMoreThanOnce.ex(parameterName);
516                         }
517
518                         Type type =
519                             ParameterFunDef.getParameterType(call.getArgs());
520
521                         // Create a temporary parameter. We don't know its
522
// type yet. The default of NULL is temporary.
523
Parameter parameter = new ParameterImpl(
524                             parameterName, Literal.nullValue, null, type);
525                         parameters.add(parameter);
526                         parametersByName.put(parameterName, parameter);
527                     }
528                     return null;
529                 }
530             }
531         );
532
533         // Validate formulas.
534
if (formulas != null) {
535             for (Formula formula : formulas) {
536                 validator.validate(formula);
537             }
538         }
539
540         // Validate axes.
541
if (axes != null) {
542             for (QueryAxis axis : axes) {
543                 validator.validate(axis);
544             }
545         }
546         if (slicerAxis != null) {
547             slicerAxis.validate(validator);
548         }
549
550         // Make sure that no dimension is used on more than one axis.
551
final Dimension[] dimensions = getCube().getDimensions();
552         for (Dimension dimension : dimensions) {
553             int useCount = 0;
554             for (int j = -1; j < axes.length; j++) {
555                 final QueryAxis axisExp;
556                 if (j < 0) {
557                     if (slicerAxis == null) {
558                         continue;
559                     }
560                     axisExp = slicerAxis;
561                 } else {
562                     axisExp = axes[j];
563                 }
564                 if (axisExp.getSet().getType().usesDimension(dimension,
565                     false)) {
566                     ++useCount;
567                 }
568             }
569             if (useCount > 1) {
570                 throw MondrianResource.instance().DimensionInIndependentAxes.ex(
571                     dimension.getUniqueName());
572             }
573         }
574     }
575
576     public void unparse(PrintWriter pw) {
577         if (formulas != null) {
578             for (int i = 0; i < formulas.length; i++) {
579                 if (i == 0) {
580                     pw.print("with ");
581                 } else {
582                     pw.print(" ");
583                 }
584                 formulas[i].unparse(pw);
585                 pw.println();
586             }
587         }
588         pw.print("select ");
589         if (axes != null) {
590             for (int i = 0; i < axes.length; i++) {
591                 axes[i].unparse(pw);
592                 if (i < axes.length - 1) {
593                     pw.println(",");
594                     pw.print(" ");
595                 } else {
596                     pw.println();
597                 }
598             }
599         }
600         if (cube != null) {
601             pw.println("from [" + cube.getName() + "]");
602         }
603         if (slicerAxis != null) {
604             pw.print("where ");
605             slicerAxis.unparse(pw);
606             pw.println();
607         }
608     }
609
610     public String JavaDoc toMdx() {
611         StringWriter sw = new StringWriter();
612         PrintWriter pw = new QueryPrintWriter(sw);
613         unparse(pw);
614         return sw.toString();
615     }
616
617     /** Returns the MDX query string. */
618     public String JavaDoc toString() {
619         resolve();
620         return Util.unparse(this);
621     }
622
623     public Object JavaDoc[] getChildren() {
624         // Chidren are axes, slicer, and formulas (in that order, to be
625
// consistent with replaceChild).
626
List<QueryPart> list = new ArrayList<QueryPart>();
627         for (QueryAxis axis : axes) {
628             list.add(axis);
629         }
630         if (slicerAxis != null) {
631             list.add(slicerAxis);
632         }
633         for (Formula formula : formulas) {
634             list.add(formula);
635         }
636         return list.toArray();
637     }
638
639     public QueryAxis getSlicerAxis() {
640         return slicerAxis;
641     }
642
643     public void setSlicerAxis(QueryAxis axis) {
644         this.slicerAxis = axis;
645     }
646
647     /**
648      * Adds a level to an axis expression.
649      */

650     public void addLevelToAxis(AxisOrdinal axis, Level level) {
651         assert axis != null;
652         axes[axis.logicalOrdinal()].addLevel(level);
653     }
654
655     /**
656      * Returns the hierarchies in an expression.
657      *
658      * <p>If the expression's type is a dimension with several hierarchies,
659      * assumes that the expression yields a member of the first (default)
660      * hierarchy of the dimension.
661      *
662      * <p>For example, the expression
663      * <blockquote><code>Crossjoin(
664      * Hierarchize(
665      * Union(
666      * {[Time].LastSibling}, [Time].LastSibling.Children)),
667      * {[Measures].[Unit Sales], [Measures].[Store Cost]})</code>
668      * </blockquote>
669      *
670      * has type <code>{[Time.Monthly], [Measures]}</code> even though
671      * <code>[Time].LastSibling</code> might return a member of either
672      * [Time.Monthly] or [Time.Weekly].
673      */

674     private Hierarchy[] collectHierarchies(Exp queryPart) {
675         Type exprType = queryPart.getType();
676         if (exprType instanceof SetType) {
677             exprType = ((SetType) exprType).getElementType();
678         }
679         if (exprType instanceof TupleType) {
680             final Type[] types = ((TupleType) exprType).elementTypes;
681             ArrayList<Hierarchy> hierarchyList = new ArrayList<Hierarchy>();
682             for (Type type : types) {
683                 hierarchyList.add(getTypeHierarchy(type));
684             }
685             return hierarchyList.toArray(new Hierarchy[hierarchyList.size()]);
686         }
687         return new Hierarchy[] {getTypeHierarchy(exprType)};
688     }
689
690     private Hierarchy getTypeHierarchy(final Type type) {
691         Hierarchy hierarchy = type.getHierarchy();
692         if (hierarchy != null) {
693             return hierarchy;
694         }
695         final Dimension dimension = type.getDimension();
696         if (dimension != null) {
697             return dimension.getHierarchy();
698         }
699         return null;
700     }
701
702     /**
703      * Assigns a value to the parameter with a given name.
704      *
705      * @throws RuntimeException if there is not parameter with the given name
706      */

707     public void setParameter(String JavaDoc parameterName, String JavaDoc value) {
708         // Need to resolve query before we set parameters, in order to create
709
// slots to store them in. (This code will go away when parameters
710
// belong to prepared statements.)
711
if (parameters.isEmpty()) {
712             resolve();
713         }
714
715         Parameter param = getSchemaReader(false).getParameter(parameterName);
716         if (param == null) {
717             throw MondrianResource.instance().UnknownParameter.ex(parameterName);
718         }
719         if (!param.isModifiable()) {
720             throw MondrianResource.instance().ParameterIsNotModifiable.ex(
721                 parameterName, param.getScope().name());
722         }
723         final Exp exp = quickParse(
724             TypeUtil.typeToCategory(param.getType()), value, this);
725         param.setValue(exp);
726     }
727
728     private static Exp quickParse(int category, String JavaDoc value, Query query) {
729         switch (category) {
730         case Category.Numeric:
731             return Literal.create(new Double JavaDoc(value));
732         case Category.String:
733             return Literal.createString(value);
734         case Category.Member:
735             Member member = (Member) Util.lookup(query, Util.explode(value));
736             return new MemberExpr(member);
737         default:
738             throw Category.instance.badValue(category);
739         }
740     }
741
742     /**
743      * Swaps the x- and y- axes.
744      * Does nothing if the number of axes != 2.
745      */

746     public void swapAxes() {
747         if (axes.length == 2) {
748             Exp e0 = axes[0].getSet();
749             boolean nonEmpty0 = axes[0].isNonEmpty();
750             Exp e1 = axes[1].getSet();
751             boolean nonEmpty1 = axes[1].isNonEmpty();
752             axes[1].setSet(e0);
753             axes[1].setNonEmpty(nonEmpty0);
754             axes[0].setSet(e1);
755             axes[0].setNonEmpty(nonEmpty1);
756             // showSubtotals ???
757
}
758     }
759
760     /**
761      * Returns the parameters defined in this query.
762      */

763     public Parameter[] getParameters() {
764         return parameters.toArray(new Parameter[parameters.size()]);
765     }
766
767     public Cube getCube() {
768         return cube;
769     }
770
771     public SchemaReader getSchemaReader(boolean accessControlled) {
772         final Role role = accessControlled
773             ? getConnection().getRole()
774             : null;
775         final SchemaReader cubeSchemaReader = cube.getSchemaReader(role);
776         return new QuerySchemaReader(cubeSchemaReader);
777     }
778
779     /**
780      * Looks up a member whose unique name is <code>s</code> from cache.
781      * If the member is not in cache, returns null.
782      */

783     public Member lookupMemberFromCache(String JavaDoc s) {
784         // first look in defined members
785
for (Member member : getDefinedMembers()) {
786             if (Util.equalName(member.getUniqueName(), s)) {
787                 return member;
788             }
789         }
790         return null;
791     }
792
793     /**
794      * Looks up a named set.
795      */

796     private NamedSet lookupNamedSet(String JavaDoc name) {
797         for (Formula formula : formulas) {
798             if (!formula.isMember() &&
799                 formula.getElement() != null &&
800                 formula.getName().equals(name)) {
801                 return (NamedSet) formula.getElement();
802             }
803         }
804         return null;
805     }
806
807     /**
808      * Returns an array of the formulas used in this query.
809      */

810     public Formula[] getFormulas() {
811         return formulas;
812     }
813
814     /**
815      * Returns an array of this query's axes.
816      */

817     public QueryAxis[] getAxes() {
818         return axes;
819     }
820
821     /**
822      * Remove a formula from the query. If <code>failIfUsedInQuery</code> is
823      * true, checks and throws an error if formula is used somewhere in the
824      * query.
825      */

826     public void removeFormula(String JavaDoc uniqueName, boolean failIfUsedInQuery) {
827         Formula formula = findFormula(uniqueName);
828         if (failIfUsedInQuery && formula != null) {
829             OlapElement mdxElement = formula.getElement();
830             //search the query tree to see if this formula expression is used
831
//anywhere (on the axes or in another formula)
832
Walker walker = new Walker(this);
833             while (walker.hasMoreElements()) {
834                 Object JavaDoc queryElement = walker.nextElement();
835                 if (!queryElement.equals(mdxElement)) {
836                     continue;
837                 }
838                 // mdxElement is used in the query. lets find on on which axis
839
// or formula
840
String JavaDoc formulaType = formula.isMember()
841                     ? MondrianResource.instance().CalculatedMember.str()
842                     : MondrianResource.instance().CalculatedSet.str();
843
844                 int i = 0;
845                 Object JavaDoc parent = walker.getAncestor(i);
846                 Object JavaDoc grandParent = walker.getAncestor(i+1);
847                 while ((parent != null) && (grandParent != null)) {
848                     if (grandParent instanceof Query) {
849                         if (parent instanceof Axis) {
850                             throw MondrianResource.instance().
851                                 MdxCalculatedFormulaUsedOnAxis.ex(
852                                 formulaType,
853                                 uniqueName,
854                                 ((QueryAxis) parent).getAxisName());
855
856                         } else if (parent instanceof Formula) {
857                             String JavaDoc parentFormulaType =
858                                 ((Formula) parent).isMember()
859                                     ? MondrianResource.instance().CalculatedMember.str()
860                                     : MondrianResource.instance().CalculatedSet.str();
861                             throw MondrianResource.instance().
862                                 MdxCalculatedFormulaUsedInFormula.ex(
863                                 formulaType, uniqueName, parentFormulaType,
864                                 ((Formula) parent).getUniqueName());
865
866                         } else {
867                             throw MondrianResource.instance().
868                                 MdxCalculatedFormulaUsedOnSlicer.ex(
869                                 formulaType, uniqueName);
870                         }
871                     }
872                     ++i;
873                     parent = walker.getAncestor(i);
874                     grandParent = walker.getAncestor(i+1);
875                 }
876                 throw MondrianResource.instance().
877                     MdxCalculatedFormulaUsedInQuery.ex(
878                     formulaType, uniqueName, Util.unparse(this));
879             }
880         }
881
882         // remove formula from query
883
List<Formula> formulaList = new ArrayList<Formula>();
884         for (Formula formula1 : formulas) {
885             if (!formula1.getUniqueName().equalsIgnoreCase(uniqueName)) {
886                 formulaList.add(formula1);
887             }
888         }
889
890         // it has been found and removed
891
this.formulas = formulaList.toArray(new Formula[0]);
892     }
893
894     /**
895      * Check, whether a formula can be removed from the query.
896      */

897     public boolean canRemoveFormula(String JavaDoc uniqueName) {
898         Formula formula = findFormula(uniqueName);
899         if (formula == null) {
900             return false;
901         }
902
903         OlapElement mdxElement = formula.getElement();
904         //search the query tree to see if this formula expression is used
905
//anywhere (on the axes or in another formula)
906
Walker walker = new Walker(this);
907         while (walker.hasMoreElements()) {
908             Object JavaDoc queryElement = walker.nextElement();
909             if (!queryElement.equals(mdxElement)) {
910                 continue;
911             }
912             return false;
913         }
914         return true;
915     }
916
917     /** finds calculated member or set in array of formulas */
918     public Formula findFormula(String JavaDoc uniqueName) {
919         for (Formula formula : formulas) {
920             if (formula.getUniqueName().equalsIgnoreCase(uniqueName)) {
921                 return formula;
922             }
923         }
924         return null;
925     }
926
927     /**
928      * Finds formula by name and renames it to new name.
929      */

930     public void renameFormula(String JavaDoc uniqueName, String JavaDoc newName) {
931         Formula formula = findFormula(uniqueName);
932         if (formula == null) {
933             throw MondrianResource.instance().MdxFormulaNotFound.ex(
934                 "formula", uniqueName, Util.unparse(this));
935         }
936         formula.rename(newName);
937     }
938
939     List<Member> getDefinedMembers() {
940         List<Member> definedMembers = new ArrayList<Member>();
941         for (final Formula formula : formulas) {
942             if (formula.isMember() &&
943                 formula.getElement() != null &&
944                 getConnection().getRole().canAccess(formula.getElement())) {
945                 definedMembers.add((Member) formula.getElement());
946             }
947         }
948         return definedMembers;
949     }
950
951     /**
952      * Finds axis by index and sets flag to show empty cells on that axis.
953      */

954     public void setAxisShowEmptyCells(int axis, boolean showEmpty) {
955         if (axis >= axes.length) {
956             throw MondrianResource.instance().MdxAxisShowSubtotalsNotSupported.
957                 ex(axis);
958         }
959         axes[axis].setNonEmpty(!showEmpty);
960     }
961
962     /**
963      * Returns <code>Hierarchy[]</code> used on <code>axis</code>. It calls
964      * {@link #collectHierarchies}.
965      */

966     public Hierarchy[] getMdxHierarchiesOnAxis(AxisOrdinal axis) {
967         if (axis.logicalOrdinal() >= axes.length) {
968             throw MondrianResource.instance().MdxAxisShowSubtotalsNotSupported.
969                 ex(axis.logicalOrdinal());
970         }
971         QueryAxis queryAxis = (axis == AxisOrdinal.SLICER) ?
972                 slicerAxis :
973                 axes[axis.logicalOrdinal()];
974         return collectHierarchies(queryAxis.getSet());
975     }
976
977     public Calc compileExpression(Exp exp, boolean scalar) {
978         Evaluator evaluator = RolapEvaluator.create(this);
979         final Validator validator = createValidator();
980         final ExpCompiler compiler = createCompiler(evaluator, validator);
981         Calc calc = (scalar)
982             ? compiler.compileScalar(exp, false)
983             : compiler.compile(exp);
984         return calc;
985     }
986
987     private ExpCompiler createCompiler(
988             final Evaluator evaluator, final Validator validator) {
989
990         ExpCompiler compiler = ExpCompiler.Factory.getExpCompiler(
991                                     evaluator,
992                                     validator,
993                                     new ResultStyle[] { resultStyle });
994
995         final int expDeps = MondrianProperties.instance().TestExpDependencies.get();
996         if (expDeps > 0) {
997             compiler = RolapUtil.createDependencyTestingCompiler(compiler);
998         }
999         return compiler;
1000    }
1001
1002    /**
1003     * Keeps track of references to members of the measures dimension
1004     *
1005     * @param olapElement potential measure member
1006     */

1007    public void addMeasuresMembers(OlapElement olapElement)
1008    {
1009        if (olapElement instanceof Member) {
1010            Member member = (Member) olapElement;
1011            if (member.getDimension().getOrdinal(getCube()) == 0) {
1012                measuresMembers.add(member);
1013            }
1014        }
1015    }
1016
1017    /**
1018     * @return set of members from the measures dimension referenced within
1019     * this query
1020     */

1021    public Set<Member> getMeasuresMembers() {
1022        return Collections.unmodifiableSet(measuresMembers);
1023    }
1024
1025    /**
1026     * Indicates that the query cannot use native cross joins to process
1027     * this virtual cube
1028     */

1029    public void setVirtualCubeNonNativeCrossJoin() {
1030        nativeCrossJoinVirtualCube = false;
1031    }
1032
1033    /**
1034     * @return true if the query can use native cross joins on a virtual
1035     * cube
1036     */

1037    public boolean nativeCrossJoinVirtualCube() {
1038        return nativeCrossJoinVirtualCube;
1039    }
1040
1041    /**
1042     * Saves away the level to column maps for the underlying cubes that make
1043     * up the virtual cube referenced in this query
1044     *
1045     * @param maps the set of maps to be saved
1046     */

1047    public void setVirtualCubeBaseCubeMaps(Set<Map<RolapLevel, RolapStar.Column>> maps)
1048    {
1049        virtualCubeBaseCubeMaps = maps;
1050    }
1051
1052    /**
1053     * @return the set of level to column maps associated with the virtual
1054     * cube this query references
1055     */

1056    public Set<Map<RolapLevel, RolapStar.Column>> getVirtualCubeBaseCubeMaps()
1057    {
1058        return virtualCubeBaseCubeMaps;
1059    }
1060
1061    /**
1062     * Saves away the map that maps a level-to-column map to a measure
1063     *
1064     * @param map map to be saved
1065     */

1066    public void setLevelMapToMeasureMap(
1067        Map<Map<RolapLevel, RolapStar.Column>, RolapMember> map)
1068    {
1069        levelMapToMeasureMap = map;
1070    }
1071
1072    /**
1073     * @return the level-to-column-to-measure map
1074     */

1075    public Map<Map<RolapLevel, RolapStar.Column>, RolapMember> getLevelMapToMeasureMap()
1076    {
1077        return levelMapToMeasureMap;
1078    }
1079
1080    public Object JavaDoc accept(MdxVisitor visitor) {
1081        Object JavaDoc o = visitor.visit(this);
1082
1083        // visit formulas
1084
for (Formula formula : formulas) {
1085            formula.accept(visitor);
1086        }
1087        // visit axes
1088
for (QueryAxis axis : axes) {
1089            axis.accept(visitor);
1090        }
1091        if (slicerAxis != null) {
1092            slicerAxis.accept(visitor);
1093        }
1094
1095        return o;
1096    }
1097
1098    /**
1099     * Put an Object value into the evaluation cache with given key.
1100     * This is used by Calc's to store information between iterations
1101     * (rather than re-generate each time).
1102     *
1103     * @param key the cache key
1104     * @param value the cache value
1105     */

1106    public void putEvalCache(String JavaDoc key, Object JavaDoc value) {
1107        evalCache.put(key, value);
1108    }
1109    
1110    /**
1111     * Get the Object associated with the value.
1112     *
1113     * @param key the cache key
1114     * @return the cached value or null.
1115     */

1116    public Object JavaDoc getEvalCache(String JavaDoc key) {
1117        return evalCache.get(key);
1118    }
1119
1120    /**
1121     * Remove all entries in the evaluation cache
1122     */

1123    public void clearEvalCache() {
1124        evalCache.clear();
1125    }
1126
1127    /**
1128     * Default implementation of {@link Validator}.
1129     *
1130     * <p>Uses a stack to help us guess the type of our parent expression
1131     * before we've completely resolved our children -- necessary,
1132     * unfortunately, when figuring out whether the "*" operator denotes
1133     * multiplication or crossjoin.
1134     *
1135     * <p>Keeps track of which nodes have already been resolved, so we don't
1136     * try to resolve nodes which have already been resolved. (That would not
1137     * be wrong, but can cause resolution to be an <code>O(2^N)</code>
1138     * operation.)
1139     */

1140    private class StackValidator implements Validator {
1141        private final Stack<QueryPart> stack = new Stack<QueryPart>();
1142        private final FunTable funTable;
1143        private final Map<QueryPart, QueryPart> resolvedNodes =
1144            new HashMap<QueryPart, QueryPart>();
1145        private final QueryPart placeHolder = Literal.zero;
1146
1147        /**
1148         * Creates a StackValidator.
1149         *
1150         * @pre funTable != null
1151         */

1152        public StackValidator(FunTable funTable) {
1153            Util.assertPrecondition(funTable != null, "funTable != null");
1154            this.funTable = funTable;
1155        }
1156
1157        public Query getQuery() {
1158            return Query.this;
1159        }
1160
1161        public Exp validate(Exp exp, boolean scalar) {
1162            Exp resolved;
1163            try {
1164                resolved = (Exp) resolvedNodes.get(exp);
1165            } catch (ClassCastException JavaDoc e) {
1166                // A classcast exception will occur if there is a String
1167
// placeholder in the map. This is an internal error -- should
1168
// not occur for any query, valid or invalid.
1169
throw Util.newInternal(
1170                    e,
1171                    "Infinite recursion encountered while validating '" +
1172                        Util.unparse(exp) + "'");
1173            }
1174            if (resolved == null) {
1175                try {
1176                    stack.push((QueryPart) exp);
1177                    // To prevent recursion, put in a placeholder while we're
1178
// resolving.
1179
resolvedNodes.put((QueryPart) exp, placeHolder);
1180                    resolved = exp.accept(this);
1181                    Util.assertTrue(resolved != null);
1182                    resolvedNodes.put((QueryPart) exp, (QueryPart) resolved);
1183                } finally {
1184                    stack.pop();
1185                }
1186            }
1187
1188            if (scalar) {
1189                final Type type = resolved.getType();
1190                if (!TypeUtil.canEvaluate(type)) {
1191                    String JavaDoc exprString = Util.unparse(resolved);
1192                    throw MondrianResource.instance().MdxMemberExpIsSet.ex(exprString);
1193                }
1194            }
1195
1196            return resolved;
1197        }
1198
1199        public void validate(ParameterExpr parameterExpr) {
1200            ParameterExpr resolved =
1201                (ParameterExpr) resolvedNodes.get(parameterExpr);
1202            if (resolved != null) {
1203                return; // already resolved
1204
}
1205            try {
1206                stack.push(parameterExpr);
1207                resolvedNodes.put(parameterExpr, placeHolder);
1208                resolved = (ParameterExpr) parameterExpr.accept(this);
1209                assert resolved != null;
1210                resolvedNodes.put(parameterExpr, resolved);
1211            } finally {
1212                stack.pop();
1213            }
1214        }
1215
1216        public void validate(MemberProperty memberProperty) {
1217            MemberProperty resolved =
1218                    (MemberProperty) resolvedNodes.get(memberProperty);
1219            if (resolved != null) {
1220                return; // already resolved
1221
}
1222            try {
1223                stack.push(memberProperty);
1224                resolvedNodes.put(memberProperty, placeHolder);
1225                memberProperty.resolve(this);
1226                resolvedNodes.put(memberProperty, memberProperty);
1227            } finally {
1228                stack.pop();
1229            }
1230        }
1231
1232        public void validate(QueryAxis axis) {
1233            final QueryAxis resolved = (QueryAxis) resolvedNodes.get(axis);
1234            if (resolved != null) {
1235                return; // already resolved
1236
}
1237            try {
1238                stack.push(axis);
1239                resolvedNodes.put(axis, placeHolder);
1240                axis.resolve(this);
1241                resolvedNodes.put(axis, axis);
1242            } finally {
1243                stack.pop();
1244            }
1245        }
1246
1247        public void validate(Formula formula) {
1248            final Formula resolved = (Formula) resolvedNodes.get(formula);
1249            if (resolved != null) {
1250                return; // already resolved
1251
}
1252            try {
1253                stack.push(formula);
1254                resolvedNodes.put(formula, placeHolder);
1255                formula.accept(this);
1256                resolvedNodes.put(formula, formula);
1257            } finally {
1258                stack.pop();
1259            }
1260        }
1261
1262        public boolean canConvert(Exp fromExp, int to, int[] conversionCount) {
1263            return FunUtil.canConvert(fromExp, to, conversionCount);
1264        }
1265
1266        public boolean requiresExpression() {
1267            return requiresExpression(stack.size() - 1);
1268        }
1269
1270        private boolean requiresExpression(int n) {
1271            if (n < 1) {
1272                return false;
1273            }
1274            final Object JavaDoc parent = stack.get(n - 1);
1275            if (parent instanceof Formula) {
1276                return ((Formula) parent).isMember();
1277            } else if (parent instanceof ResolvedFunCall) {
1278                final ResolvedFunCall funCall = (ResolvedFunCall) parent;
1279                if (funCall.getFunDef().getSyntax() == Syntax.Parentheses) {
1280                    return requiresExpression(n - 1);
1281                } else {
1282                    int k = whichArg(funCall, (Exp) stack.get(n));
1283                    if (k < 0) {
1284                        // Arguments of call have mutated since call was placed
1285
// on stack. Presumably the call has already been
1286
// resolved correctly, so the answer we give here is
1287
// irrelevant.
1288
return false;
1289                    }
1290                    final FunDef funDef = funCall.getFunDef();
1291                    final int[] parameterTypes = funDef.getParameterCategories();
1292                    return parameterTypes[k] != Category.Set;
1293                }
1294            } else if (parent instanceof UnresolvedFunCall) {
1295                final UnresolvedFunCall funCall = (UnresolvedFunCall) parent;
1296                if (funCall.getSyntax() == Syntax.Parentheses) {
1297                    return requiresExpression(n - 1);
1298                } else {
1299                    int k = whichArg(funCall, (Exp) stack.get(n));
1300                    if (k < 0) {
1301                        // Arguments of call have mutated since call was placed
1302
// on stack. Presumably the call has already been
1303
// resolved correctly, so the answer we give here is
1304
// irrelevant.
1305
return false;
1306                    }
1307                    return funTable.requiresExpression(funCall, k, this);
1308                }
1309            } else {
1310                return false;
1311            }
1312        }
1313
1314        public FunTable getFunTable() {
1315            return funTable;
1316        }
1317
1318        public Parameter createOrLookupParam(
1319            boolean definition,
1320            String JavaDoc name,
1321            Type type,
1322            Exp defaultExp,
1323            String JavaDoc description)
1324        {
1325            final SchemaReader schemaReader = getSchemaReader(false);
1326            Parameter param = schemaReader.getParameter(name);
1327
1328            if (definition) {
1329                if (param != null) {
1330                    if (param.getScope() == Parameter.Scope.Statement) {
1331                        ParameterImpl paramImpl = (ParameterImpl) param;
1332                        paramImpl.setDescription(description);
1333                        paramImpl.setDefaultExp(defaultExp);
1334                        paramImpl.setType(type);
1335                    }
1336                    return param;
1337                }
1338                param = new ParameterImpl(
1339                    name,
1340                    defaultExp, description, type);
1341
1342                // Append it to the list of known parameters.
1343
parameters.add(param);
1344                parametersByName.put(name, param);
1345                return param;
1346            } else {
1347                if (param != null) {
1348                    return param;
1349                }
1350                throw MondrianResource.instance().UnknownParameter.ex(name);
1351            }
1352        }
1353
1354        private int whichArg(final FunCall node, final Exp arg) {
1355            final Exp[] children = node.getArgs();
1356            for (int i = 0; i < children.length; i++) {
1357                if (children[i] == arg) {
1358                    return i;
1359                }
1360            }
1361            return -1;
1362        }
1363    }
1364
1365    /**
1366     * Source of metadata within the scope of a query.
1367     *
1368     * <p>Note especially that {@link #getCalculatedMember(String[])}
1369     * returns the calculated members defined in this query.
1370     */

1371    private class QuerySchemaReader extends DelegatingSchemaReader {
1372
1373        public QuerySchemaReader(SchemaReader cubeSchemaReader) {
1374            super(cubeSchemaReader);
1375        }
1376
1377        public Member getMemberByUniqueName(
1378            String JavaDoc[] uniqueNameParts,
1379            boolean failIfNotFound)
1380        {
1381            return getMemberByUniqueName(
1382                uniqueNameParts, failIfNotFound, MatchType.EXACT);
1383        }
1384
1385        public Member getMemberByUniqueName(
1386                String JavaDoc[] uniqueNameParts,
1387                boolean failIfNotFound,
1388                MatchType matchType)
1389        {
1390            final String JavaDoc uniqueName = Util.implode(uniqueNameParts);
1391            Member member = lookupMemberFromCache(uniqueName);
1392            if (member == null) {
1393                // Not a calculated member in the query, so go to the cube.
1394
member = schemaReader.getMemberByUniqueName(
1395                    uniqueNameParts, failIfNotFound, matchType);
1396            }
1397            if (!failIfNotFound && member == null) {
1398                return null;
1399            }
1400            if (getRole().canAccess(member)) {
1401                return member;
1402            } else {
1403                return null;
1404            }
1405        }
1406
1407        public Member[] getLevelMembers(
1408                Level level, boolean includeCalculated) {
1409            Member[] members = super.getLevelMembers(level, false);
1410            if (includeCalculated) {
1411                members = Util.addLevelCalculatedMembers(this, level, members);
1412            }
1413            return members;
1414        }
1415
1416        public Member getCalculatedMember(String JavaDoc[] nameParts) {
1417            final String JavaDoc uniqueName = Util.implode(nameParts);
1418            return lookupMemberFromCache(uniqueName);
1419        }
1420
1421        public List<Member> getCalculatedMembers(Hierarchy hierarchy) {
1422            List<Member> result = new ArrayList<Member>();
1423            // Add calculated members in the cube.
1424
final List<Member> calculatedMembers =
1425                super.getCalculatedMembers(hierarchy);
1426            result.addAll(calculatedMembers);
1427            // Add calculated members defined in the query.
1428
for (Member member : getDefinedMembers()) {
1429                if (member.getHierarchy().equals(hierarchy)) {
1430                    result.add(member);
1431                }
1432            }
1433            return result;
1434        }
1435
1436        public List<Member> getCalculatedMembers(Level level) {
1437            List<Member> hierarchyMembers =
1438                getCalculatedMembers(level.getHierarchy());
1439            List<Member> result = new ArrayList<Member>();
1440            for (Member member : hierarchyMembers) {
1441                if (member.getLevel().equals(level)) {
1442                    result.add(member);
1443                }
1444            }
1445            return result;
1446        }
1447
1448        public List<Member> getCalculatedMembers() {
1449            return getDefinedMembers();
1450        }
1451
1452        public OlapElement getElementChild(OlapElement parent, String JavaDoc s)
1453        {
1454            return getElementChild(parent, s, MatchType.EXACT);
1455        }
1456
1457        public OlapElement getElementChild(
1458            OlapElement parent, String JavaDoc s, MatchType matchType)
1459        {
1460            // first look in cube
1461
OlapElement mdxElement =
1462                schemaReader.getElementChild(parent, s, matchType);
1463            if (mdxElement != null) {
1464                return mdxElement;
1465            }
1466            // then look in defined members (removed sf#1084651)
1467

1468            // then in defined sets
1469
for (Formula formula : formulas) {
1470                if (formula.isMember()) {
1471                    continue; // have already done these
1472
}
1473                if (Util.equalName(formula.getNames()[0], s)) {
1474                    return formula.getNamedSet();
1475                }
1476            }
1477
1478            return mdxElement;
1479        }
1480
1481        public OlapElement lookupCompound(
1482            OlapElement parent,
1483            String JavaDoc[] names,
1484            boolean failIfNotFound,
1485            int category)
1486        {
1487            return lookupCompound(
1488                parent, names, failIfNotFound, category, MatchType.EXACT);
1489        }
1490
1491        public OlapElement lookupCompound(
1492                OlapElement parent,
1493                String JavaDoc[] names,
1494                boolean failIfNotFound,
1495                int category,
1496                MatchType matchType)
1497        {
1498            // First look to ourselves.
1499
switch (category) {
1500            case Category.Unknown:
1501            case Category.Member:
1502                if (parent == cube) {
1503                    final Member calculatedMember = getCalculatedMember(names);
1504                    if (calculatedMember != null) {
1505                        return calculatedMember;
1506                    }
1507                }
1508            }
1509            switch (category) {
1510            case Category.Unknown:
1511            case Category.Set:
1512                if (parent == cube) {
1513                    final NamedSet namedSet = getNamedSet(names);
1514                    if (namedSet != null) {
1515                        return namedSet;
1516                    }
1517                }
1518            }
1519            // Then delegate to the next reader.
1520
OlapElement olapElement = super.lookupCompound(
1521                    parent, names, failIfNotFound, category, matchType);
1522            if (olapElement instanceof Member) {
1523                Member member = (Member) olapElement;
1524                final Formula formula = (Formula)
1525                    member.getPropertyValue(Property.FORMULA.name);
1526                if (formula != null) {
1527                    // This is a calculated member defined against the cube.
1528
// Create a free-standing formula using the same
1529
// expression, then use the member defined in that formula.
1530
final Formula formulaClone = (Formula) formula.clone();
1531                    formulaClone.createElement(Query.this);
1532                    formulaClone.accept(createValidator());
1533                    olapElement = formulaClone.getMdxMember();
1534                }
1535            }
1536            return olapElement;
1537        }
1538
1539        public NamedSet getNamedSet(String JavaDoc[] nameParts) {
1540            if (nameParts.length != 1) {
1541                return null;
1542            }
1543            return lookupNamedSet(nameParts[0]);
1544        }
1545
1546        public Parameter getParameter(String JavaDoc name) {
1547            // Look for a parameter defined in the query.
1548
for (Parameter parameter : parameters) {
1549                if (parameter.getName().equals(name)) {
1550                    return parameter;
1551                }
1552            }
1553
1554            // Look for a parameter defined in this connection.
1555
if (Util.lookup(RolapConnectionProperties.class, name) != null) {
1556                Object JavaDoc value = connection.getProperty(name);
1557                // TODO: Don't assume it's a string.
1558
// TODO: Create expression which will get the value from the
1559
// connection at the time the query is executed.
1560
Literal defaultValue =
1561                    Literal.createString(String.valueOf(value));
1562                return new ConnectionParameterImpl(name, defaultValue);
1563            }
1564
1565            return super.getParameter(name);
1566        }
1567
1568    }
1569
1570    private static class ConnectionParameterImpl
1571        extends ParameterImpl
1572    {
1573        public ConnectionParameterImpl(String JavaDoc name, Literal defaultValue) {
1574            super(name, defaultValue, "Connection property", new StringType());
1575        }
1576
1577        public Scope getScope() {
1578            return Scope.Connection;
1579        }
1580
1581        public void setValue(Object JavaDoc value) {
1582            throw MondrianResource.instance().ParameterIsNotModifiable.ex(
1583                getName(), getScope().name());
1584        }
1585    }
1586}
1587
1588// End Query.java
1589
Popular Tags