001    /*
002    // $Id: //open/mondrian/src/main/mondrian/olap/fun/ValidMeasureFunDef.java#10 $
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 and others
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.*;
013    import mondrian.calc.impl.GenericCalc;
014    import mondrian.mdx.ResolvedFunCall;
015    import mondrian.olap.*;
016    import mondrian.olap.type.TypeUtil;
017    import mondrian.rolap.RolapCube;
018    import mondrian.rolap.RolapMember;
019    
020    import java.util.*;
021    
022    /**
023     * Definition of the <code>ValidMeasure</code> MDX function.
024     *
025     * <p>Returns a valid measure in a virtual cube by forcing inapplicable
026     * dimensions to their top level.
027     *
028     * <p>Syntax:
029     * <blockquote><code>
030     * ValidMeasure(&lt;Tuple&gt;)
031     * </code></blockquote>
032     *
033     * @author kwalker, mpflug
034     * @version $Id: //open/mondrian/src/main/mondrian/olap/fun/ValidMeasureFunDef.java#10 $
035     */
036    public class ValidMeasureFunDef extends FunDefBase
037    {
038        static final ValidMeasureFunDef instance = new ValidMeasureFunDef();
039    
040        private ValidMeasureFunDef() {
041            super(
042                    "ValidMeasure",
043                    "Returns a valid measure in a virtual cube by forcing inapplicable dimensions to their top level.",
044                    "fnt");
045        }
046    
047        public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) {
048            final Calc calc;
049            final Exp arg = call.getArg(0);
050            if (TypeUtil.couldBeMember(arg.getType())) {
051                calc = compiler.compileMember(arg);
052            } else {
053                calc = compiler.compileTuple(arg);
054            }
055            return new CalcImpl(call, calc);
056        }
057    
058        private static class CalcImpl
059                extends GenericCalc {
060            private final Calc calc;
061    
062            public CalcImpl(ResolvedFunCall call, Calc calc) {
063                super(call);
064                this.calc = calc;
065            }
066    
067            public Object evaluate(Evaluator evaluator) {
068                final List<Member> memberList;
069                if (calc instanceof MemberCalc) {
070                    memberList = new ArrayList<Member>(1);
071                    memberList.add(((MemberCalc) calc).evaluateMember(evaluator));
072                } else {
073                    final Member[] tupleMembers =
074                        ((TupleCalc)calc).evaluateTuple(evaluator);
075                    memberList = Arrays.asList(tupleMembers);
076                }
077                RolapCube baseCube = null;
078                RolapCube virtualCube = (RolapCube) evaluator.getCube();
079                // find the measure in the tuple
080                int measurePosition = -1;
081                for (int i = 0; i < memberList.size(); i++) {
082                    if (memberList.get(i).getDimension().isMeasures()) {
083                        measurePosition = i;
084                        break;
085                    }
086                }
087                // problem: if measure is in two base cubes
088                baseCube =
089                    getBaseCubeofMeasure(
090                        evaluator, memberList.get(measurePosition), baseCube);
091                List<Dimension> vMinusBDimensions =
092                    getDimensionsToForceToAllLevel(virtualCube, baseCube, memberList);
093                // declare members array and fill in with all needed members
094                final List<Member> validMeasureMembers =
095                    new ArrayList<Member>(memberList);
096                // start adding to validMeasureMembers at right place
097                for (Dimension vMinusBDimension : vMinusBDimensions) {
098                    final Hierarchy hierarchy = vMinusBDimension.getHierarchy();
099                    if (hierarchy.hasAll()) {
100                        validMeasureMembers.add(hierarchy.getAllMember());
101                    } else {
102                        validMeasureMembers.add(hierarchy.getDefaultMember());
103                    }
104                }
105                // this needs to be done before validmeasuremembers are set on the
106                // context since calculated members defined on a non joining
107                // dimension might have been pulled to default member
108                List<Member> calculatedMembers =
109                    getCalculatedMembersFromContext(evaluator);
110    
111                evaluator.setContext(validMeasureMembers);
112    
113                for (Member member : calculatedMembers) {
114                    evaluator.setContext(member);
115                }
116    
117                return evaluator.evaluateCurrent();
118            }
119    
120            private List<Member> getCalculatedMembersFromContext(Evaluator evaluator) {
121                Member[] currentMembers = evaluator.getMembers();
122                List<Member> calculatedMembers = new ArrayList<Member>();
123                for (Member currentMember : currentMembers) {
124                    if (currentMember.isCalculated()) {
125                        calculatedMembers.add(currentMember);
126                    }
127                }
128                return calculatedMembers;
129            }
130    
131    
132            public Calc[] getCalcs() {
133                return new Calc[]{calc};
134            }
135    
136            private RolapCube getBaseCubeofMeasure(
137                    Evaluator evaluator, Member member, RolapCube baseCube) {
138                final Cube[] cubes = evaluator.getSchemaReader().getCubes();
139                for (Cube cube1 : cubes) {
140                    RolapCube cube = (RolapCube) cube1;
141                    if (!cube.isVirtual()) {
142                        for (RolapMember measure : cube.getMeasuresMembers()) {
143                            if (measure.getName().equals(member.getName())) {
144                                baseCube = cube;
145                            }
146                        }
147                    }
148                    if (baseCube != null) {
149                        break;
150                    }
151                }
152                return baseCube;
153            }
154    
155            private List<Dimension> getDimensionsToForceToAllLevel(
156                RolapCube virtualCube,
157                RolapCube baseCube,
158                List<Member> memberList)
159            {
160                List<Dimension> vMinusBDimensions = new ArrayList<Dimension>();
161                Set<Dimension> virtualCubeDims = new HashSet<Dimension>();
162                virtualCubeDims.addAll(Arrays.asList(virtualCube.getDimensions()));
163    
164                Set<Dimension> nonJoiningDims =
165                    baseCube.nonJoiningDimensions(virtualCubeDims);
166    
167                for (Dimension nonJoiningDim : nonJoiningDims) {
168                    if (!isDimInMembersList(memberList, nonJoiningDim)) {
169                        vMinusBDimensions.add(nonJoiningDim);
170                    }
171                }
172                return vMinusBDimensions;
173            }
174    
175            private boolean isDimInMembersList(
176                List<Member> members,
177                Dimension dimension)
178            {
179                for (Member member : members) {
180                    if (member.getName().equalsIgnoreCase(dimension.getName())) {
181                        return true;
182                    }
183                }
184                return false;
185            }
186    
187            public boolean dependsOn(Dimension dimension) {
188                // depends on all dimensions
189                return butDepends(getCalcs(), dimension);
190            }
191        }
192    }
193    
194    // End ValidMeasureFunDef.java