001/*
002// This software is subject to the terms of the Eclipse Public License v1.0
003// Agreement, available at the following URL:
004// http://www.eclipse.org/legal/epl-v10.html.
005// You must accept the terms of that agreement to use this software.
006//
007// Copyright (C) 2006-2009 Pentaho
008// All Rights Reserved.
009*/
010package mondrian.mdx;
011
012import mondrian.calc.*;
013import mondrian.olap.*;
014import mondrian.olap.type.*;
015
016import java.io.PrintWriter;
017import java.util.List;
018
019/**
020 * MDX expression which is a usage of a {@link mondrian.olap.Parameter}.
021 *
022 * @author jhyde
023 */
024public class ParameterExpr extends ExpBase {
025
026    private Parameter parameter;
027
028    /**
029     * Creates a ParameterExpr.
030     *
031     * @param parameter Parameter
032     */
033    public ParameterExpr(Parameter parameter)
034    {
035        this.parameter = parameter;
036    }
037
038    public Type getType() {
039        return parameter.getType();
040    }
041
042    public int getCategory() {
043        return TypeUtil.typeToCategory(parameter.getType());
044    }
045
046    public Exp accept(Validator validator) {
047        // There must be some Parameter with this name registered with the
048        // Query.  After clone(), there will be many copies of the same
049        // parameter, and we rely on this method to bring them down to one.
050        // So if this object is not the registered vesion, that's fine, go with
051        // the other one.  The registered one will be resolved after everything
052        // else in the query has been resolved.
053        String parameterName = parameter.getName();
054        final SchemaReader schemaReader =
055            validator.getQuery().getSchemaReader(false);
056        Parameter p = schemaReader.getParameter(parameterName);
057        if (p == null) {
058            this.parameter =
059                validator.createOrLookupParam(
060                    true,
061                    parameter.getName(),
062                    parameter.getType(),
063                    parameter.getDefaultExp(),
064                    parameter.getDescription());
065        } else {
066            this.parameter = p;
067        }
068        return this;
069    }
070
071    public Calc accept(ExpCompiler compiler) {
072        return ((ParameterCompilable) parameter).compile(compiler);
073    }
074
075    public Object accept(MdxVisitor visitor) {
076        return visitor.visit(this);
077    }
078
079    public ParameterExpr clone() {
080        return new ParameterExpr(parameter);
081    }
082
083    /**
084     * Unparses the definition of this Parameter.
085     *
086     * <p>The first usage of a parameter in a query becomes a call to the
087     * <code>Parameter(paramName, description, defaultValue)</code>
088     * function, and subsequent usages become calls to
089     * <code>ParamRef(paramName)</code>
090     *
091     * @param pw PrintWriter
092     */
093    public void unparse(PrintWriter pw) {
094        // Is this the first time we've seen a statement parameter? If so,
095        // we will generate a call to the Parameter() function, to define
096        // the parameter.
097        final boolean def;
098        if (pw instanceof QueryPrintWriter
099            && parameter.getScope() == Parameter.Scope.Statement)
100        {
101            def = ((QueryPrintWriter) pw).parameters.add(parameter);
102        } else {
103            def = false;
104        }
105        final String name = parameter.getName();
106        final Type type = parameter.getType();
107        final int category = TypeUtil.typeToCategory(type);
108        if (def) {
109            pw.print("Parameter(" + Util.quoteForMdx(name) + ", ");
110            switch (category) {
111            case Category.String:
112            case Category.Numeric:
113                pw.print(Category.instance.getName(category).toUpperCase());
114                break;
115            case Category.Member:
116                pw.print(uniqueName(type));
117                break;
118            case Category.Set:
119                Type elementType = ((SetType) type).getElementType();
120                pw.print(uniqueName(elementType));
121                break;
122            default:
123                throw Category.instance.badValue(category);
124            }
125            pw.print(", ");
126            final Object value = parameter.getValue();
127            if (value == null) {
128                parameter.getDefaultExp().unparse(pw);
129            } else if (value instanceof String) {
130                String s = (String) value;
131                pw.print(Util.quoteForMdx(s));
132            } else if (value instanceof List) {
133                List list = (List) value;
134                pw.print("{");
135                int i = -1;
136                for (Object o : list) {
137                    ++i;
138                    if (i > 0) {
139                        pw.print(", ");
140                    }
141                    pw.print(o);
142                }
143                pw.print("}");
144            } else {
145                pw.print(value);
146            }
147            final String description = parameter.getDescription();
148            if (description != null) {
149                pw.print(", " + Util.quoteForMdx(description));
150            }
151            pw.print(")");
152        } else {
153            pw.print("ParamRef(" + Util.quoteForMdx(name) + ")");
154        }
155    }
156
157    /**
158     * Returns the unique name of the level, hierarchy, or dimension of this
159     * type, whichever is most specific.
160     *
161     * @param type Type
162     * @return Most specific description of type
163     */
164    private String uniqueName(Type type) {
165        if (type.getLevel() != null) {
166            return type.getLevel().getUniqueName();
167        } else if (type.getHierarchy() != null) {
168            return type.getHierarchy().getUniqueName();
169        } else {
170            return type.getDimension().getUniqueName();
171        }
172    }
173
174    // For the purposes of type inference and expression substitution, a
175    // parameter is atomic; therefore, we ignore the child member, if any.
176    public Object[] getChildren() {
177        return null;
178    }
179
180    /**
181     * Returns whether this parameter is equal to another, based upon name,
182     * type and value
183     */
184    public boolean equals(Object other) {
185        if (!(other instanceof ParameterExpr)) {
186            return false;
187        }
188        ParameterExpr that = (ParameterExpr) other;
189        return this.parameter == that.parameter;
190    }
191
192    public int hashCode() {
193        return parameter.hashCode();
194    }
195
196    /**
197     * Returns whether the parameter can be modified.
198     *
199     * @return whether parameter can be modified
200     */
201    public boolean isModifiable() {
202        return true;
203    }
204
205    /**
206     * Returns the parameter used by this expression.
207     *
208     * @return parameter used by this expression
209     */
210    public Parameter getParameter() {
211        return parameter;
212    }
213}
214
215// End ParameterExpr.java