001    /*
002    // $Id: //open/mondrian/src/main/mondrian/olap/fun/StrToTupleFunDef.java#7 $
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-2007 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.calc.Calc;
013    import mondrian.calc.ExpCompiler;
014    import mondrian.calc.StringCalc;
015    import mondrian.calc.impl.AbstractMemberCalc;
016    import mondrian.calc.impl.AbstractTupleCalc;
017    import mondrian.mdx.ResolvedFunCall;
018    import mondrian.mdx.DimensionExpr;
019    import mondrian.mdx.HierarchyExpr;
020    import mondrian.olap.*;
021    import mondrian.olap.type.Type;
022    import mondrian.olap.type.TupleType;
023    import mondrian.olap.type.MemberType;
024    import mondrian.olap.type.StringType;
025    import mondrian.resource.MondrianResource;
026    
027    import java.util.ArrayList;
028    import java.util.List;
029    
030    /**
031     * Definition of the <code>StrToTuple</code> MDX function.
032     *
033     * @author jhyde
034     * @version $Id: //open/mondrian/src/main/mondrian/olap/fun/StrToTupleFunDef.java#7 $
035     * @since Mar 23, 2006
036     */
037    class StrToTupleFunDef extends FunDefBase {
038        static final ResolverImpl Resolver = new ResolverImpl();
039    
040        private StrToTupleFunDef(int[] parameterTypes) {
041            super("StrToTuple",
042                null,
043                "Constructs a tuple from a string.",
044                Syntax.Function, Category.Tuple, parameterTypes);
045        }
046    
047        public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) {
048            final StringCalc stringCalc = compiler.compileString(call.getArg(0));
049            Type elementType = call.getType();
050            if (elementType instanceof MemberType) {
051                final Hierarchy hierarchy = elementType.getHierarchy();
052                return new AbstractMemberCalc(call, new Calc[] {stringCalc}) {
053                    public Member evaluateMember(Evaluator evaluator) {
054                        String string = stringCalc.evaluateString(evaluator);
055                        return parseMember(evaluator, string, hierarchy);
056                    }
057                };
058            } else {
059                TupleType tupleType = (TupleType) elementType;
060                final Hierarchy[] hierarchies =
061                    new Hierarchy[tupleType.elementTypes.length];
062                for (int i = 0; i < tupleType.elementTypes.length; i++) {
063                    hierarchies[i] = tupleType.elementTypes[i].getHierarchy();
064                }
065                return new AbstractTupleCalc(call, new Calc[] {stringCalc}) {
066                    public Member[] evaluateTuple(Evaluator evaluator) {
067                        String string = stringCalc.evaluateString(evaluator);
068                        return parseTuple(evaluator, string, hierarchies);
069                    }
070                };
071            }
072        }
073    
074        /**
075         * Parses a tuple, such as "([Gender].[M], [Marital Status].[S])".
076         *
077         * @param evaluator Evaluator, provides a {@link mondrian.olap.SchemaReader}
078         *   and {@link Cube}
079         * @param string String to parse
080         * @param hierarchies Hierarchies of the members
081         * @return Tuple represented as array of members
082         */
083        private Member[] parseTuple(
084            Evaluator evaluator, String string, Hierarchy[] hierarchies)
085        {
086            final Member[] members = new Member[hierarchies.length];
087            int i = StrToSetFunDef.parseTuple(
088                evaluator, string, 0, members, hierarchies);
089            // todo: check for garbage at end of string
090            return members;
091        }
092    
093        private Member parseMember(
094            Evaluator evaluator, String string, Hierarchy hierarchy) {
095            Member[] members = {null};
096            int i = StrToSetFunDef.parseMember(
097                evaluator, string, 0, members, new Hierarchy[] {hierarchy}, 0);
098            // todo: check for garbage at end of string
099            return members[0];
100        }
101    
102        public Exp createCall(Validator validator, Exp[] args) {
103            final int argCount = args.length;
104            if (argCount <= 1) {
105                throw MondrianResource.instance().MdxFuncArgumentsNum.ex(getName());
106            }
107            for (int i = 1; i < argCount; i++) {
108                final Exp arg = args[i];
109                if (arg instanceof DimensionExpr) {
110                    // if arg is a dimension, switch to dimension's default
111                    // hierarchy
112                    DimensionExpr dimensionExpr = (DimensionExpr) arg;
113                    Dimension dimension = dimensionExpr.getDimension();
114                    args[i] = new HierarchyExpr(dimension.getHierarchy());
115                } else if (arg instanceof HierarchyExpr) {
116                    // nothing
117                } else {
118                    throw MondrianResource.instance().MdxFuncNotHier.ex(
119                        i + 1, getName());
120                }
121            }
122            return super.createCall(validator, args);
123        }
124    
125        public Type getResultType(Validator validator, Exp[] args) {
126            switch (args.length) {
127            case 1:
128                // This is a call to the standard version of StrToTuple,
129                // which doesn't give us any hints about type.
130                return new TupleType(null);
131    
132            case 2:
133                final Type argType = args[1].getType();
134                return new MemberType(
135                    argType.getDimension(),
136                    argType.getHierarchy(),
137                    argType.getLevel(),
138                    null);
139    
140            default: {
141                // This is a call to Mondrian's extended version of
142                // StrToTuple, of the form
143                //   StrToTuple(s, <Hier1>, ... , <HierN>)
144                //
145                // The result is a tuple
146                //  (<Hier1>, ... ,  <HierN>)
147                final List<Type> list = new ArrayList<Type>();
148                for (int i = 1; i < args.length; i++) {
149                    Exp arg = args[i];
150                    final Type type = arg.getType();
151                    list.add(type);
152                }
153                final Type[] types = list.toArray(new Type[list.size()]);
154                return new TupleType(types);
155            }
156            }
157        }
158    
159        private static class ResolverImpl extends ResolverBase {
160            ResolverImpl() {
161                super("StrToTuple",
162                        "StrToTuple(<String Expression>)",
163                        "Constructs a tuple from a string.",
164                        Syntax.Function);
165            }
166    
167            public FunDef resolve(
168                    Exp[] args, Validator validator, int[] conversionCount) {
169                if (args.length < 1) {
170                    return null;
171                }
172                Type type = args[0].getType();
173                if (!(type instanceof StringType)) {
174                    return null;
175                }
176                for (int i = 1; i < args.length; i++) {
177                    Exp exp = args[i];
178                    if (!(exp instanceof DimensionExpr
179                        || exp instanceof HierarchyExpr)) {
180                        return null;
181                    }
182                }
183                int[] argTypes = new int[args.length];
184                argTypes[0] = Category.String;
185                for (int i = 1; i < argTypes.length; i++) {
186                    argTypes[i] = Category.Hierarchy;
187                }
188                return new StrToTupleFunDef(argTypes);
189            }
190    
191            public FunDef getFunDef() {
192                return new StrToTupleFunDef(new int[] {Category.String});
193            }
194        }
195    }
196    
197    // End StrToTupleFunDef.java