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) 2002-2005 Julian Hyde
008// Copyright (C) 2005-2011 Pentaho and others
009// All Rights Reserved.
010//
011// jhyde, 3 March, 2002
012*/
013package mondrian.util;
014
015import mondrian.calc.TupleList;
016import mondrian.calc.impl.ArrayTupleList;
017import mondrian.olap.*;
018import mondrian.olap.fun.FunUtil;
019import mondrian.resource.MondrianResource;
020import mondrian.rolap.RolapCube;
021
022import java.util.*;
023
024/**
025 * Utilities for parsing fully-qualified member names, tuples, member lists,
026 * and tuple lists.
027 *
028 * @author jhyde
029 */
030public class IdentifierParser extends org.olap4j.impl.IdentifierParser {
031
032    /**
033     * Implementation of Builder that resolves segment lists to members.
034     */
035    public static class BuilderImpl extends MemberBuilder {
036        private final SchemaReader schemaReader;
037        private final Cube cube;
038        protected final List<Hierarchy> hierarchyList;
039        private final boolean ignoreInvalid;
040
041        BuilderImpl(
042            SchemaReader schemaReader,
043            Cube cube,
044            List<Hierarchy> hierarchyList)
045        {
046            this.schemaReader = schemaReader;
047            this.cube = cube;
048            this.hierarchyList = hierarchyList;
049            final MondrianProperties props = MondrianProperties.instance();
050            final boolean load = ((RolapCube) cube).isLoadInProgress();
051            this.ignoreInvalid =
052                (load
053                    ? props.IgnoreInvalidMembers.get()
054                    : props.IgnoreInvalidMembersDuringQuery.get());
055        }
056
057        protected Member resolveMember(Hierarchy expectedHierarchy) {
058            final List<Id.Segment> mondrianSegmentList =
059                Util.convert(this.segmentList);
060            Member member =
061                (Member) Util.lookupCompound(
062                    schemaReader, cube, mondrianSegmentList, !ignoreInvalid,
063                    Category.Member);
064            if (member == null) {
065                assert ignoreInvalid;
066                if (expectedHierarchy != null) {
067                    return expectedHierarchy.getNullMember();
068                } else {
069                    // Guess the intended hierarchy from the largest valid
070                    // prefix.
071                    for (int i = mondrianSegmentList.size() - 1; i > 0; --i) {
072                        List<Id.Segment> partialName =
073                            mondrianSegmentList.subList(0, i);
074                        OlapElement olapElement =
075                            schemaReader.lookupCompound(
076                                cube, partialName, false, Category.Unknown);
077                        if (olapElement != null) {
078                            return olapElement.getHierarchy().getNullMember();
079                        }
080                    }
081                    throw MondrianResource.instance().MdxChildObjectNotFound.ex(
082                        Util.implode(mondrianSegmentList),
083                        cube.getQualifiedName());
084                }
085            }
086            if (expectedHierarchy != null
087                && member.getHierarchy() != expectedHierarchy)
088            {
089                // TODO: better error
090                throw Util.newInternal("member is of wrong hierarchy");
091            }
092            return member;
093        }
094    }
095
096    /**
097     * Implementation of Builder that builds a tuple.
098     */
099    public static class TupleBuilder extends BuilderImpl {
100        protected final List<Member> memberList = new ArrayList<Member>();
101
102        public TupleBuilder(
103            SchemaReader schemaReader,
104            Cube cube,
105            List<Hierarchy> hierarchyList)
106        {
107            super(schemaReader, cube, hierarchyList);
108        }
109
110        public void memberComplete() {
111            super.memberComplete();
112            if (memberList.size() >= hierarchyList.size()) {
113                throw Util.newInternal("expected ')");
114            }
115            final Hierarchy hierarchy = hierarchyList.get(memberList.size());
116            final Member member = resolveMember(hierarchy);
117            memberList.add(member);
118            segmentList.clear();
119        }
120
121        public void tupleComplete() {
122            if (memberList.size() < hierarchyList.size()) {
123                throw Util.newInternal("too few members");
124            }
125        }
126    }
127
128    /**
129     * Implementation of Builder that builds a tuple list.
130     */
131    public static class TupleListBuilder extends TupleBuilder {
132        public final TupleList tupleList;
133
134        public TupleListBuilder(
135            SchemaReader schemaReader, Cube cube, List<Hierarchy> hierarchyList)
136        {
137            super(schemaReader, cube, hierarchyList);
138            tupleList = new ArrayTupleList(hierarchyList.size());
139        }
140
141        public void tupleComplete() {
142            super.tupleComplete();
143            if (!FunUtil.tupleContainsNullMember(memberList)) {
144                tupleList.add(memberList);
145            }
146            this.memberList.clear();
147        }
148    }
149
150    /**
151     * Implementation of Builder that builds a member list.
152     */
153    public static class MemberListBuilder extends BuilderImpl {
154        public final List<Member> memberList = new ArrayList<Member>();
155
156        public MemberListBuilder(
157            SchemaReader schemaReader, Cube cube, Hierarchy hierarchy)
158        {
159            super(schemaReader, cube, Collections.singletonList(hierarchy));
160        }
161
162        public void memberComplete() {
163            final Member member = resolveMember(hierarchyList.get(0));
164            if (!member.isNull()) {
165                memberList.add(member);
166            }
167            segmentList.clear();
168        }
169
170        @Override
171        public void tupleComplete() {
172            // nothing to do
173        }
174    }
175}
176
177// End IdentifierParser.java