001 /* 002 // $Id: //open/mondrian/src/main/mondrian/olap/fun/TupleFunDef.java#20 $ 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) 2002-2002 Kana Software, Inc. 007 // Copyright (C) 2002-2008 Julian Hyde and others 008 // All Rights Reserved. 009 // You must accept the terms of that agreement to use this software. 010 // 011 // jhyde, 3 March, 2002 012 */ 013 package mondrian.olap.fun; 014 import mondrian.olap.*; 015 import mondrian.olap.type.*; 016 import mondrian.resource.MondrianResource; 017 import mondrian.calc.*; 018 import mondrian.calc.impl.AbstractTupleCalc; 019 import mondrian.mdx.ResolvedFunCall; 020 021 import java.io.PrintWriter; 022 023 /** 024 * <code>TupleFunDef</code> implements the '(...)' operator which builds 025 * tuples, as in <code>([Time].CurrentMember, 026 * [Stores].[USA].[California])</code>. 027 * 028 * @author jhyde 029 * @since 3 March, 2002 030 * @version $Id: //open/mondrian/src/main/mondrian/olap/fun/TupleFunDef.java#20 $ 031 */ 032 public class TupleFunDef extends FunDefBase { 033 private final int[] argTypes; 034 static final ResolverImpl Resolver = new ResolverImpl(); 035 036 private TupleFunDef(int[] argTypes) { 037 super( 038 "()", 039 "(<Member> [, <Member>]...)", 040 "Parenthesis operator constructs a tuple. If there is only one member, the expression is equivalent to the member expression.", 041 Syntax.Parentheses, 042 Category.Tuple, 043 argTypes); 044 this.argTypes = argTypes; 045 } 046 047 public int getReturnCategory() { 048 return Category.Tuple; 049 } 050 051 public int[] getParameterCategories() { 052 return argTypes; 053 } 054 055 public void unparse(Exp[] args, PrintWriter pw) { 056 ExpBase.unparseList(pw, args, "(", ", ", ")"); 057 } 058 059 public Type getResultType(Validator validator, Exp[] args) { 060 // _Tuple(<Member1>[,<MemberI>]...), which is written 061 // (<Member1>[,<MemberI>]...), has type [Hie1] x ... x [HieN]. 062 // 063 // If there is only one member, it merely represents a parenthesized 064 // expression, whose Hierarchy is that of the member. 065 if (args.length == 1) { 066 return TypeUtil.toMemberType(args[0].getType()); 067 } else { 068 MemberType[] types = new MemberType[args.length]; 069 for (int i = 0; i < args.length; i++) { 070 Exp arg = args[i]; 071 types[i] = TypeUtil.toMemberType(arg.getType()); 072 } 073 checkDimensions(types); 074 return new TupleType(types); 075 } 076 } 077 078 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) { 079 final Exp[] args = call.getArgs(); 080 final MemberCalc[] memberCalcs = new MemberCalc[args.length]; 081 for (int i = 0; i < args.length; i++) { 082 memberCalcs[i] = compiler.compileMember(args[i]); 083 } 084 return new CalcImpl(call, memberCalcs); 085 } 086 087 private void checkDimensions(MemberType[] memberTypes) { 088 for (int i = 0; i < memberTypes.length; i++) { 089 MemberType memberType = memberTypes[i]; 090 for (int j = 0; j < i; j++) { 091 MemberType member1 = memberTypes[j]; 092 final Dimension dimension = memberType.getDimension(); 093 final Dimension dimension1 = member1.getDimension(); 094 if (dimension != null && dimension == dimension1) { 095 throw MondrianResource.instance().DupDimensionsInTuple.ex( 096 dimension.getUniqueName()); 097 } 098 } 099 } 100 } 101 102 public static class CalcImpl extends AbstractTupleCalc { 103 private final MemberCalc[] memberCalcs; 104 105 public CalcImpl(ResolvedFunCall call, MemberCalc[] memberCalcs) { 106 super(call, memberCalcs); 107 this.memberCalcs = memberCalcs; 108 } 109 110 public Member[] evaluateTuple(Evaluator evaluator) { 111 final Member[] members = new Member[memberCalcs.length]; 112 for (int i = 0; i < members.length; i++) { 113 final Member member = members[i] 114 = memberCalcs[i].evaluateMember(evaluator); 115 if (member == null || member.isNull()) { 116 return null; 117 } 118 } 119 return members; 120 } 121 122 public MemberCalc[] getMemberCalcs() { 123 return memberCalcs; 124 } 125 } 126 127 private static class ResolverImpl extends ResolverBase { 128 public ResolverImpl() { 129 super("()", null, null, Syntax.Parentheses); 130 } 131 132 public FunDef resolve( 133 Exp[] args, Validator validator, int[] conversionCount) { 134 // Compare with TupleFunDef.getReturnCategory(). For example, 135 // ([Gender].members) is a set, 136 // ([Gender].[M]) is a member, 137 // (1 + 2) is a numeric, 138 // but 139 // ([Gender].[M], [Marital Status].[S]) is a tuple. 140 if (args.length == 1) { 141 return new ParenthesesFunDef(args[0].getCategory()); 142 } else { 143 final int[] argTypes = new int[args.length]; 144 for (int i = 0; i < args.length; i++) { 145 Exp arg = args[i]; 146 // Arg must be a member: 147 // OK: ([Gender].[S], [Time].[1997]) (member, member) 148 // OK: ([Gender], [Time]) (dimension, dimension) 149 // Not OK: ([Gender].[S], [Store].[Store City]) (member, level) 150 if (!validator.canConvert( 151 arg, Category.Member, conversionCount)) { 152 return null; 153 } 154 argTypes[i] = Category.Member; 155 } 156 return new TupleFunDef(argTypes); 157 } 158 } 159 } 160 } 161 162 // End TupleFunDef.java