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