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