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