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
008// All Rights Reserved.
009*/
010package mondrian.olap.fun;
011
012import mondrian.calc.*;
013import mondrian.calc.impl.AbstractListCalc;
014import mondrian.calc.impl.UnaryTupleList;
015import mondrian.mdx.ResolvedFunCall;
016import mondrian.olap.*;
017import mondrian.olap.type.*;
018
019import java.util.*;
020
021/**
022 * Definition of the <code>AddCalculatedMembers</code> MDX function.
023 *
024 * <p>AddCalculatedMembers adds calculated members that are siblings
025 * of the members in the set. The set is limited to one dimension.
026 *
027 * <p>Syntax:
028 *
029 * <blockquote><pre>AddCalculatedMembers(&lt;Set&gt;)</pre></blockquote>
030
031 * @author jhyde
032 * @since Mar 23, 2006
033 */
034class AddCalculatedMembersFunDef extends FunDefBase {
035    private static final AddCalculatedMembersFunDef instance =
036        new AddCalculatedMembersFunDef();
037
038    public static final Resolver resolver = new ResolverImpl();
039    private static final String FLAG = "fxx";
040
041    private AddCalculatedMembersFunDef() {
042        super(
043            "AddCalculatedMembers",
044            "Adds calculated members to a set.",
045            FLAG);
046    }
047
048    public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) {
049        final ListCalc listCalc = compiler.compileList(call.getArg(0));
050        return new AbstractListCalc(call, new Calc[] {listCalc}) {
051            public TupleList evaluateList(Evaluator evaluator) {
052                final TupleList list =
053                    listCalc.evaluateList(evaluator);
054                return new UnaryTupleList(
055                    addCalculatedMembers(list.slice(0), evaluator));
056            }
057        };
058    }
059
060    private List<Member> addCalculatedMembers(
061        List<Member> memberList,
062        Evaluator evaluator)
063    {
064        // Determine unique levels in the set
065        final Set<Level> levels = new LinkedHashSet<Level>();
066        Hierarchy hierarchy = null;
067
068        for (Member member : memberList) {
069            if (hierarchy == null) {
070                hierarchy = member.getHierarchy();
071            } else if (hierarchy != member.getHierarchy()) {
072                throw newEvalException(
073                    this,
074                    "Only members from the same hierarchy are allowed in the "
075                    + "AddCalculatedMembers set: " + hierarchy
076                    + " vs " + member.getHierarchy());
077            }
078            levels.add(member.getLevel());
079        }
080
081        // For each level, add the calculated members from both
082        // the schema and the query
083        List<Member> workingList = new ArrayList<Member>(memberList);
084        final SchemaReader schemaReader =
085                evaluator.getQuery().getSchemaReader(true);
086        for (Level level : levels) {
087            List<Member> calcMemberList =
088                schemaReader.getCalculatedMembers(level);
089            workingList.addAll(calcMemberList);
090        }
091        return workingList;
092    }
093
094    private static class ResolverImpl extends MultiResolver {
095        public ResolverImpl() {
096            super(
097                instance.getName(),
098                instance.getSignature(),
099                instance.getDescription(),
100                new String[] {FLAG});
101        }
102
103        protected FunDef createFunDef(Exp[] args, FunDef dummyFunDef) {
104            if (args.length == 1) {
105                Exp arg = args[0];
106                final Type type1 = arg.getType();
107                if (type1 instanceof SetType) {
108                    SetType type = (SetType) type1;
109                    if (type.getElementType() instanceof MemberType) {
110                        return instance;
111                    } else {
112                        throw newEvalException(
113                            instance,
114                            "Only single dimension members allowed in set for AddCalculatedMembers");
115                    }
116                }
117            }
118            return null;
119        }
120    }
121}
122
123// End AddCalculatedMembersFunDef.java