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) 2004-2005 Julian Hyde 008// Copyright (C) 2005-2012 Pentaho and others 009// All Rights Reserved. 010*/ 011package mondrian.olap.fun; 012 013import mondrian.calc.*; 014import mondrian.calc.impl.GenericCalc; 015import mondrian.mdx.ResolvedFunCall; 016import mondrian.olap.*; 017 018import java.util.List; 019 020/** 021 * Definition of the <code>Properties</code> MDX function. 022 * 023 * @author jhyde 024 * @since Mar 23, 2006 025 */ 026class PropertiesFunDef extends FunDefBase { 027 static final ResolverImpl Resolver = new ResolverImpl(); 028 029 public PropertiesFunDef( 030 String name, 031 String signature, 032 String description, 033 Syntax syntax, 034 int returnType, 035 int[] parameterTypes) 036 { 037 super(name, signature, description, syntax, returnType, parameterTypes); 038 } 039 040 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) { 041 final MemberCalc memberCalc = compiler.compileMember(call.getArg(0)); 042 final StringCalc stringCalc = compiler.compileString(call.getArg(1)); 043 return new GenericCalc(call) { 044 public Object evaluate(Evaluator evaluator) { 045 return properties( 046 memberCalc.evaluateMember(evaluator), 047 stringCalc.evaluateString(evaluator)); 048 } 049 050 public Calc[] getCalcs() { 051 return new Calc[] {memberCalc, stringCalc}; 052 } 053 }; 054 } 055 056 static Object properties(Member member, String s) { 057 boolean matchCase = MondrianProperties.instance().CaseSensitive.get(); 058 Object o = member.getPropertyValue(s, matchCase); 059 if (o == null) { 060 if (!Util.isValidProperty(s, member.getLevel())) { 061 throw new MondrianEvaluationException( 062 "Property '" + s 063 + "' is not valid for member '" + member + "'"); 064 } 065 } 066 return o; 067 } 068 069 /** 070 * Resolves calls to the <code>PROPERTIES</code> MDX function. 071 */ 072 private static class ResolverImpl extends ResolverBase { 073 private static final int[] PARAMETER_TYPES = { 074 Category.Member, Category.String 075 }; 076 077 private ResolverImpl() { 078 super( 079 "Properties", 080 "<Member>.Properties(<String Expression>)", 081 "Returns the value of a member property.", 082 Syntax.Method); 083 } 084 085 private boolean matches( 086 Exp[] args, 087 int[] parameterTypes, 088 Validator validator, 089 List<Conversion> conversions) 090 { 091 if (parameterTypes.length != args.length) { 092 return false; 093 } 094 for (int i = 0; i < args.length; i++) { 095 if (!validator.canConvert( 096 i, args[i], parameterTypes[i], conversions)) 097 { 098 return false; 099 } 100 } 101 return true; 102 } 103 104 public FunDef resolve( 105 Exp[] args, 106 Validator validator, 107 List<Conversion> conversions) 108 { 109 if (!matches(args, PARAMETER_TYPES, validator, conversions)) { 110 return null; 111 } 112 int returnType = deducePropertyCategory(args[0], args[1]); 113 return new PropertiesFunDef( 114 getName(), getSignature(), getDescription(), getSyntax(), 115 returnType, PARAMETER_TYPES); 116 } 117 118 /** 119 * Deduces the category of a property. This is possible only if the 120 * name is a string literal, and the member's hierarchy is unambigous. 121 * If the type cannot be deduced, returns {@link Category#Value}. 122 * 123 * @param memberExp Expression for the member 124 * @param propertyNameExp Expression for the name of the property 125 * @return Category of the property 126 */ 127 private int deducePropertyCategory( 128 Exp memberExp, 129 Exp propertyNameExp) 130 { 131 if (!(propertyNameExp instanceof Literal)) { 132 return Category.Value; 133 } 134 String propertyName = 135 (String) ((Literal) propertyNameExp).getValue(); 136 Hierarchy hierarchy = memberExp.getType().getHierarchy(); 137 if (hierarchy == null) { 138 return Category.Value; 139 } 140 Level[] levels = hierarchy.getLevels(); 141 Property property = lookupProperty( 142 levels[levels.length - 1], propertyName); 143 if (property == null) { 144 // we'll likely get a runtime error 145 return Category.Value; 146 } else { 147 switch (property.getType()) { 148 case TYPE_BOOLEAN: 149 return Category.Logical; 150 case TYPE_NUMERIC: 151 return Category.Numeric; 152 case TYPE_STRING: 153 return Category.String; 154 case TYPE_DATE: 155 case TYPE_TIME: 156 case TYPE_TIMESTAMP: 157 return Category.DateTime; 158 default: 159 throw Util.badValue(property.getType()); 160 } 161 } 162 } 163 164 public boolean requiresExpression(int k) { 165 return true; 166 } 167 } 168} 169 170// End PropertiesFunDef.java