001 /*
002 // $Id: //open/mondrian-release/3.0/src/main/mondrian/olap/fun/CastFunDef.java#2 $
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-release/3.0/src/main/mondrian/olap/fun/CastFunDef.java#2 $
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