001    /*
002    // $Id: //open/mondrian/src/main/mondrian/olap/fun/CastFunDef.java#6 $
003    // This software is subject to the terms of the Common Public License
004    // Agreement, available at the following URL:
005    // http://www.opensource.org/licenses/cpl.html.
006    // Copyright (C) 2006-2008 Julian Hyde
007    // All Rights Reserved.
008    // You must accept the terms of that agreement to use this software.
009    */
010    package mondrian.olap.fun;
011    
012    import mondrian.olap.*;
013    import mondrian.olap.type.Type;
014    import mondrian.resource.MondrianResource;
015    import mondrian.calc.Calc;
016    import mondrian.calc.ExpCompiler;
017    import mondrian.calc.impl.GenericCalc;
018    import mondrian.mdx.ResolvedFunCall;
019    
020    /**
021     * Definition of the <code>CAST</code> MDX operator.
022     *
023     * <p><code>CAST</code> is a mondrian-specific extension to MDX, because the MDX
024     * standard does not define how values are to be converted from one
025     * type to another. Microsoft Analysis Services, for Resolver, uses the Visual
026     * Basic functions <code>CStr</code>, <code>CInt</code>, etc.
027     * The syntax for this operator was inspired by the <code>CAST</code> operator
028     * in the SQL standard.
029     *
030     * <p>Examples:<ul>
031     * <li><code>CAST(1 + 2 AS STRING)</code></li>
032     * <li><code>CAST('12.' || '56' AS NUMERIC)</code></li>
033     * <li><code>CAST('tr' || 'ue' AS BOOLEAN)</code></li>
034     * </ul>
035     *
036     * @author jhyde
037     * @version $Id: //open/mondrian/src/main/mondrian/olap/fun/CastFunDef.java#6 $
038     * @since Sep 3, 2006
039     */
040    public class CastFunDef extends FunDefBase {
041        static final ResolverBase Resolver = new ResolverImpl();
042    
043        private CastFunDef(FunDef dummyFunDef) {
044            super(dummyFunDef);
045        }
046    
047        public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) {
048            final Type targetType = call.getType();
049            final Exp arg = call.getArg(0);
050            final Calc calc = compiler.compileScalar(arg, false);
051            return new CalcImpl(arg, calc, targetType);
052        }
053    
054        private static RuntimeException cannotConvert(
055            Object o,
056            final Type targetType)
057        {
058            return Util.newInternal(
059                "cannot convert value '" + o +
060                "' to targetType '" + targetType +
061                "'");
062        }
063    
064        public static int toInt(
065            Object o,
066            final Type targetType)
067        {
068            if (o == null) {
069                return FunUtil.IntegerNull;
070            }
071            if (o instanceof String) {
072                return Integer.parseInt((String) o);
073            }
074            if (o instanceof Number) {
075                return ((Number) o).intValue();
076            }
077            throw cannotConvert(o, targetType);
078        }
079    
080        private static double toDouble(Object o, final Type targetType) {
081            if (o == null) {
082                return FunUtil.DoubleNull;
083            }
084            if (o instanceof String) {
085                return Double.valueOf((String) o);
086            }
087            if (o instanceof Number) {
088                return ((Number) o).doubleValue();
089            }
090            throw cannotConvert(o, targetType);
091        }
092    
093        public static boolean toBoolean(Object o, final Type targetType) {
094            if (o == null) {
095                return FunUtil.BooleanNull;
096            }
097            if (o instanceof Boolean) {
098                return (Boolean) o;
099            }
100            if (o instanceof String) {
101                return Boolean.valueOf((String) o);
102            }
103            if (o instanceof Number) {
104                return ((Number) o).doubleValue() > 0;
105            }
106            throw cannotConvert(o, targetType);
107        }
108    
109        /**
110         * Resolves calls to the CAST operator.
111         */
112        private static class ResolverImpl extends ResolverBase {
113    
114            public ResolverImpl() {
115                super("Cast", "Cast(<Expression> AS <Type>)",
116                    "Converts values to another type.", Syntax.Cast);
117            }
118    
119            public FunDef resolve(
120                Exp[] args, Validator validator, int[] conversionCount) {
121                if (args.length != 2) {
122                    return null;
123                }
124                if (!(args[1] instanceof Literal)) {
125                    return null;
126                }
127                Literal literal = (Literal) args[1];
128                String typeName = (String) literal.getValue();
129                int returnCategory;
130                if (typeName.equalsIgnoreCase("String")) {
131                    returnCategory = Category.String;
132                } else if (typeName.equalsIgnoreCase("Numeric")) {
133                    returnCategory = Category.Numeric;
134                } else if (typeName.equalsIgnoreCase("Boolean")) {
135                    returnCategory = Category.Logical;
136                } else if (typeName.equalsIgnoreCase("Integer")) {
137                    returnCategory = Category.Integer;
138                } else {
139                    throw MondrianResource.instance().CastInvalidType.ex(typeName);
140                }
141                final FunDef dummyFunDef =
142                    createDummyFunDef(this, returnCategory, args);
143                return new CastFunDef(dummyFunDef);
144            }
145        }
146    
147        private static class CalcImpl extends GenericCalc {
148            private final Calc calc;
149            private final Type targetType;
150    
151            public CalcImpl(Exp arg, Calc calc, Type targetType) {
152                super(arg);
153                this.calc = calc;
154                this.targetType = targetType;
155            }
156    
157            public Calc[] getCalcs() {
158                return new Calc[] {calc};
159            }
160    
161            public Object evaluate(Evaluator evaluator) {
162                return calc.evaluate(evaluator);
163            }
164    
165            public String evaluateString(Evaluator evaluator) {
166                final Object o = evaluate(evaluator);
167                if (o == null) {
168                    return null;
169                }
170                return String.valueOf(o);
171            }
172    
173            public int evaluateInteger(Evaluator evaluator) {
174                final Object o = evaluate(evaluator);
175                return toInt(o, targetType);
176            }
177    
178            public double evaluateDouble(Evaluator evaluator) {
179                final Object o = evaluate(evaluator);
180                return toDouble(o, targetType);
181            }
182    
183            public boolean evaluateBoolean(Evaluator evaluator) {
184                final Object o = evaluate(evaluator);
185                return toBoolean(o, targetType);
186            }
187        }
188    }
189    
190    // End CastFunDef.java