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