From e479cf97008710894e173f9bead7b8930b83dcde Mon Sep 17 00:00:00 2001 From: Julian Hyde Date: Thu, 28 Oct 2010 18:14:32 +0000 Subject: [PATCH] Fix bug 3095309, "Lookup methods should use Segment, not name, before 1.0". IdentifierNode.Segment is used a lot, so it should be a top-level interace. So: * IdentifierNode.Segment becomes IdentifierSegment * IdentifierNode.KeySegment becomes KeySegment * IdentifierNode.NameSegment becomes NameSegment * IdentifierNode.Quoting becomes Quoting (all top-level in org.olap4j.mdx package). Change signature of some methods from 'String...' to 'List'. This is safer, because not all member names are lists of quoted name segments (some are key segments, as in [Product].[Product Name].&[1000]). Methods affected: * Cube.lookupMember * Cube.lookupMembers * QueryDimension.include * QueryDimension.exclude * QueryDimension.createSelection * QueryAxis.sort Remove deprecated and obsolete methods: * QueryDimension.select * QueryDimensions.getSelections * QueryDimension.getNameParts Remove obsolete interface XmlaOlap4jDriver.Proxy (replaced by XmlaOlap4jProxy). Remove obsolete class constants Axis.NONE and Axis.UNUSED. (Their values were null anyway.) git-svn-id: https://olap4j.svn.sourceforge.net/svnroot/olap4j/trunk@360 c6a108a4-781c-0410-a6c6-c2d559e19af0 --- src/org/olap4j/Axis.java | 12 - .../olap4j/driver/xmla/XmlaOlap4jCube.java | 31 +- .../olap4j/driver/xmla/XmlaOlap4jDriver.java | 11 - src/org/olap4j/impl/IdentifierParser.java | 39 +- .../olap4j/mdx/DefaultMdxValidatorImpl.java | 6 +- src/org/olap4j/mdx/IdentifierNode.java | 339 ++++-------------- src/org/olap4j/mdx/IdentifierSegment.java | 92 +++++ src/org/olap4j/mdx/KeySegment.java | 89 +++++ src/org/olap4j/mdx/NameSegment.java | 99 +++++ src/org/olap4j/mdx/Quoting.java | 45 +++ .../mdx/parser/impl/DefaultMdxParser.cup | 21 +- src/org/olap4j/metadata/Cube.java | 33 +- src/org/olap4j/query/Query.java | 9 + src/org/olap4j/query/QueryAxis.java | 14 +- src/org/olap4j/query/QueryDimension.java | 128 +++---- src/org/olap4j/sample/SimpleQuerySample.java | 6 +- testsrc/org/olap4j/ConnectionTest.java | 82 +++-- testsrc/org/olap4j/OlapTest.java | 184 +++++----- testsrc/org/olap4j/impl/Olap4jUtilTest.java | 69 ++-- testsrc/org/olap4j/mdx/MdxTest.java | 87 +++-- testsrc/org/olap4j/test/ParserTest.java | 50 +-- testsrc/org/olap4j/test/TestContext.java | 13 +- 22 files changed, 775 insertions(+), 684 deletions(-) create mode 100644 src/org/olap4j/mdx/IdentifierSegment.java create mode 100644 src/org/olap4j/mdx/KeySegment.java create mode 100644 src/org/olap4j/mdx/NameSegment.java create mode 100644 src/org/olap4j/mdx/Quoting.java diff --git a/src/org/olap4j/Axis.java b/src/org/olap4j/Axis.java index 3fbc689..9561085 100644 --- a/src/org/olap4j/Axis.java +++ b/src/org/olap4j/Axis.java @@ -26,18 +26,6 @@ */ public interface Axis { - /** - * @deprecated Will be removed before olap4j 1.0. - */ - // REVIEW: Is it wise to remove this axis enum value? - // It's existence IS relevant. - Standard UNUSED = null; - - /** - * @deprecated Will be removed before olap4j 1.0. - */ - Standard NONE = null; - /** * Abbreviation for {@link org.olap4j.Axis.Standard#FILTER}. */ diff --git a/src/org/olap4j/driver/xmla/XmlaOlap4jCube.java b/src/org/olap4j/driver/xmla/XmlaOlap4jCube.java index c864504..b0956e9 100644 --- a/src/org/olap4j/driver/xmla/XmlaOlap4jCube.java +++ b/src/org/olap4j/driver/xmla/XmlaOlap4jCube.java @@ -10,7 +10,7 @@ import org.olap4j.OlapException; import org.olap4j.impl.*; -import org.olap4j.mdx.IdentifierNode; +import org.olap4j.mdx.*; import org.olap4j.metadata.*; import java.util.*; @@ -154,27 +154,12 @@ public Collection getSupportedLocales() { return Collections.singletonList(Locale.getDefault()); } - public Member lookupMember(String... nameParts) throws OlapException { - List segmentList = - new ArrayList(); - for (String namePart : nameParts) { - segmentList.add(new IdentifierNode.NameSegment(namePart)); - } - return lookupMember(segmentList); - } - - /** - * Finds a member, given its fully qualfieid name. - * - * @param segmentList List of the segments of the name - * @return Member, or null if not found - * @throws OlapException on error - */ - private Member lookupMember( - List segmentList) throws OlapException + public Member lookupMember( + List segmentList) + throws OlapException { StringBuilder buf = new StringBuilder(); - for (IdentifierNode.Segment segment : segmentList) { + for (IdentifierSegment segment : segmentList) { if (buf.length() > 0) { buf.append('.'); } @@ -197,14 +182,14 @@ MetadataReader getMetadataReader() { public List lookupMembers( Set treeOps, - String... nameParts) throws OlapException + List nameParts) throws OlapException { StringBuilder buf = new StringBuilder(); - for (String namePart : nameParts) { + for (IdentifierSegment namePart : nameParts) { if (buf.length() > 0) { buf.append('.'); } - buf.append(new IdentifierNode.NameSegment(namePart)); + buf.append(namePart); } final String uniqueName = buf.toString(); final List list = diff --git a/src/org/olap4j/driver/xmla/XmlaOlap4jDriver.java b/src/org/olap4j/driver/xmla/XmlaOlap4jDriver.java index 094f07a..82e1dd0 100644 --- a/src/org/olap4j/driver/xmla/XmlaOlap4jDriver.java +++ b/src/org/olap4j/driver/xmla/XmlaOlap4jDriver.java @@ -313,17 +313,6 @@ public enum Property { Olap4jUtil.discard(description); } } - - /** - * This is a mock subclass to prevent retro-compatibility issues. - * If you're using this class, please change your code to - * use XmlaOlap4jProxy instead. - * @author Luc Boudreau - * - */ - @Deprecated - public static interface Proxy extends XmlaOlap4jProxy { - } } // End XmlaOlap4jDriver.java diff --git a/src/org/olap4j/impl/IdentifierParser.java b/src/org/olap4j/impl/IdentifierParser.java index 94aa395..25aa5ba 100644 --- a/src/org/olap4j/impl/IdentifierParser.java +++ b/src/org/olap4j/impl/IdentifierParser.java @@ -8,8 +8,7 @@ */ package org.olap4j.impl; -import org.olap4j.mdx.IdentifierNode; -import org.olap4j.mdx.ParseRegion; +import org.olap4j.mdx.*; import java.util.*; @@ -211,7 +210,7 @@ public static int parseMember( builder.segmentComplete( null, string.substring(start, i).trim(), - IdentifierNode.Quoting.UNQUOTED, + Quoting.UNQUOTED, syntax); state = AFTER_SEG; break loop; @@ -219,7 +218,7 @@ public static int parseMember( builder.segmentComplete( null, string.substring(start, i).trim(), - IdentifierNode.Quoting.UNQUOTED, + Quoting.UNQUOTED, syntax); syntax = Builder.Syntax.NAME; state = BEFORE_SEG; @@ -229,7 +228,7 @@ public static int parseMember( builder.segmentComplete( null, string.substring(start, i).trim(), - IdentifierNode.Quoting.UNQUOTED, + Quoting.UNQUOTED, syntax); syntax = Builder.Syntax.NEXT_KEY; state = BEFORE_SEG; @@ -254,7 +253,7 @@ public static int parseMember( null, Olap4jUtil.replace( string.substring(start, i), "]]", "]"), - IdentifierNode.Quoting.QUOTED, + Quoting.QUOTED, syntax); ++i; state = AFTER_SEG; @@ -332,7 +331,7 @@ private static IllegalArgumentException fail( * @param s MDX identifier * @return List of segments */ - public static List parseIdentifier(String s) { + public static List parseIdentifier(String s) { final MemberBuilder builder = new MemberBuilder(); int i = parseMember(builder, s, 0); if (i < s.length()) { @@ -357,7 +356,7 @@ public static List parseIdentifier(String s) { * @param s MDX identifier list * @return List of lists of segments */ - public static List> parseIdentifierList( + public static List> parseIdentifierList( String s) { final MemberListBuilder builder = new MemberListBuilder(); @@ -400,7 +399,7 @@ public interface Builder { void segmentComplete( ParseRegion region, String name, - IdentifierNode.Quoting quoting, + Quoting quoting, Syntax syntax); enum Syntax { @@ -416,12 +415,12 @@ enum Syntax { * It cannot handle tuples or lists of members. */ public static class MemberBuilder implements Builder { - protected final List subSegments; - protected final List segmentList; + protected final List subSegments; + protected final List segmentList; public MemberBuilder() { - segmentList = new ArrayList(); - subSegments = new ArrayList(); + segmentList = new ArrayList(); + subSegments = new ArrayList(); } public void tupleComplete() { @@ -434,7 +433,7 @@ public void memberComplete() { private void flushSubSegments() { if (!subSegments.isEmpty()) { - segmentList.add(new IdentifierNode.KeySegment(subSegments)); + segmentList.add(new KeySegment(subSegments)); subSegments.clear(); } } @@ -442,11 +441,11 @@ private void flushSubSegments() { public void segmentComplete( ParseRegion region, String name, - IdentifierNode.Quoting quoting, + Quoting quoting, Syntax syntax) { - final IdentifierNode.NameSegment segment = - new IdentifierNode.NameSegment( + final NameSegment segment = + new NameSegment( region, name, quoting); if (syntax != Syntax.NEXT_KEY) { // If we were building a previous key, write it out. @@ -467,13 +466,13 @@ public void segmentComplete( * then collects members into lists. */ public static class MemberListBuilder extends MemberBuilder { - final List> list = - new ArrayList>(); + final List> list = + new ArrayList>(); public void memberComplete() { super.memberComplete(); list.add( - new ArrayList(segmentList)); + new ArrayList(segmentList)); segmentList.clear(); } } diff --git a/src/org/olap4j/mdx/DefaultMdxValidatorImpl.java b/src/org/olap4j/mdx/DefaultMdxValidatorImpl.java index 23a607d..dcc0329 100644 --- a/src/org/olap4j/mdx/DefaultMdxValidatorImpl.java +++ b/src/org/olap4j/mdx/DefaultMdxValidatorImpl.java @@ -181,8 +181,8 @@ public ParseTreeNode acceptScalar(ParseTreeNode node) { // from IdentifierNode public ParseTreeNode accept(IdentifierNode identifier) { if (identifier.getSegmentList().size() == 1) { - final IdentifierNode.Segment s = identifier.getSegmentList().get(0); - if (s.getQuoting() == IdentifierNode.Quoting.UNQUOTED + final IdentifierSegment s = identifier.getSegmentList().get(0); + if (s.getQuoting() == Quoting.UNQUOTED && isReserved(s.getName())) { return LiteralNode.createSymbol( @@ -209,7 +209,7 @@ public boolean isReserved(String name) { private ParseTreeNode lookup( SelectNode select, - List segments, + List segments, boolean allowProp) { // todo: something like diff --git a/src/org/olap4j/mdx/IdentifierNode.java b/src/org/olap4j/mdx/IdentifierNode.java index 05844a4..a39f6b2 100644 --- a/src/org/olap4j/mdx/IdentifierNode.java +++ b/src/org/olap4j/mdx/IdentifierNode.java @@ -21,8 +21,8 @@ * *

