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