An identifier is immutable. * - *

An identifer consists of one or more {@link Segment}s. A segment is - * either:

    + *

    An identifer consists of one or more {@link IdentifierSegment}s. A segment + * is either:

      *
    • An unquoted value such as '{@code CA}', *
    • A value quoted in brackets, such as '{@code [San Francisco]}', or *
    • A key of one or more parts, each of which is prefixed with '&', @@ -33,16 +33,16 @@ * *

      A key segment is of type {@link Quoting#KEY}, and has one or more * component parts accessed via the - * {@link Segment#getKeyParts()} method. The parts + * {@link IdentifierSegment#getKeyParts()} method. The parts * are of type {@link Quoting#UNQUOTED} or {@link Quoting#QUOTED}. * *

      A simple example is the identifier {@code Measures.[Unit Sales]}. It * has two segments:

        *
      • Segment #0 is - * {@link org.olap4j.mdx.IdentifierNode.Quoting#UNQUOTED UNQUOTED}, + * {@link Quoting#UNQUOTED UNQUOTED}, * name "Measures"
      • *
      • Segment #1 is - * {@link org.olap4j.mdx.IdentifierNode.Quoting#QUOTED QUOTED}, + * {@link Quoting#QUOTED QUOTED}, * name "Unit Sales"
      • *
      * @@ -52,7 +52,7 @@ *
        *
      • Segment #0 is QUOTED, name "Customers"
      • *
      • Segment #1 is QUOTED, name "City"
      • - *
      • Segment #2 is a {@link org.olap4j.mdx.IdentifierNode.Quoting#KEY KEY}. + *
      • Segment #2 is a {@link Quoting#KEY KEY}. * It has 3 sub-segments: *
          *
        • Sub-segment #0 is QUOTED, name "San Francisco"
        • @@ -73,7 +73,7 @@ public class IdentifierNode implements ParseTreeNode { - private final List segments; + private final List segments; /** * Creates an identifier containing one or more segments. @@ -81,7 +81,7 @@ public class IdentifierNode * @param segments Array of Segments, each consisting of a name and quoting * style */ - public IdentifierNode(IdentifierNode.Segment... segments) { + public IdentifierNode(IdentifierSegment... segments) { if (segments.length < 1) { throw new IllegalArgumentException(); } @@ -93,14 +93,14 @@ public IdentifierNode(IdentifierNode.Segment... segments) { * * @param segments List of segments */ - public IdentifierNode(List segments) { + public IdentifierNode(List segments) { if (segments.size() < 1) { throw new IllegalArgumentException(); } this.segments = - new UnmodifiableArrayList( + new UnmodifiableArrayList( segments.toArray( - new Segment[segments.size()])); + new IdentifierSegment[segments.size()])); } public Type getType() { @@ -113,7 +113,7 @@ public Type getType() { * * @return list of constituent segments */ - public List getSegmentList() { + public List getSegmentList() { return segments; } @@ -129,8 +129,8 @@ public ParseRegion getRegion() { * @param segments List of segments * @return Region encompassed by list of segments */ - private static ParseRegion sumSegmentRegions( - final List segments) + static ParseRegion sumSegmentRegions( + final List segments) { return ParseRegion.sum( new AbstractList() { @@ -151,9 +151,9 @@ public int size() { * @param segment Name of segment * @return New identifier */ - public IdentifierNode append(IdentifierNode.Segment segment) { - List newSegments = - new ArrayList(segments); + public IdentifierNode append(IdentifierSegment segment) { + List newSegments = + new ArrayList(segments); newSegments.add(segment); return new IdentifierNode(newSegments); } @@ -179,7 +179,7 @@ public IdentifierNode deepCopy() { * Parses an MDX identifier string into an * {@link org.olap4j.mdx.IdentifierNode}. * - *

          It contains a list of {@link IdentifierNode.Segment segments}, each + *

          It contains a list of {@link IdentifierSegment segments}, each * of which is a name combined with a description of how the name * was {@link Quoting quoted}. For example, * @@ -191,14 +191,14 @@ public IdentifierNode deepCopy() { * returns an IdentifierNode consisting of the following segments: * *

            - *
          • Segment("Customers", QUOTED), - *
          • Segment("USA", UNQUOTED), - *
          • Segment("South Dakota", QUOTED), - *
          • Segment("Sioux Falls", QUOTED), - *
          • Segment("1245", KEY) + *
          • NameSegment("Customers", quoted=true), + *
          • NameSegment("USA", quoted=false), + *
          • NameSegment("South Dakota", quoted=true), + *
          • NameSegment("Sioux Falls", quoted=true), + *
          • KeySegment( { NameSegment("1245", quoted=true) } ) *
          * - * @see org.olap4j.metadata.Cube#lookupMember(String...) + * @see #ofNames(String...) * * @param identifier MDX identifier string * @@ -211,6 +211,39 @@ public static IdentifierNode parseIdentifier(String identifier) { return new IdentifierNode(IdentifierParser.parseIdentifier(identifier)); } + /** + * Converts an array of quoted name segments into an identifier. + * + *

          For example, + * + *

          + * IdentifierNode.ofNames("Store", "USA", "CA")
          + * + * returns an IdentifierNode consisting of the following segments: + * + *
            + *
          • NameSegment("Customers", quoted=true), + *
          • NameSegment("USA", quoted=false), + *
          • NameSegment("South Dakota", quoted=true), + *
          • NameSegment("Sioux Falls", quoted=true), + *
          • KeySegment( { NameSegment("1245", quoted=true) } ) + *
          + * + * @see #parseIdentifier(String) + * + * @param names Array of names + * + * @return Identifier parse tree node + */ + public static IdentifierNode ofNames(String... names) { + final List list = + new ArrayList(); + for (String name : names) { + list.add(new NameSegment(null, name, Quoting.QUOTED)); + } + return new IdentifierNode(list); + } + /** * Returns string quoted in [...]. * @@ -251,10 +284,12 @@ static void quoteMdxIdentifier(String id, StringBuilder buf) { * @param segments List of segments * @return Segments as quoted string */ - static String unparseIdentifierList(List segments) { + static String unparseIdentifierList( + List segments) + { final StringBuilder buf = new StringBuilder(64); for (int i = 0; i < segments.size(); i++) { - Segment segment = segments.get(i); + IdentifierSegment segment = segments.get(i); if (i > 0) { buf.append('.'); } @@ -262,258 +297,6 @@ static String unparseIdentifierList(List segments) { } return buf.toString(); } - - /** - * Component in a compound identifier. It is described by its name and how - * the name is quoted. - * - *

          For example, the identifier - * [Store].USA.[New Mexico].&[45] has four segments:

            - *
          • "Store", {@link IdentifierNode.Quoting#QUOTED}
          • - *
          • "USA", {@link IdentifierNode.Quoting#UNQUOTED}
          • - *
          • "New Mexico", {@link IdentifierNode.Quoting#QUOTED}
          • - *
          • "45", {@link IdentifierNode.Quoting#KEY}
          • - *
          - * - *

          QUOTED and UNQUOTED segments are represented using a - * {@link org.olap4j.mdx.IdentifierNode.NameSegment NameSegment}; - * KEY segments are represented using a - * {@link org.olap4j.mdx.IdentifierNode.KeySegment KeySegment}. - * - *

          To parse an identifier into a list of segments, use the method - * {@link IdentifierNode#parseIdentifier(String)} and then call - * {@link IdentifierNode#getSegmentList()} on the resulting node.

          - */ - public interface Segment { - - /** - * Returns a string representation of this Segment. - * - *

          For example, "[Foo]", "&[123]", "Abc". - * - * @return String representation of this Segment - */ - String toString(); - - /** - * Appends a string representation of this Segment to a StringBuffer. - * - * @param buf StringBuffer - */ - void toString(StringBuilder buf); - - /** - * Returns the region of the source code which this Segment was created - * from, if it was created by parsing. - * - * @return region of source code - */ - ParseRegion getRegion(); - - /** - * Returns how this Segment is quoted. - * - * @return how this Segment is quoted - */ - Quoting getQuoting(); - - /** - * Returns the name of this Segment. - * Returns {@code null} if this Segment represents a key. - * - * @return name of this Segment - */ - String getName(); - - /** - * Returns the key components, if this Segment is a key. (That is, - * if {@link #getQuoting()} returns - * {@link org.olap4j.mdx.IdentifierNode.Quoting#KEY}.) - * - * Returns null otherwise. - * - * @return Components of key, or null if this Segment is not a key - */ - List getKeyParts(); - } - - /** - * Component in a compound identifier that describes the name of an object. - * Optionally, the name is quoted in brackets. - * - * @see org.olap4j.mdx.IdentifierNode.KeySegment - */ - public static class NameSegment implements Segment { - final String name; - final IdentifierNode.Quoting quoting; - private final ParseRegion region; - - /** - * Creates a segment with the given quoting and region. - * - * @param region Region of source code - * @param name Name - * @param quoting Quoting style - */ - public NameSegment( - ParseRegion region, - String name, - IdentifierNode.Quoting quoting) - { - this.region = region; - this.name = name; - this.quoting = quoting; - if (!(quoting == Quoting.QUOTED || quoting == Quoting.UNQUOTED)) { - throw new IllegalArgumentException(); - } - } - - /** - * Creates a quoted segment, "[name]". - * - * @param name Name of segment - */ - public NameSegment(String name) { - this(null, name, Quoting.QUOTED); - } - - public String toString() { - switch (quoting) { - case UNQUOTED: - return name; - case QUOTED: - return quoteMdxIdentifier(name); - default: - throw Olap4jUtil.unexpected(quoting); - } - } - - public void toString(StringBuilder buf) { - switch (quoting) { - case UNQUOTED: - buf.append(name); - return; - case QUOTED: - quoteMdxIdentifier(name, buf); - return; - default: - throw Olap4jUtil.unexpected(quoting); - } - } - public ParseRegion getRegion() { - return region; - } - - public String getName() { - return name; - } - - public Quoting getQuoting() { - return quoting; - } - - public List getKeyParts() { - return null; - } - } - - /** - * Segment that represents a key or compound key. - * - *

          Such a segment appears in an identifier with each component prefixed - * with '&'. For example, in the identifier - * '{@code [Customer].[State].&[WA]&[USA]}', the third segment is a - * compound key whose parts are "@{code WA}" and "{@code USA}". - * - * @see org.olap4j.mdx.IdentifierNode.NameSegment - */ - public static class KeySegment implements Segment { - private final List subSegmentList; - - /** - * Creates a KeySegment with one or more sub-segments. - * - * @param subSegments Array of sub-segments - */ - public KeySegment(NameSegment... subSegments) { - if (subSegments.length < 1) { - throw new IllegalArgumentException(); - } - this.subSegmentList = UnmodifiableArrayList.asCopyOf(subSegments); - } - - /** - * Creates a KeySegment a list of sub-segments. - * - * @param subSegmentList List of sub-segments - */ - public KeySegment(List subSegmentList) { - if (subSegmentList.size() < 1) { - throw new IllegalArgumentException(); - } - this.subSegmentList = - new UnmodifiableArrayList( - subSegmentList.toArray( - new NameSegment[subSegmentList.size()])); - } - - public String toString() { - final StringBuilder buf = new StringBuilder(); - toString(buf); - return buf.toString(); - } - - public void toString(StringBuilder buf) { - for (Segment segment : subSegmentList) { - buf.append('&'); - segment.toString(buf); - } - } - - public ParseRegion getRegion() { - return sumSegmentRegions(subSegmentList); - } - - public Quoting getQuoting() { - return Quoting.KEY; - } - - public String getName() { - return null; - } - - public List getKeyParts() { - return subSegmentList; - } - } - - /** - * Enumeration of styles by which the component of an identifier can be - * quoted. - */ - public enum Quoting { - - /** - * Unquoted identifier, for example "Measures". - */ - UNQUOTED, - - /** - * Quoted identifier, for example "[Measures]". - */ - QUOTED, - - /** - * Identifier quoted with an ampersand and brackets to indicate a key - * value, for example the second segment in "[Employees].&[89]". - * - *

          Such a segment has one or more sub-segments. Each segment is - * either quoted or unquoted. For example, the second segment in - * "[Employees].&[89]&[San Francisco]&CA&USA" has four sub-segments, - * two quoted and two unquoted. - */ - KEY, - } } // End IdentifierNode.java diff --git a/src/org/olap4j/mdx/IdentifierSegment.java b/src/org/olap4j/mdx/IdentifierSegment.java new file mode 100644 index 0000000..1ec6a23 --- /dev/null +++ b/src/org/olap4j/mdx/IdentifierSegment.java @@ -0,0 +1,92 @@ +/* +// $Id: IdentifierNode.java 359 2010-10-14 21:24:51Z jhyde $ +// This software is subject to the terms of the Eclipse Public License v1.0 +// Agreement, available at the following URL: +// http://www.eclipse.org/legal/epl-v10.html. +// Copyright (C) 2007-2010 Julian Hyde +// All Rights Reserved. +// You must accept the terms of that agreement to use this software. +*/ +package org.olap4j.mdx; + +import java.util.List; + +/** + * Component in a compound identifier. It is described by its name and how + * the name is quoted. + * + *

          For example, the identifier + * [Store].USA.[New Mexico].&[45] has four segments:

            + *
          • "Store", {@link Quoting#QUOTED}
          • + *
          • "USA", {@link Quoting#UNQUOTED}
          • + *
          • "New Mexico", {@link Quoting#QUOTED}
          • + *
          • "45", {@link Quoting#KEY}
          • + *
          + * + *

          QUOTED and UNQUOTED segments are represented using a + * {@link NameSegment NameSegment}; + * KEY segments are represented using a + * {@link KeySegment KeySegment}. + * + *

          To parse an identifier into a list of segments, use the method + * {@link org.olap4j.mdx.IdentifierNode#parseIdentifier(String)} and then call + * {@link org.olap4j.mdx.IdentifierNode#getSegmentList()} on the resulting + * node.

          + * + * @version $Id: IdentifierNode.java 359 2010-10-14 21:24:51Z jhyde $ + * @author jhyde + */ +public interface IdentifierSegment { + + /** + * Returns a string representation of this Segment. + * + *

          For example, "[Foo]", "&[123]", "Abc". + * + * @return String representation of this Segment + */ + String toString(); + + /** + * Appends a string representation of this Segment to a StringBuffer. + * + * @param buf StringBuffer + */ + void toString(StringBuilder buf); + + /** + * Returns the region of the source code which this Segment was created + * from, if it was created by parsing. + * + * @return region of source code + */ + ParseRegion getRegion(); + + /** + * Returns how this Segment is quoted. + * + * @return how this Segment is quoted + */ + Quoting getQuoting(); + + /** + * Returns the name of this IdentifierSegment. + * Returns {@code null} if this IdentifierSegment represents a key. + * + * @return name of this Segment + */ + String getName(); + + /** + * Returns the key components, if this IdentifierSegment is a key. (That is, + * if {@link #getQuoting()} returns + * {@link Quoting#KEY}.) + * + * Returns null otherwise. + * + * @return Components of key, or null if this IdentifierSegment is not a key + */ + List getKeyParts(); +} + +// End IdentifierSegment.java diff --git a/src/org/olap4j/mdx/KeySegment.java b/src/org/olap4j/mdx/KeySegment.java new file mode 100644 index 0000000..36f4587 --- /dev/null +++ b/src/org/olap4j/mdx/KeySegment.java @@ -0,0 +1,89 @@ +/* +// $Id: IdentifierNode.java 359 2010-10-14 21:24:51Z jhyde $ +// This software is subject to the terms of the Eclipse Public License v1.0 +// Agreement, available at the following URL: +// http://www.eclipse.org/legal/epl-v10.html. +// Copyright (C) 2007-2010 Julian Hyde +// All Rights Reserved. +// You must accept the terms of that agreement to use this software. +*/ +package org.olap4j.mdx; + +import org.olap4j.impl.UnmodifiableArrayList; + +import java.util.List; + +/** + * Segment that represents a key or compound key. + * + *

          Such a segment appears in an identifier with each component prefixed + * with '&'. For example, in the identifier + * '{@code [Customer].[State].&[WA]&[USA]}', the third segment is a + * compound key whose parts are "@{code WA}" and "{@code USA}". + * + * @see org.olap4j.mdx.NameSegment + * + * @version $Id: IdentifierNode.java 359 2010-10-14 21:24:51Z jhyde $ + * @author jhyde + */ +public class KeySegment implements IdentifierSegment { + private final List subSegmentList; + + /** + * Creates a KeySegment with one or more sub-segments. + * + * @param subSegments Array of sub-segments + */ + public KeySegment(NameSegment... subSegments) { + if (subSegments.length < 1) { + throw new IllegalArgumentException(); + } + this.subSegmentList = UnmodifiableArrayList.asCopyOf(subSegments); + } + + /** + * Creates a KeySegment a list of sub-segments. + * + * @param subSegmentList List of sub-segments + */ + public KeySegment(List subSegmentList) { + if (subSegmentList.size() < 1) { + throw new IllegalArgumentException(); + } + this.subSegmentList = + new UnmodifiableArrayList( + subSegmentList.toArray( + new NameSegment[subSegmentList.size()])); + } + + public String toString() { + final StringBuilder buf = new StringBuilder(); + toString(buf); + return buf.toString(); + } + + public void toString(StringBuilder buf) { + for (IdentifierSegment segment : subSegmentList) { + buf.append('&'); + segment.toString(buf); + } + } + + public ParseRegion getRegion() { + return IdentifierNode.sumSegmentRegions(subSegmentList); + } + + public Quoting getQuoting() { + return Quoting.KEY; + } + + public String getName() { + return null; + } + + public List getKeyParts() { + return subSegmentList; + } +} + +// End KeySegment.java diff --git a/src/org/olap4j/mdx/NameSegment.java b/src/org/olap4j/mdx/NameSegment.java new file mode 100644 index 0000000..c34cfde --- /dev/null +++ b/src/org/olap4j/mdx/NameSegment.java @@ -0,0 +1,99 @@ +/* +// $Id: IdentifierNode.java 359 2010-10-14 21:24:51Z jhyde $ +// This software is subject to the terms of the Eclipse Public License v1.0 +// Agreement, available at the following URL: +// http://www.eclipse.org/legal/epl-v10.html. +// Copyright (C) 2007-2010 Julian Hyde +// All Rights Reserved. +// You must accept the terms of that agreement to use this software. +*/ +package org.olap4j.mdx; + +import org.olap4j.impl.Olap4jUtil; + +import java.util.List; + +/** + * Component in a compound identifier that describes the name of an object. + * Optionally, the name is quoted in brackets. + * + * @see KeySegment + * + * @version $Id: IdentifierNode.java 359 2010-10-14 21:24:51Z jhyde $ + * @author jhyde + */ +public class NameSegment implements IdentifierSegment { + final String name; + final Quoting quoting; + private final ParseRegion region; + + /** + * Creates a segment with the given quoting and region. + * + * @param region Region of source code + * @param name Name + * @param quoting Quoting style + */ + public NameSegment( + ParseRegion region, + String name, + Quoting quoting) + { + this.region = region; + this.name = name; + this.quoting = quoting; + if (!(quoting == Quoting.QUOTED || quoting == Quoting.UNQUOTED)) { + throw new IllegalArgumentException(); + } + } + + /** + * Creates a quoted segment, "[name]". + * + * @param name Name of segment + */ + public NameSegment(String name) { + this(null, name, Quoting.QUOTED); + } + + public String toString() { + switch (quoting) { + case UNQUOTED: + return name; + case QUOTED: + return IdentifierNode.quoteMdxIdentifier(name); + default: + throw Olap4jUtil.unexpected(quoting); + } + } + + public void toString(StringBuilder buf) { + switch (quoting) { + case UNQUOTED: + buf.append(name); + return; + case QUOTED: + IdentifierNode.quoteMdxIdentifier(name, buf); + return; + default: + throw Olap4jUtil.unexpected(quoting); + } + } + public ParseRegion getRegion() { + return region; + } + + public String getName() { + return name; + } + + public Quoting getQuoting() { + return quoting; + } + + public List getKeyParts() { + return null; + } +} + +// End NameSegment.java diff --git a/src/org/olap4j/mdx/Quoting.java b/src/org/olap4j/mdx/Quoting.java new file mode 100644 index 0000000..348fa0b --- /dev/null +++ b/src/org/olap4j/mdx/Quoting.java @@ -0,0 +1,45 @@ +/* +// $Id: IdentifierNode.java 359 2010-10-14 21:24:51Z jhyde $ +// This software is subject to the terms of the Eclipse Public License v1.0 +// Agreement, available at the following URL: +// http://www.eclipse.org/legal/epl-v10.html. +// Copyright (C) 2007-2010 Julian Hyde +// All Rights Reserved. +// You must accept the terms of that agreement to use this software. +*/ +package org.olap4j.mdx; + +/** + * Enumeration of styles by which the component of an identifier can be + * quoted. + * + * @see org.olap4j.mdx.IdentifierSegment + * + * @version $Id: IdentifierNode.java 359 2010-10-14 21:24:51Z jhyde $ + * @author jhyde + */ +public enum Quoting { + + /** + * Unquoted identifier, for example "Measures". + */ + UNQUOTED, + + /** + * Quoted identifier, for example "[Measures]". + */ + QUOTED, + + /** + * Identifier quoted with an ampersand and brackets to indicate a key + * value, for example the second segment in "[Employees].&[89]". + * + *

          Such a segment has one or more sub-segments. Each segment is + * either quoted or unquoted. For example, the second segment in + * "[Employees].&[89]&[San Francisco]&CA&USA" has four sub-segments, + * two quoted and two unquoted. + */ + KEY, +} + +// End Quoting.java diff --git a/src/org/olap4j/mdx/parser/impl/DefaultMdxParser.cup b/src/org/olap4j/mdx/parser/impl/DefaultMdxParser.cup index 62c3790..8827681 100644 --- a/src/org/olap4j/mdx/parser/impl/DefaultMdxParser.cup +++ b/src/org/olap4j/mdx/parser/impl/DefaultMdxParser.cup @@ -365,7 +365,7 @@ non terminal Axis.Standard non terminal String comp_op, keyword; -non terminal IdentifierNode.Segment +non terminal IdentifierSegment identifier, key_identifier, quoted_identifier, @@ -425,34 +425,31 @@ start with statement; quoted_identifier ::= QUOTED_ID:i {: ParseRegion region = createRegion(ileft, iright); - RESULT = new IdentifierNode.NameSegment( - region, i, IdentifierNode.Quoting.QUOTED); + RESULT = new NameSegment(region, i, Quoting.QUOTED); :} ; unquoted_identifier ::= ID:i {: ParseRegion region = createRegion(ileft, iright); - RESULT = new IdentifierNode.NameSegment( - region, i, IdentifierNode.Quoting.UNQUOTED); + RESULT = new NameSegment(region, i, Quoting.UNQUOTED); :} | keyword:i {: ParseRegion region = createRegion(ileft, iright); - RESULT = new IdentifierNode.NameSegment( - region, i, IdentifierNode.Quoting.UNQUOTED); + RESULT = new NameSegment(region, i, Quoting.UNQUOTED); :} ; // for example '&foo&[1]&bar' in '[x].&foo&[1]&bar.[y]' key_identifier ::= amp_identifier_list:list {: - RESULT = new IdentifierNode.KeySegment(list); + RESULT = new KeySegment(list); :} ; amp_identifier_list ::= amp_identifier:i {: - RESULT = new ArrayList(); + RESULT = new ArrayList(); RESULT.add(i); :} | @@ -471,16 +468,14 @@ amp_identifier ::= amp_quoted_identifier ::= AMP_QUOTED_ID:i {: ParseRegion region = createRegion(ileft, iright); - RESULT = new IdentifierNode.NameSegment( - region, i, IdentifierNode.Quoting.QUOTED); + RESULT = new NameSegment(region, i, Quoting.QUOTED); :} ; amp_unquoted_identifier ::= AMP_UNQUOTED_ID:i {: ParseRegion region = createRegion(ileft, iright); - RESULT = new IdentifierNode.NameSegment( - region, i, IdentifierNode.Quoting.UNQUOTED); + RESULT = new NameSegment(region, i, Quoting.UNQUOTED); :} ; diff --git a/src/org/olap4j/metadata/Cube.java b/src/org/olap4j/metadata/Cube.java index d5fde7f..95aeb21 100644 --- a/src/org/olap4j/metadata/Cube.java +++ b/src/org/olap4j/metadata/Cube.java @@ -10,6 +10,7 @@ package org.olap4j.metadata; import org.olap4j.OlapException; +import org.olap4j.mdx.IdentifierSegment; import java.util.*; @@ -114,28 +115,44 @@ public interface Cube extends MetadataElement { * each successive member on the path from the root member. If a member's * name is unique within its level, preceding member name can be omitted. * - *

          For example, - * lookupMember("Product", "Food") - * and - * lookupMember("Product", "All Products", "Food") + *

          For example, {@code "[Product].[Food]"} and + * {@code "[Product].[All Products].[Food]"} * are both valid ways to locate the "Food" member of the "Product" * dimension. * + *

          The name is represented as a list of {@link IdentifierSegment} + * objects. There are some common ways to create such a list. If you have an + * identifier, call + * {@link org.olap4j.mdx.IdentifierNode#parseIdentifier(String)} + * to parse the string into an identifier, then + * {@link org.olap4j.mdx.IdentifierNode#getSegmentList()}. For example, + * + *

          Member member = cube.lookupMember(
          + *   IdentifierNode.parseIdentifier( + * "[Product].[Food]").getSegmentList())
          + * + *

          If you have an array of names, call + * {@link org.olap4j.mdx.IdentifierNode#ofNames(String...)}. For example, + * + *

          Member member = cube.lookupMember(
          + *   IdentifierNode.parseIdentifier( + * "[Product].[Food]").getSegmentList())
          + * * @param nameParts Components of the fully-qualified member name * * @return member with the given name, or null if not found * * @throws OlapException if error occurs */ - Member lookupMember(String... nameParts) throws OlapException; + Member lookupMember(List nameParts) throws OlapException; /** * Finds a collection of members in the current Cube related to a given * member. * *

          The method first looks up a member with the given fully-qualified - * name as for {@link #lookupMember(String[])}, then applies the set of - * tree-operations to find related members. + * name as for {@link #lookupMember(java.util.List)}, then applies the set + * of tree-operations to find related members. * *

          The returned collection is sorted by level number then by member * ordinal. If no member is found with the given name, the collection is @@ -182,7 +199,7 @@ public interface Cube extends MetadataElement { */ List lookupMembers( Set treeOps, - String... nameParts) throws OlapException; + List nameParts) throws OlapException; } // End Cube.java diff --git a/src/org/olap4j/query/Query.java b/src/org/olap4j/query/Query.java index 15b2fb5..60be5fb 100644 --- a/src/org/olap4j/query/Query.java +++ b/src/org/olap4j/query/Query.java @@ -141,6 +141,15 @@ public void swapAxes() { down.notifyAdd(acrossChildList); } + /** + * Returns the query axis for a given axis type. + * + *

          If you pass axis=null, returns a special axis that is used to hold + * all unused hierarchies. (We may change this behavior in future.) + * + * @param axis Axis type + * @return Query axis + */ public QueryAxis getAxis(Axis axis) { return this.axes.get(axis); } diff --git a/src/org/olap4j/query/QueryAxis.java b/src/org/olap4j/query/QueryAxis.java index a30ab39..3b137b9 100644 --- a/src/org/olap4j/query/QueryAxis.java +++ b/src/org/olap4j/query/QueryAxis.java @@ -11,6 +11,7 @@ import org.olap4j.Axis; import org.olap4j.OlapException; +import org.olap4j.mdx.IdentifierSegment; import org.olap4j.metadata.Measure; import org.olap4j.metadata.Member; @@ -261,30 +262,31 @@ public void sort(SortOrder order) throws OlapException { } /** - *

          Sorts the axis according to the supplied order + * Sorts the axis according to the supplied order * and member unique name. + * *

          Using this method will try to resolve the supplied name * parts from the underlying cube and find the corresponding * member. This member will then be passed as a sort evaluation * expression. + * * @param order The {@link SortOrder} in which to * sort the axis. * @param nameParts The unique name parts of the sort * evaluation expression. * @throws OlapException If the supplied member cannot be resolved - * with {@link org.olap4j.metadata.Cube#lookupMember(String...)} + * with {@link org.olap4j.metadata.Cube#lookupMember(java.util.List)} */ - public void sort(SortOrder order, String... nameParts) + public void sort(SortOrder order, List nameParts) throws OlapException { assert order != null; assert nameParts != null; Member member = query.getCube().lookupMember(nameParts); - if (member != null) { - sort(order, member); - } else { + if (member == null) { throw new OlapException("Cannot find member."); } + sort(order, member); } /** diff --git a/src/org/olap4j/query/QueryDimension.java b/src/org/olap4j/query/QueryDimension.java index 172dca6..2d5b811 100644 --- a/src/org/olap4j/query/QueryDimension.java +++ b/src/org/olap4j/query/QueryDimension.java @@ -11,9 +11,7 @@ import org.olap4j.OlapException; import org.olap4j.impl.IdentifierParser; -import org.olap4j.impl.Olap4jUtil; -import org.olap4j.mdx.IdentifierNode; -import org.olap4j.mdx.IdentifierNode.Segment; +import org.olap4j.mdx.IdentifierSegment; import org.olap4j.metadata.*; import java.util.HashMap; @@ -68,63 +66,37 @@ public String getName() { return dimension.getName(); } - @Deprecated - public void select(String... nameParts) throws OlapException { - this.include(nameParts); - } - - @Deprecated - public void select( - Selection.Operator operator, - String... nameParts) throws OlapException - { - this.include(operator, nameParts); - } - - @Deprecated - public void select(Member member) { - this.include(member); - } - - @Deprecated - public void select( - Selection.Operator operator, - Member member) - { - this.include(operator, member); - } - - /** - * Clears the current member inclusions from this query dimension. - * @deprecated This method is deprecated in favor of - * {@link QueryDimension#clearInclusions()} - */ - @Deprecated - public void clearSelection() { - this.clearInclusions(); - } - /** * Selects members and includes them in the query. + * *

          This method selects and includes a single member with the * {@link Selection.Operator#MEMBER} operator. + * * @param nameParts Name of the member to select and include. * @throws OlapException If no member corresponding to the supplied * name parts could be resolved in the cube. */ - public Selection include(String... nameParts) throws OlapException { + public Selection include( + List nameParts) + throws OlapException + { return this.include(Selection.Operator.MEMBER, nameParts); } - public Selection createSelection(String... nameParts) throws OlapException { + public Selection createSelection( + List nameParts) + throws OlapException + { return this.createSelection(Selection.Operator.MEMBER, nameParts); - } + } /** * Selects members and includes them in the query. - *

          This method selects and includes a member along with it's + * + *

          This method selects and includes a member along with its * relatives, depending on the supplied {@link Selection.Operator} * operator. + * * @param operator Selection operator that defines what relatives of the * supplied member name to include along. * @param nameParts Name of the root member to select and include. @@ -133,34 +105,30 @@ public Selection createSelection(String... nameParts) throws OlapException { */ public Selection include( Selection.Operator operator, - String... nameParts) throws OlapException + List nameParts) throws OlapException { Member member = this.getQuery().getCube().lookupMember(nameParts); if (member == null) { throw new OlapException( - "Unable to find a member with name " - + Olap4jUtil.stringArrayToString(nameParts)); - } else { - return this.include( - operator, - member); + "Unable to find a member with name " + nameParts); } + return this.include( + operator, + member); } public Selection createSelection( Selection.Operator operator, - String... nameParts) throws OlapException + List nameParts) throws OlapException { Member member = this.getQuery().getCube().lookupMember(nameParts); if (member == null) { throw new OlapException( - "Unable to find a member with name " - + Olap4jUtil.stringArrayToString(nameParts)); - } else { - return this.createSelection( - operator, - member); + "Unable to find a member with name " + nameParts); } + return this.createSelection( + operator, + member); } /** @@ -250,21 +218,28 @@ public void clearInclusions() { /** * Selects members and excludes them from the query. + * *

          This method selects and excludes a single member with the * {@link Selection.Operator#MEMBER} operator. + * * @param nameParts Name of the member to select and exclude. * @throws OlapException If no member corresponding to the supplied * name parts could be resolved in the cube. */ - public void exclude(String... nameParts) throws OlapException { + public void exclude( + List nameParts) + throws OlapException + { this.exclude(Selection.Operator.MEMBER, nameParts); } /** * Selects members and excludes them from the query. - *

          This method selects and excludes a member along with it's + * + *

          This method selects and excludes a member along with its * relatives, depending on the supplied {@link Selection.Operator} * operator. + * * @param operator Selection operator that defines what relatives of the * supplied member name to exclude along. * @param nameParts Name of the root member to select and exclude. @@ -273,18 +248,16 @@ public void exclude(String... nameParts) throws OlapException { */ public void exclude( Selection.Operator operator, - String... nameParts) throws OlapException + List nameParts) throws OlapException { Member rootMember = this.getQuery().getCube().lookupMember(nameParts); if (rootMember == null) { throw new OlapException( - "Unable to find a member with name " - + Olap4jUtil.stringArrayToString(nameParts)); - } else { - this.exclude( - operator, - rootMember); + "Unable to find a member with name " + nameParts); } + this.exclude( + operator, + rootMember); } /** @@ -344,15 +317,6 @@ public void clearExclusions() { this.notifyRemove(removed); } - public static String[] getNameParts(String sel) { - List list = IdentifierParser.parseIdentifier(sel); - String nameParts[] = new String[list.size()]; - for (int i = 0; i < list.size(); i++) { - nameParts[i] = list.get(i).getName(); - } - return nameParts; - } - /** * Resolves a selection of members into an actual list * of the root member and it's relatives selected by the Selection object. @@ -393,7 +357,7 @@ public List resolve(Selection selection) throws OlapException return query.getCube().lookupMembers( set, - getNameParts(selection.getName())); + IdentifierParser.parseIdentifier(selection.getName())); } catch (Exception e) { throw new OlapException( "Error while resolving selection " + selection.toString(), @@ -401,18 +365,6 @@ public List resolve(Selection selection) throws OlapException } } - /** - * Returns a list of the inclusions within this dimension. - *

          Be aware that modifications to this list might - * have unpredictable consequences.

          - * @deprecated Use {@link QueryDimension#getInclusions()} - * @return list of inclusions - */ - @Deprecated - public List getSelections() { - return this.getInclusions(); - } - /** * Returns a list of the inclusions within this dimension. * diff --git a/src/org/olap4j/sample/SimpleQuerySample.java b/src/org/olap4j/sample/SimpleQuerySample.java index afea5b9..f1b35c7 100644 --- a/src/org/olap4j/sample/SimpleQuerySample.java +++ b/src/org/olap4j/sample/SimpleQuerySample.java @@ -261,7 +261,7 @@ void executeSelectNode(OlapConnection connection) { SelectNode query = new SelectNode(); query.setFrom( new IdentifierNode( - new IdentifierNode.NameSegment("Sales"))); + new NameSegment("Sales"))); query.getAxisList().add( new AxisNode( null, @@ -273,8 +273,8 @@ void executeSelectNode(OlapConnection connection) { "{}", Syntax.Braces, new IdentifierNode( - new IdentifierNode.NameSegment("Measures"), - new IdentifierNode.NameSegment("Unit Sales"))))); + new NameSegment("Measures"), + new NameSegment("Unit Sales"))))); // Create a statement based upon the query model. OlapStatement stmt; diff --git a/testsrc/org/olap4j/ConnectionTest.java b/testsrc/org/olap4j/ConnectionTest.java index fd1b391..ccffa72 100644 --- a/testsrc/org/olap4j/ConnectionTest.java +++ b/testsrc/org/olap4j/ConnectionTest.java @@ -25,6 +25,8 @@ import java.sql.*; import java.util.*; +import static org.olap4j.test.TestContext.nameList; + /** * Unit test for olap4j Driver and Connection classes. * @@ -1375,7 +1377,7 @@ public void testUnparsing() { // Note that the select statement constructed here is equivalent // to the one in testParsing. final IdentifierNode cubeName = - new IdentifierNode(new IdentifierNode.NameSegment("sales")); + new IdentifierNode(new NameSegment("sales")); SelectNode select = new SelectNode( null, new ArrayList(), @@ -1392,11 +1394,11 @@ public void testUnparsing() { new WithMemberNode( null, new IdentifierNode( - new IdentifierNode.NameSegment("Measures"), - new IdentifierNode.NameSegment("Foo")), + new NameSegment("Measures"), + new NameSegment("Foo")), new IdentifierNode( - new IdentifierNode.NameSegment("Measures"), - new IdentifierNode.NameSegment("Bar")), + new NameSegment("Measures"), + new NameSegment("Bar")), Arrays.asList( new PropertyValueNode( null, @@ -1417,7 +1419,7 @@ public void testUnparsing() { Arrays.asList( (ParseTreeNode) new IdentifierNode( - new IdentifierNode.NameSegment("Gender")))))); + new NameSegment("Gender")))))); select.getAxisList().add( new AxisNode( null, @@ -1433,12 +1435,12 @@ public void testUnparsing() { "Children", Syntax.Property, new IdentifierNode( - new IdentifierNode.NameSegment("Store")))))); + new NameSegment("Store")))))); select.getFilterAxis().setExpression( new IdentifierNode( - new IdentifierNode.NameSegment("Time"), - new IdentifierNode.NameSegment("1997"), - new IdentifierNode.NameSegment("Q4"))); + new NameSegment("Time"), + new NameSegment("1997"), + new NameSegment("Q4"))); assertEquals(select.getFrom(), cubeName); checkUnparsedMdx(select); @@ -1448,7 +1450,7 @@ public void testUnparsing() { null, new ArrayList(), new ArrayList(), - new IdentifierNode(new IdentifierNode.NameSegment("warehouse")), + new IdentifierNode(new NameSegment("warehouse")), new AxisNode( null, false, @@ -1481,7 +1483,7 @@ public void testBuildParseTree() { null, new ArrayList(), new ArrayList(), - new IdentifierNode(new IdentifierNode.NameSegment("sales")), + new IdentifierNode(new NameSegment("sales")), new AxisNode( null, false, @@ -1500,7 +1502,7 @@ public void testBuildParseTree() { null, new ArrayList(), new ArrayList(), - new IdentifierNode(new IdentifierNode.NameSegment("sales")), + new IdentifierNode(new NameSegment("sales")), new AxisNode( null, false, @@ -1526,11 +1528,11 @@ public void testBuildParseTree() { "()", Syntax.Parentheses, new IdentifierNode( - new IdentifierNode.NameSegment("Measures"), - new IdentifierNode.NameSegment("Store Sales")), + new NameSegment("Measures"), + new NameSegment("Store Sales")), new IdentifierNode( - new IdentifierNode.NameSegment("Gender"), - new IdentifierNode.NameSegment("M")))); + new NameSegment("Gender"), + new NameSegment("M")))); checkUnparsedMdx( select, "SELECT\n" @@ -1546,7 +1548,7 @@ public void testBuildParseTree() { } /** - * Tests the {@link Cube#lookupMember(String...)} method. + * Tests the {@link Cube#lookupMember(java.util.List} method. */ public void testCubeLookupMember() throws Exception { Class.forName(tester.getDriverClassName()); @@ -1556,8 +1558,7 @@ public void testCubeLookupMember() throws Exception { Cube cube = olapConnection.getSchema().getCubes().get("Sales Ragged"); Member member = - cube.lookupMember( - "Time", "1997", "Q2"); + cube.lookupMember(nameList("Time", "1997", "Q2")); assertEquals("[Time].[1997].[Q2]", member.getUniqueName()); // Member.getChildMemberCount @@ -1575,25 +1576,23 @@ public void testCubeLookupMember() throws Exception { assertNull(childMembers.get("1")); member = - cube.lookupMember( - "Time", "1997", "Q5"); + cube.lookupMember(nameList("Time", "1997", "Q5")); assertNull(member); // arguably this should return [Customers].[All Customers]; but it // makes a bit more sense for it to return null member = - cube.lookupMember( - "Customers"); + cube.lookupMember(nameList("Customers")); assertNull(member); member = - cube.lookupMember( - "Customers", "All Customers"); + cube.lookupMember(nameList("Customers", "All Customers")); assertTrue(member.isAll()); } /** - * Tests the {@link Cube#lookupMembers(java.util.Set, String...)} method. + * Tests the {@link Cube#lookupMembers(java.util.Set, java.util.List)} + * method. */ public void testCubeLookupMembers() throws Exception { Class.forName(tester.getDriverClassName()); @@ -1606,7 +1605,7 @@ public void testCubeLookupMembers() throws Exception { cube.lookupMembers( Olap4jUtil.enumSetOf( Member.TreeOp.ANCESTORS, Member.TreeOp.CHILDREN), - "Time", "1997", "Q2"); + nameList("Time", "1997", "Q2")); String expected; switch (tester.getFlavor()) { case XMLA: @@ -1635,7 +1634,7 @@ public void testCubeLookupMembers() throws Exception { cube.lookupMembers( Olap4jUtil.enumSetOf( Member.TreeOp.ANCESTORS, Member.TreeOp.CHILDREN), - "Time", "1997", "Q5"); + nameList("Time", "1997", "Q5")); assertTrue(memberList.isEmpty()); // ask for parent & ancestors; should not get duplicates @@ -1643,7 +1642,7 @@ public void testCubeLookupMembers() throws Exception { cube.lookupMembers( Olap4jUtil.enumSetOf( Member.TreeOp.ANCESTORS, Member.TreeOp.PARENT), - "Time", "1997", "Q2"); + nameList("Time", "1997", "Q2")); TestContext.assertEqualsVerbose( "[Time].[1997]\n", memberListToString(memberList)); @@ -1653,7 +1652,7 @@ public void testCubeLookupMembers() throws Exception { cube.lookupMembers( Olap4jUtil.enumSetOf( Member.TreeOp.ANCESTORS, Member.TreeOp.PARENT), - "Product"); + nameList("Product")); assertTrue(memberList.isEmpty()); // ask for siblings and children, and the results should be @@ -1662,7 +1661,7 @@ public void testCubeLookupMembers() throws Exception { cube.lookupMembers( Olap4jUtil.enumSetOf( Member.TreeOp.SIBLINGS, Member.TreeOp.CHILDREN), - "Time", "1997", "Q2"); + nameList("Time", "1997", "Q2")); switch (tester.getFlavor()) { case XMLA: case REMOTE_XMLA: @@ -1694,7 +1693,7 @@ public void testCubeLookupMembers() throws Exception { memberList = cube.lookupMembers( Olap4jUtil.enumSetOf(Member.TreeOp.SIBLINGS), - "Time", "1997"); + nameList("Time", "1997")); TestContext.assertEqualsVerbose( "[Time].[1998]\n", memberListToString(memberList)); @@ -1703,7 +1702,7 @@ public void testCubeLookupMembers() throws Exception { cube.lookupMembers( Olap4jUtil.enumSetOf( Member.TreeOp.SIBLINGS, Member.TreeOp.SELF), - "Customers", "USA", "OR"); + nameList("Customers", "USA", "OR")); TestContext.assertEqualsVerbose( "[Customers].[USA].[CA]\n" + "[Customers].[USA].[OR]\n" @@ -1868,14 +1867,21 @@ public void testMetadata() throws Exception { // ~ Member - Member member = cube.lookupMember("Product", "Food", "Marshmallows"); + Member member = + cube.lookupMember( + nameList("Product", "Food", "Marshmallows")); assertNull(member); // we don't sell marshmallows! - member = cube.lookupMember("Product", "Food"); + member = + cube.lookupMember( + nameList("Product", "Food")); assertNotNull(member); - Member member2 = cube.lookupMember("Product", "All Products", "Food"); + Member member2 = + cube.lookupMember( + nameList("Product", "All Products", "Food")); assertEquals(member, member2); final Member bread = - cube.lookupMember("Product", "Food", "Baked Goods", "Bread"); + cube.lookupMember( + nameList("Product", "Food", "Baked Goods", "Bread")); assertEquals("[Product].[Food]", member.getUniqueName()); assertEquals("Food", member.getName()); diff --git a/testsrc/org/olap4j/OlapTest.java b/testsrc/org/olap4j/OlapTest.java index 1b1da90..351b3c9 100644 --- a/testsrc/org/olap4j/OlapTest.java +++ b/testsrc/org/olap4j/OlapTest.java @@ -20,6 +20,8 @@ import junit.framework.TestCase; +import static org.olap4j.test.TestContext.nameList; + /** * Unit test illustrating sequence of calls to olap4j API from a graphical * client. @@ -165,21 +167,23 @@ public void testModel() { QueryDimension timeQuery = query.getDimension("Time"); //$NON-NLS-1$ - Member productMember = cube.lookupMember("Product", "Drink"); + Member productMember = + cube.lookupMember(nameList("Product", "Drink")); // create some selections for Store storeQuery.include( - Selection.Operator.CHILDREN, "Store", "USA"); + Selection.Operator.CHILDREN, nameList("Store", "USA")); // create some selections for Product productQuery.clearInclusions(); productQuery.include( Selection.Operator.CHILDREN, productMember); productQuery.include( - Selection.Operator.CHILDREN, "Product", "Food"); + Selection.Operator.CHILDREN, nameList("Product", "Food")); // create some selections for Time - timeQuery.include(Selection.Operator.CHILDREN, "Time", "1997"); + timeQuery.include( + Selection.Operator.CHILDREN, nameList("Time", "1997")); // place our dimensions on the axes query.getAxis(Axis.COLUMNS).addDimension(productQuery); @@ -221,10 +225,10 @@ public void testSelectionModes() { QueryDimension productDimension = query.getDimension("Product"); productDimension.include( - Selection.Operator.CHILDREN, "Product", "Drink"); + Selection.Operator.CHILDREN, nameList("Product", "Drink")); QueryDimension measuresDimension = query.getDimension("Measures"); - measuresDimension.include("Measures", "Store Sales"); + measuresDimension.include(nameList("Measures", "Store Sales")); query.getAxis(Axis.ROWS).addDimension(productDimension); query.getAxis(Axis.COLUMNS).addDimension(measuresDimension); @@ -244,7 +248,7 @@ public void testSelectionModes() { productDimension.clearInclusions(); productDimension.include( - Selection.Operator.ANCESTORS, "Product", "Drink"); + Selection.Operator.ANCESTORS, nameList("Product", "Drink")); query.validate(); @@ -261,7 +265,7 @@ public void testSelectionModes() { productDimension.clearInclusions(); productDimension.include( - Selection.Operator.DESCENDANTS, "Product", "Drink"); + Selection.Operator.DESCENDANTS, nameList("Product", "Drink")); query.validate(); @@ -278,7 +282,8 @@ public void testSelectionModes() { productDimension.clearInclusions(); productDimension.include( - Selection.Operator.INCLUDE_CHILDREN, "Product", "Drink"); + Selection.Operator.INCLUDE_CHILDREN, + nameList("Product", "Drink")); query.validate(); @@ -295,7 +300,7 @@ public void testSelectionModes() { productDimension.clearInclusions(); productDimension.include( - Selection.Operator.SIBLINGS, "Product", "Drink"); + Selection.Operator.SIBLINGS, nameList("Product", "Drink")); query.validate(); @@ -325,17 +330,18 @@ public void testMultipleDimensionSelections() { QueryDimension productDimension = query.getDimension("Product"); productDimension.include( - Selection.Operator.CHILDREN, "Product", "Drink"); + Selection.Operator.CHILDREN, nameList("Product", "Drink")); QueryDimension storeDimension = query.getDimension("Store"); storeDimension.include( - Selection.Operator.INCLUDE_CHILDREN, "Store", "USA"); + Selection.Operator.INCLUDE_CHILDREN, nameList("Store", "USA")); QueryDimension timeDimension = query.getDimension("Time"); - timeDimension.include(Selection.Operator.CHILDREN, "Time", "1997"); + timeDimension.include( + Selection.Operator.CHILDREN, nameList("Time", "1997")); QueryDimension measuresDimension = query.getDimension("Measures"); - measuresDimension.include("Measures", "Store Sales"); + measuresDimension.include(nameList("Measures", "Store Sales")); query.getAxis(Axis.ROWS).addDimension(productDimension); query.getAxis(Axis.ROWS).addDimension(storeDimension); @@ -372,10 +378,10 @@ public void testSwapAxes() { QueryDimension productDimension = query.getDimension("Product"); productDimension.include( - Selection.Operator.CHILDREN, "Product", "Drink"); + Selection.Operator.CHILDREN, nameList("Product", "Drink")); QueryDimension measuresDimension = query.getDimension("Measures"); - measuresDimension.include("Measures", "Store Sales"); + measuresDimension.include(nameList("Measures", "Store Sales")); query.getAxis(Axis.ROWS).addDimension(productDimension); query.getAxis(Axis.COLUMNS).addDimension(measuresDimension); @@ -435,13 +441,14 @@ public void testSortDimension() { QueryDimension productDimension = query.getDimension("Product"); productDimension.include( - Selection.Operator.INCLUDE_CHILDREN, "Product", "Drink"); + Selection.Operator.INCLUDE_CHILDREN, + nameList("Product", "Drink")); QueryDimension measuresDimension = query.getDimension("Measures"); - measuresDimension.include("Measures", "Store Sales"); + measuresDimension.include(nameList("Measures", "Store Sales")); QueryDimension timeDimension = query.getDimension("Time"); - timeDimension.include("Time", "Year", "1997", "Q3", "7"); + timeDimension.include(nameList("Time", "Year", "1997", "Q3", "7")); query.getAxis(Axis.ROWS).addDimension(productDimension); query.getAxis(Axis.COLUMNS).addDimension(measuresDimension); @@ -514,13 +521,13 @@ public void testSortMultipleDimension() { QueryDimension productDimension = query.getDimension("Product"); productDimension.include( - Selection.Operator.CHILDREN, "Product", "Drink"); + Selection.Operator.CHILDREN, nameList("Product", "Drink")); QueryDimension measuresDimension = query.getDimension("Measures"); - measuresDimension.include("Measures", "Store Sales"); + measuresDimension.include(nameList("Measures", "Store Sales")); QueryDimension timeDimension = query.getDimension("Time"); - timeDimension.include("Time", "Year", "1997", "Q3", "7"); + timeDimension.include(nameList("Time", "Year", "1997", "Q3", "7")); query.getAxis(Axis.ROWS).addDimension(timeDimension); query.getAxis(Axis.ROWS).addDimension(productDimension); @@ -579,7 +586,7 @@ public void testSortMultipleDimension() { query.getAxis(Axis.ROWS).setNonEmpty(true); productDimension.clearInclusions(); productDimension.include( - Selection.Operator.CHILDREN, "Product", "Food"); + Selection.Operator.CHILDREN, nameList("Product", "Food")); SelectNode sortedMdxNonEmpty = query.getSelect(); String sortedMdxNonEmptyString = sortedMdxNonEmpty.toString(); @@ -645,35 +652,36 @@ public void testSelectionContext() throws Exception { // create selections QueryDimension productDimension = query.getDimension("Product"); productDimension.include( - Selection.Operator.INCLUDE_CHILDREN, "Product", "All Products"); + Selection.Operator.INCLUDE_CHILDREN, + nameList("Product", "All Products")); QueryDimension timeDimension = query.getDimension("Time"); timeDimension.include( - Selection.Operator.MEMBER, "Time", "Year", "1997"); + Selection.Operator.MEMBER, nameList("Time", "Year", "1997")); Selection selection = timeDimension.include( - Selection.Operator.CHILDREN, "Time", "Year", "1997"); + Selection.Operator.CHILDREN, nameList("Time", "Year", "1997")); selection.addContext( productDimension.createSelection( - "Product", "All Products", "Drink")); + nameList("Product", "All Products", "Drink"))); // [Store].[All Stores] QueryDimension storeDimension = query.getDimension("Store"); storeDimension.include( - Selection.Operator.MEMBER, "Store", "All Stores"); + Selection.Operator.MEMBER, nameList("Store", "All Stores")); Selection children = storeDimension.include( - Selection.Operator.CHILDREN, "Store", "All Stores"); + Selection.Operator.CHILDREN, nameList("Store", "All Stores")); children.addContext( productDimension.createSelection( - "Product", "All Products", "Drink")); + nameList("Product", "All Products", "Drink"))); children.addContext( - timeDimension.createSelection("Time", "1997", "Q3")); + timeDimension.createSelection(nameList("Time", "1997", "Q3"))); QueryDimension measuresDimension = query.getDimension("Measures"); - measuresDimension.include("Measures", "Store Sales"); + measuresDimension.include(nameList("Measures", "Store Sales")); query.getAxis(Axis.ROWS).addDimension(productDimension); query.getAxis(Axis.ROWS).addDimension(timeDimension); @@ -702,8 +710,7 @@ public void testSelectionContext() throws Exception { // Sort the rows in ascending order. query.getAxis(Axis.ROWS).sort( SortOrder.ASC, - "Measures", - "Store Sales"); + nameList("Measures", "Store Sales")); SelectNode sortedMdx = query.getSelect(); String sortedMdxString = sortedMdx.toString(); @@ -759,18 +766,18 @@ public void testComplexSelectionContext() throws Exception { productDimension.include( - Selection.Operator.MEMBER, "Product", "All Products"); + Selection.Operator.MEMBER, nameList("Product", "All Products")); productDimension.include( - Selection.Operator.CHILDREN, "Product", "All Products"); + Selection.Operator.CHILDREN, nameList("Product", "All Products")); QueryDimension timeDimension = query.getDimension("Time"); Selection selection = timeDimension.include( - Selection.Operator.CHILDREN, "Time", "Year", "1997"); + Selection.Operator.CHILDREN, nameList("Time", "Year", "1997")); selection.addContext( - productDimension.createSelection( - "Product", "All Products")); + productDimension.createSelection( + nameList("Product", "All Products"))); QueryDimension measuresDimension = query.getDimension("Measures"); - measuresDimension.include("Measures", "Store Sales"); + measuresDimension.include(nameList("Measures", "Store Sales")); query.getAxis(Axis.ROWS).addDimension(productDimension); query.getAxis(Axis.ROWS).addDimension(timeDimension); @@ -852,13 +859,14 @@ public void testSortAxis() { QueryDimension productDimension = query.getDimension("Product"); productDimension.include( - Selection.Operator.INCLUDE_CHILDREN, "Product", "Drink"); + Selection.Operator.INCLUDE_CHILDREN, + nameList("Product", "Drink")); QueryDimension measuresDimension = query.getDimension("Measures"); - measuresDimension.include("Measures", "Store Sales"); + measuresDimension.include(nameList("Measures", "Store Sales")); QueryDimension timeDimension = query.getDimension("Time"); - timeDimension.include("Time", "Year", "1997", "Q3", "7"); + timeDimension.include(nameList("Time", "Year", "1997", "Q3", "7")); query.getAxis(Axis.ROWS).addDimension(productDimension); query.getAxis(Axis.COLUMNS).addDimension(measuresDimension); @@ -886,8 +894,7 @@ public void testSortAxis() { // Sort the rows in ascending order. query.getAxis(Axis.ROWS).sort( SortOrder.BASC, - "Measures", - "Store Sales"); + nameList("Measures", "Store Sales")); SelectNode sortedMdx = query.getSelect(); String sortedMdxString = sortedMdx.toString(); @@ -934,17 +941,18 @@ public void testDimensionsOrder() { QueryDimension productDimension = query.getDimension("Product"); productDimension.include( - Selection.Operator.CHILDREN, "Product", "Drink"); + Selection.Operator.CHILDREN, nameList("Product", "Drink")); QueryDimension storeDimension = query.getDimension("Store"); storeDimension.include( - Selection.Operator.INCLUDE_CHILDREN, "Store", "USA"); + Selection.Operator.INCLUDE_CHILDREN, nameList("Store", "USA")); QueryDimension timeDimension = query.getDimension("Time"); - timeDimension.include(Selection.Operator.CHILDREN, "Time", "1997"); + timeDimension.include( + Selection.Operator.CHILDREN, nameList("Time", "1997")); QueryDimension measuresDimension = query.getDimension("Measures"); - measuresDimension.include("Measures", "Store Sales"); + measuresDimension.include(nameList("Measures", "Store Sales")); query.getAxis(Axis.ROWS).addDimension(productDimension); @@ -1020,11 +1028,11 @@ public void testDimensionsHierarchize() { QueryDimension storeDimension = query.getDimension("Store"); storeDimension.include( - Selection.Operator.INCLUDE_CHILDREN, "Store", "USA"); + Selection.Operator.INCLUDE_CHILDREN, nameList("Store", "USA")); storeDimension.setHierarchizeMode(HierarchizeMode.POST); QueryDimension measuresDimension = query.getDimension("Measures"); - measuresDimension.include("Measures", "Store Sales"); + measuresDimension.include(nameList("Measures", "Store Sales")); query.getAxis(Axis.ROWS).addDimension(storeDimension); query.getAxis(Axis.COLUMNS).addDimension(measuresDimension); @@ -1076,10 +1084,10 @@ public void testQueryVersusParseTreeIndependence() { QueryDimension productDimension = query.getDimension("Product"); productDimension.include( - Selection.Operator.INCLUDE_CHILDREN, "Product", "Drink"); + Selection.Operator.INCLUDE_CHILDREN, nameList("Product", "Drink")); QueryDimension measuresDimension = query.getDimension("Measures"); - measuresDimension.include("Measures", "Store Sales"); + measuresDimension.include(nameList("Measures", "Store Sales")); query.getAxis(Axis.ROWS).addDimension(productDimension); query.getAxis(Axis.COLUMNS).addDimension(measuresDimension); @@ -1107,20 +1115,20 @@ public void testQueryVersusParseTreeIndependence() { // change selections measuresDimension.include( - Selection.Operator.SIBLINGS, "Measures", "Customer Count"); + Selection.Operator.SIBLINGS, + nameList("Measures", "Customer Count")); productDimension.include( Selection.Operator.SIBLINGS, - "Product", "All Products", "Drink", "Alcoholic Beverages"); + nameList( + "Product", "All Products", "Drink", "Alcoholic Beverages")); // Add something to crossjoin with query.getAxis(Axis.ROWS).addDimension( query.getDimension("Gender")); query.getDimension("Gender").include( - Operator.CHILDREN, - "Gender", - "All Gender"); + Operator.CHILDREN, nameList("Gender", "All Gender")); - query.getAxis(Axis.UNUSED).addDimension( + query.getAxis(null).addDimension( query.getDimension("Product")); query.validate(); @@ -1145,17 +1153,14 @@ public void testExclusionModes() { QueryDimension productDimension = query.getDimension("Product"); productDimension.include( Selection.Operator.CHILDREN, - "Product", "Drink", - "Beverages"); + nameList("Product", "Drink", "Beverages")); productDimension.include( Selection.Operator.CHILDREN, - "Product", - "Food", - "Frozen Foods"); + nameList("Product", "Food", "Frozen Foods")); QueryDimension measuresDimension = query.getDimension("Measures"); - measuresDimension.include("Measures", "Sales Count"); + measuresDimension.include(nameList("Measures", "Sales Count")); QueryDimension timeDimension = query.getDimension("Time"); - timeDimension.include("Time", "Year", "1997", "Q3", "7"); + timeDimension.include(nameList("Time", "Year", "1997", "Q3", "7")); query.getAxis(Axis.ROWS).addDimension(productDimension); query.getAxis(Axis.COLUMNS).addDimension(measuresDimension); query.getAxis(Axis.FILTER).addDimension(timeDimension); @@ -1205,10 +1210,8 @@ public void testExclusionModes() { // Exclude the Carbonated Beverages because they are not good // for your health. query.getDimension("Product").exclude( - "Product", - "Drink", - "Beverages", - "Carbonated Beverages"); + nameList( + "Product", "Drink", "Beverages", "Carbonated Beverages")); // Validate the generated MDX query.validate(); @@ -1267,20 +1270,17 @@ public void testExclusionMultipleDimensionModes() { QueryDimension productDimension = query.getDimension("Product"); productDimension.include( Selection.Operator.CHILDREN, - "Product", "Drink", - "Beverages"); + nameList("Product", "Drink", "Beverages")); productDimension.include( Selection.Operator.CHILDREN, - "Product", - "Food", - "Frozen Foods"); + nameList("Product", "Food", "Frozen Foods")); QueryDimension measuresDimension = query.getDimension("Measures"); - measuresDimension.include("Measures", "Sales Count"); + measuresDimension.include(nameList("Measures", "Sales Count")); QueryDimension timeDimension = query.getDimension("Time"); - timeDimension.include("Time", "Year", "1997", "Q3", "7"); + timeDimension.include(nameList("Time", "Year", "1997", "Q3", "7")); QueryDimension storeDimension = query.getDimension("Store"); storeDimension.include( - Selection.Operator.MEMBER, "Store", "USA"); + Selection.Operator.MEMBER, nameList("Store", "USA")); query.getAxis(Axis.ROWS).addDimension(storeDimension); query.getAxis(Axis.ROWS).addDimension(productDimension); query.getAxis(Axis.FILTER).addDimension(timeDimension); @@ -1332,10 +1332,8 @@ public void testExclusionMultipleDimensionModes() { // Exclude the Carbonated Beverages because they are not good // for your health. query.getDimension("Product").exclude( - "Product", - "Drink", - "Beverages", - "Carbonated Beverages"); + nameList( + "Product", "Drink", "Beverages", "Carbonated Beverages")); // Validate the generated MDX query.validate(); @@ -1394,20 +1392,17 @@ public void testCompoundFilter() { QueryDimension productDimension = query.getDimension("Product"); productDimension.include( Selection.Operator.MEMBER, - "Product", "Drink", - "Beverages"); + nameList("Product", "Drink", "Beverages")); productDimension.include( Selection.Operator.MEMBER, - "Product", - "Food", - "Frozen Foods"); + nameList("Product", "Food", "Frozen Foods")); QueryDimension measuresDimension = query.getDimension("Measures"); - measuresDimension.include("Measures", "Sales Count"); + measuresDimension.include(nameList("Measures", "Sales Count")); QueryDimension timeDimension = query.getDimension("Time"); - timeDimension.include("Time", "Year", "1997", "Q3", "7"); + timeDimension.include(nameList("Time", "Year", "1997", "Q3", "7")); QueryDimension storeDimension = query.getDimension("Store"); storeDimension.include( - Selection.Operator.MEMBER, "Store", "USA"); + Selection.Operator.MEMBER, nameList("Store", "USA")); query.getAxis(Axis.ROWS).addDimension(storeDimension); query.getAxis(Axis.FILTER).addDimension(productDimension); query.getAxis(Axis.FILTER).addDimension(timeDimension); @@ -1455,19 +1450,20 @@ public void testNonMandatoryQueryAxis() { QueryDimension productDimension = query.getDimension("Product"); productDimension.include( - Selection.Operator.CHILDREN, "Product", "Drink"); + Selection.Operator.CHILDREN, nameList("Product", "Drink")); QueryDimension storeDimension = query.getDimension("Store"); storeDimension.include( - Selection.Operator.INCLUDE_CHILDREN, "Store", "USA"); + Selection.Operator.INCLUDE_CHILDREN, nameList("Store", "USA")); storeDimension.setHierarchizeMode(HierarchizeMode.POST); QueryDimension timeDimension = query.getDimension("Time"); - timeDimension.include(Selection.Operator.CHILDREN, "Time", "1997"); + timeDimension.include( + Selection.Operator.CHILDREN, nameList("Time", "1997")); QueryDimension measuresDimension = query.getDimension("Measures"); - measuresDimension.include("Measures", "Store Sales"); + measuresDimension.include(nameList("Measures", "Store Sales")); //query.getAxis(Axis.ROWS).addDimension(productDimension); diff --git a/testsrc/org/olap4j/impl/Olap4jUtilTest.java b/testsrc/org/olap4j/impl/Olap4jUtilTest.java index 72a57d1..88bc7c3 100644 --- a/testsrc/org/olap4j/impl/Olap4jUtilTest.java +++ b/testsrc/org/olap4j/impl/Olap4jUtilTest.java @@ -9,8 +9,7 @@ package org.olap4j.impl; import junit.framework.TestCase; -import org.olap4j.mdx.IdentifierNode; -import org.olap4j.mdx.ParseRegion; +import org.olap4j.mdx.*; import java.util.*; @@ -347,7 +346,7 @@ private void checkParseFormattedCellValue( * Tests the {@link IdentifierNode#parseIdentifier} method. */ public void testParseIdentifier() { - List segments = + List segments = IdentifierParser.parseIdentifier( "[string].[with].[a [bracket]] in it]"); assertEquals(3, segments.size()); @@ -412,26 +411,26 @@ public void testParseIdentifier() { segments = IdentifierParser.parseIdentifier( "[ProductFilterDim].[Product Main Group Name].&[Maingroup (xyz)]"); assertEquals(3, segments.size()); - final IdentifierNode.Segment s0 = segments.get(0); + final IdentifierSegment s0 = segments.get(0); assertEquals("ProductFilterDim", s0.getName()); - assertEquals(IdentifierNode.Quoting.QUOTED, s0.getQuoting()); - final IdentifierNode.Segment s1 = segments.get(1); + assertEquals(Quoting.QUOTED, s0.getQuoting()); + final IdentifierSegment s1 = segments.get(1); assertEquals("Product Main Group Name", s1.getName()); - assertEquals(IdentifierNode.Quoting.QUOTED, s1.getQuoting()); - assertTrue(segments.get(2) instanceof IdentifierNode.KeySegment); - IdentifierNode.KeySegment s2 = - (IdentifierNode.KeySegment) segments.get(2); + assertEquals(Quoting.QUOTED, s1.getQuoting()); + assertTrue(segments.get(2) instanceof KeySegment); + KeySegment s2 = + (KeySegment) segments.get(2); assertEquals(1, s2.getKeyParts().size()); - final IdentifierNode.NameSegment s2k0 = s2.getKeyParts().get(0); + final NameSegment s2k0 = s2.getKeyParts().get(0); assertEquals("Maingroup (xyz)", s2k0.getName()); - assertEquals(IdentifierNode.Quoting.QUOTED, s2k0.getQuoting()); + assertEquals(Quoting.QUOTED, s2k0.getQuoting()); } /** * Advanced test for the {@link IdentifierNode#parseIdentifier} method. */ public void testParseIdentifierAdvanced() { - List segments; + List segments; // detailed example, per javadoc // @@ -450,39 +449,39 @@ public void testParseIdentifierAdvanced() { segments = IdentifierParser.parseIdentifier( "[Customers].[City].&[San Francisco]&CA&USA.&[cust1234]"); assertEquals(4, segments.size()); - final IdentifierNode.Segment s0 = segments.get(0); + final IdentifierSegment s0 = segments.get(0); assertEquals("Customers", s0.getName()); - assertEquals(IdentifierNode.Quoting.QUOTED, s0.getQuoting()); - final IdentifierNode.Segment s1 = segments.get(1); + assertEquals(Quoting.QUOTED, s0.getQuoting()); + final IdentifierSegment s1 = segments.get(1); assertEquals("City", s1.getName()); - assertEquals(IdentifierNode.Quoting.QUOTED, s1.getQuoting()); - assertTrue(segments.get(2) instanceof IdentifierNode.KeySegment); - IdentifierNode.KeySegment s2 = - (IdentifierNode.KeySegment) segments.get(2); + assertEquals(Quoting.QUOTED, s1.getQuoting()); + assertTrue(segments.get(2) instanceof KeySegment); + KeySegment s2 = + (KeySegment) segments.get(2); assertEquals(3, s2.getKeyParts().size()); - final IdentifierNode.NameSegment s2k0 = s2.getKeyParts().get(0); + final NameSegment s2k0 = s2.getKeyParts().get(0); assertEquals("San Francisco", s2k0.getName()); - assertEquals(IdentifierNode.Quoting.QUOTED, s2k0.getQuoting()); - final IdentifierNode.NameSegment s2k1 = s2.getKeyParts().get(1); + assertEquals(Quoting.QUOTED, s2k0.getQuoting()); + final NameSegment s2k1 = s2.getKeyParts().get(1); assertEquals("CA", s2k1.getName()); - assertEquals(IdentifierNode.Quoting.QUOTED, s2k0.getQuoting()); - final IdentifierNode.NameSegment s2k2 = s2.getKeyParts().get(0); + assertEquals(Quoting.QUOTED, s2k0.getQuoting()); + final NameSegment s2k2 = s2.getKeyParts().get(0); assertEquals("San Francisco", s2k2.getName()); - assertEquals(IdentifierNode.Quoting.QUOTED, s2k2.getQuoting()); - IdentifierNode.KeySegment s3 = - (IdentifierNode.KeySegment) segments.get(3); + assertEquals(Quoting.QUOTED, s2k2.getQuoting()); + KeySegment s3 = + (KeySegment) segments.get(3); assertNull(s3.getName()); assertEquals(1, s3.getKeyParts().size()); - final IdentifierNode.NameSegment s3k0 = s3.getKeyParts().get(0); + final NameSegment s3k0 = s3.getKeyParts().get(0); assertEquals("cust1234", s3k0.getName()); - assertEquals(IdentifierNode.Quoting.QUOTED, s3k0.getQuoting()); + assertEquals(Quoting.QUOTED, s3k0.getQuoting()); } /** * Tests the {@link IdentifierParser#parseIdentifierList(String)} method. */ public void testParseIdentifierList() { - List> list; + List> list; list = IdentifierParser.parseIdentifierList("{foo, baz.baz}"); assertEquals(2, list.size()); @@ -508,11 +507,11 @@ public void testParseIdentifierList() { assertEquals(1, list.get(0).size()); assertEquals(4, list.get(1).size()); assertEquals("baz", list.get(1).get(0).getName()); - final IdentifierNode.Segment id1s1 = list.get(1).get(1); + final IdentifierSegment id1s1 = list.get(1).get(1); assertEquals(2, id1s1.getKeyParts().size()); assertEquals("k0", id1s1.getKeyParts().get(0).getName()); assertEquals("k1", id1s1.getKeyParts().get(1).getName()); - final IdentifierNode.Segment id1s2 = list.get(1).get(2); + final IdentifierSegment id1s2 = list.get(1).get(2); assertEquals(1, id1s2.getKeyParts().size()); assertEquals("m0", id1s2.getKeyParts().get(0).getName()); assertEquals("boo", list.get(1).get(3).getName()); @@ -564,10 +563,10 @@ public void memberComplete() { public void segmentComplete( ParseRegion region, String name, - IdentifierNode.Quoting quoting, + Quoting quoting, Syntax syntax) { - if (quoting == IdentifierNode.Quoting.QUOTED) { + if (quoting == Quoting.QUOTED) { buf.append("[").append(name).append("]"); } else { buf.append(name); diff --git a/testsrc/org/olap4j/mdx/MdxTest.java b/testsrc/org/olap4j/mdx/MdxTest.java index bf3c0df..23e4864 100644 --- a/testsrc/org/olap4j/mdx/MdxTest.java +++ b/testsrc/org/olap4j/mdx/MdxTest.java @@ -41,43 +41,43 @@ public void testQuoteMdxIdentifier() { "[Store].[USA].[California]", IdentifierNode.unparseIdentifierList( Arrays.asList( - new IdentifierNode.NameSegment( - null, "Store", IdentifierNode.Quoting.QUOTED), - new IdentifierNode.NameSegment( - null, "USA", IdentifierNode.Quoting.QUOTED), - new IdentifierNode.NameSegment( - null, "California", IdentifierNode.Quoting.QUOTED)))); + new NameSegment( + null, "Store", Quoting.QUOTED), + new NameSegment( + null, "USA", Quoting.QUOTED), + new NameSegment( + null, "California", Quoting.QUOTED)))); } public void testImplode() { - List fooBar = - Arrays.asList( - new IdentifierNode.NameSegment( - null, "foo", IdentifierNode.Quoting.UNQUOTED), - new IdentifierNode.NameSegment( - null, "bar", IdentifierNode.Quoting.QUOTED)); + List fooBar = + Arrays.asList( + new NameSegment( + null, "foo", Quoting.UNQUOTED), + new NameSegment( + null, "bar", Quoting.QUOTED)); assertEquals( "foo.[bar]", IdentifierNode.unparseIdentifierList(fooBar)); - List empty = Collections.emptyList(); + List empty = Collections.emptyList(); assertEquals("", IdentifierNode.unparseIdentifierList(empty)); - List nasty = - Arrays.asList( - new IdentifierNode.NameSegment( - null, "string", IdentifierNode.Quoting.QUOTED), - new IdentifierNode.NameSegment( - null, "with", IdentifierNode.Quoting.QUOTED), - new IdentifierNode.NameSegment( - null, "a [bracket] in it", IdentifierNode.Quoting.QUOTED)); + List nasty = + Arrays.asList( + new NameSegment( + null, "string", Quoting.QUOTED), + new NameSegment( + null, "with", Quoting.QUOTED), + new NameSegment( + null, "a [bracket] in it", Quoting.QUOTED)); assertEquals( "[string].[with].[a [bracket]] in it]", IdentifierNode.unparseIdentifierList(nasty)); } public void testParseIdentifier() { - List segments = + List segments = IdentifierNode.parseIdentifier( "[string].[with].[a [bracket]] in it]").getSegmentList(); assertEquals(3, segments.size()); @@ -85,7 +85,7 @@ public void testParseIdentifier() { "a [bracket] in it", segments.get(2).getName()); assertEquals( - IdentifierNode.Quoting.QUOTED, + Quoting.QUOTED, segments.get(2).getQuoting()); segments = IdentifierNode.parseIdentifier( @@ -98,10 +98,10 @@ public void testParseIdentifier() { segments = IdentifierNode.parseIdentifier("[foo].bar").getSegmentList(); assertEquals(2, segments.size()); assertEquals( - IdentifierNode.Quoting.QUOTED, + Quoting.QUOTED, segments.get(0).getQuoting()); assertEquals( - IdentifierNode.Quoting.UNQUOTED, + Quoting.UNQUOTED, segments.get(1).getQuoting()); try { @@ -115,6 +115,43 @@ public void testParseIdentifier() { } } + /** + * Unit test for {@link org.olap4j.mdx.IdentifierNode#ofNames(String...)}. + */ + public void testIdentifierOfNames() { + IdentifierNode identifierNode = + IdentifierNode.ofNames( + "string", "with", "a [bracket] in it"); + List segments = + identifierNode.getSegmentList(); + assertEquals(3, segments.size()); + assertEquals( + "a [bracket] in it", + segments.get(2).getName()); + assertEquals( + Quoting.QUOTED, + segments.get(2).getQuoting()); + + assertEquals( + "xxx", + identifierNode.toString()); + + // Empty array is valid. (I don't feel strongly about this.) + identifierNode = + IdentifierNode.ofNames(); + assertEquals(0, identifierNode.getSegmentList().size()); + assertEquals("", identifierNode.toString()); + + // Array containing null is not valid. + try { + identifierNode = + IdentifierNode.ofNames("foo", null, "bar"); + fail("expected error, got " + identifierNode); + } catch (NullPointerException e) { + // ok + } + } + /** * Tests that escaped single quotes ('') nested inside a quoted * part of a query are handled correctly. The MDX language allows diff --git a/testsrc/org/olap4j/test/ParserTest.java b/testsrc/org/olap4j/test/ParserTest.java index 55d31ce..465da90 100644 --- a/testsrc/org/olap4j/test/ParserTest.java +++ b/testsrc/org/olap4j/test/ParserTest.java @@ -592,36 +592,36 @@ public void testIdWithKey() { assertNotNull(id.getRegion()); assertEquals(3, id.getSegmentList().size()); - final IdentifierNode.Segment seg0 = id.getSegmentList().get(0); + final IdentifierSegment seg0 = id.getSegmentList().get(0); assertNotNull(seg0.getRegion()); assertEquals("Foo", seg0.getName()); - assertEquals(IdentifierNode.Quoting.QUOTED, seg0.getQuoting()); + assertEquals(Quoting.QUOTED, seg0.getQuoting()); - final IdentifierNode.Segment seg1 = id.getSegmentList().get(1); - assertEquals(IdentifierNode.Quoting.KEY, seg1.getQuoting()); + final IdentifierSegment seg1 = id.getSegmentList().get(1); + assertEquals(Quoting.KEY, seg1.getQuoting()); assertNull(seg1.getName()); - List keyParts = seg1.getKeyParts(); + List keyParts = seg1.getKeyParts(); assertNotNull(keyParts); assertEquals(2, keyParts.size()); assertEquals("Key1", keyParts.get(0).getName()); assertEquals( - IdentifierNode.Quoting.UNQUOTED, keyParts.get(0).getQuoting()); + Quoting.UNQUOTED, keyParts.get(0).getQuoting()); assertEquals("Key2", keyParts.get(1).getName()); assertEquals( - IdentifierNode.Quoting.UNQUOTED, keyParts.get(1).getQuoting()); + Quoting.UNQUOTED, keyParts.get(1).getQuoting()); - final IdentifierNode.Segment seg2 = id.getSegmentList().get(2); + final IdentifierSegment seg2 = id.getSegmentList().get(2); assertNotNull(seg2.getRegion()); - assertEquals(IdentifierNode.Quoting.KEY, seg2.getQuoting()); - List keyParts2 = seg2.getKeyParts(); + assertEquals(Quoting.KEY, seg2.getQuoting()); + List keyParts2 = seg2.getKeyParts(); assertNotNull(keyParts2); assertEquals(3, keyParts2.size()); assertEquals( - IdentifierNode.Quoting.QUOTED, keyParts2.get(0).getQuoting()); + Quoting.QUOTED, keyParts2.get(0).getQuoting()); assertEquals( - IdentifierNode.Quoting.UNQUOTED, keyParts2.get(1).getQuoting()); + Quoting.UNQUOTED, keyParts2.get(1).getQuoting()); assertEquals( - IdentifierNode.Quoting.QUOTED, keyParts2.get(2).getQuoting()); + Quoting.QUOTED, keyParts2.get(2).getQuoting()); assertEquals("5", keyParts2.get(2).getName()); assertNotNull(keyParts2.get(2).getRegion()); @@ -759,20 +759,20 @@ public void testIdentifier() { } id = new IdentifierNode( - new IdentifierNode.NameSegment("foo")); + new NameSegment("foo")); assertEquals("[foo]", id.toString()); // append does not mutate IdentifierNode id2 = id.append( - new IdentifierNode.KeySegment( - new IdentifierNode.NameSegment( - null, "bar", IdentifierNode.Quoting.QUOTED))); + new KeySegment( + new NameSegment( + null, "bar", Quoting.QUOTED))); assertTrue(id != id2); assertEquals("[foo]", id.toString()); assertEquals("[foo].&[bar]", id2.toString()); // cannot mutate segment list - final List segments = id.getSegmentList(); + final List segments = id.getSegmentList(); try { segments.remove(0); fail("expected exception"); @@ -787,7 +787,7 @@ public void testIdentifier() { } try { segments.add( - new IdentifierNode.NameSegment("baz")); + new NameSegment("baz")); fail("expected exception"); } catch (UnsupportedOperationException e) { // ok @@ -848,16 +848,16 @@ public void testWithAdd() { SelectNode selectNode = new SelectNode(); IdentifierNode startDate = new IdentifierNode( - new IdentifierNode.NameSegment("Date"), - new IdentifierNode.NameSegment("2010-01-03")); + new NameSegment("Date"), + new NameSegment("2010-01-03")); IdentifierNode endDate = new IdentifierNode( - new IdentifierNode.NameSegment("Date"), - new IdentifierNode.NameSegment("2010-10-03")); + new NameSegment("Date"), + new NameSegment("2010-10-03")); IdentifierNode name = new IdentifierNode( - new IdentifierNode.NameSegment("Date"), - new IdentifierNode.NameSegment("Date Range")); + new NameSegment("Date"), + new NameSegment("Date Range")); CallNode cn = new CallNode(null, ":", Syntax.Infix, startDate, endDate); ParseTreeNode exp = new CallNode( diff --git a/testsrc/org/olap4j/test/TestContext.java b/testsrc/org/olap4j/test/TestContext.java index d378f00..6f0c1c0 100644 --- a/testsrc/org/olap4j/test/TestContext.java +++ b/testsrc/org/olap4j/test/TestContext.java @@ -16,8 +16,7 @@ import org.olap4j.*; import org.olap4j.impl.Olap4jUtil; -import org.olap4j.mdx.ParseTreeNode; -import org.olap4j.mdx.ParseTreeWriter; +import org.olap4j.mdx.*; import org.olap4j.layout.TraditionalCellSetFormatter; import org.apache.commons.dbcp.*; @@ -448,6 +447,16 @@ public static String getStackTrace(Throwable e) { return sw.toString(); } + /** + * Shorthand way to convert array of names into segment list. + * + * @param names Array of names + * @return Segment list + */ + public static List nameList(String... names) { + return IdentifierNode.ofNames(names).getSegmentList(); + } + /** * Checks that an exception is not null and the stack trace contains a * given string. Fails otherwise.