From 4b0a021257e6a2fb6d9634aebd345cee4b3c0118 Mon Sep 17 00:00:00 2001 From: Julian Hyde Date: Thu, 14 Oct 2010 21:24:51 +0000 Subject: [PATCH] Change IdentifierNode.parseIdentifier(String) to return an IdentifierNode; it previously returned List. Add example of adding a WITH MEMBER node to a parse tree model. Cut out spurious parentheses if child of parentheses node is going to defensively parenthesize itself. git-svn-id: https://olap4j.svn.sourceforge.net/svnroot/olap4j/trunk@359 c6a108a4-781c-0410-a6c6-c2d559e19af0 --- src/org/olap4j/mdx/IdentifierNode.java | 31 +++++++------ src/org/olap4j/mdx/Syntax.java | 21 ++++++--- src/org/olap4j/query/QueryDimension.java | 3 +- testsrc/org/olap4j/ConnectionTest.java | 9 ++-- testsrc/org/olap4j/impl/Olap4jUtilTest.java | 22 ++++----- testsrc/org/olap4j/mdx/MdxTest.java | 9 ++-- testsrc/org/olap4j/test/ParserTest.java | 51 ++++++++++++++++++++- 7 files changed, 102 insertions(+), 44 deletions(-) diff --git a/src/org/olap4j/mdx/IdentifierNode.java b/src/org/olap4j/mdx/IdentifierNode.java index b66a45e..05844a4 100644 --- a/src/org/olap4j/mdx/IdentifierNode.java +++ b/src/org/olap4j/mdx/IdentifierNode.java @@ -176,9 +176,11 @@ public IdentifierNode deepCopy() { } /** - * Parses an MDX identifier into a list of segments. + * Parses an MDX identifier string into an + * {@link org.olap4j.mdx.IdentifierNode}. * - *

Each segment is a name combined with a description of how the name + *

It contains a list of {@link IdentifierNode.Segment segments}, each + * of which is a name combined with a description of how the name * was {@link Quoting quoted}. For example, * *

@@ -186,27 +188,27 @@ public IdentifierNode deepCopy() { * "[Customers].USA.[South Dakota].[Sioux Falls].&[1245]") *
* - * returns + * 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) } - *
+ *
    + *
  • Segment("Customers", QUOTED), + *
  • Segment("USA", UNQUOTED), + *
  • Segment("South Dakota", QUOTED), + *
  • Segment("Sioux Falls", QUOTED), + *
  • Segment("1245", KEY) + *
* * @see org.olap4j.metadata.Cube#lookupMember(String...) * * @param identifier MDX identifier string * - * @return List of name segments + * @return Identifier parse tree node * * @throws IllegalArgumentException if the format of the identifier is * invalid */ - public static List parseIdentifier(String identifier) { - return IdentifierParser.parseIdentifier(identifier); + public static IdentifierNode parseIdentifier(String identifier) { + return new IdentifierNode(IdentifierParser.parseIdentifier(identifier)); } /** @@ -279,7 +281,8 @@ static String unparseIdentifierList(List segments) { * {@link org.olap4j.mdx.IdentifierNode.KeySegment KeySegment}. * *

To parse an identifier into a list of segments, use the method - * {@link IdentifierNode#parseIdentifier(String)}.

+ * {@link IdentifierNode#parseIdentifier(String)} and then call + * {@link IdentifierNode#getSegmentList()} on the resulting node.

*/ public interface Segment { diff --git a/src/org/olap4j/mdx/Syntax.java b/src/org/olap4j/mdx/Syntax.java index 60edb7d..800c51a 100644 --- a/src/org/olap4j/mdx/Syntax.java +++ b/src/org/olap4j/mdx/Syntax.java @@ -192,12 +192,21 @@ public void unparse( List argList, ParseTreeWriter writer) { - unparseList( - writer, - argList, - "(", - ", ", - ")"); + if (argList.size() == 1 + && argList.get(0) instanceof CallNode + && needParen(((CallNode) argList.get(0)).getArgList())) + { + // The parenthesized expression is going to defensively + // parenthesize itself. So, don't add another layer. + argList.get(0).unparse(writer); + } else { + unparseList( + writer, + argList, + "(", + ", ", + ")"); + } } }, diff --git a/src/org/olap4j/query/QueryDimension.java b/src/org/olap4j/query/QueryDimension.java index 3b52cd5..172dca6 100644 --- a/src/org/olap4j/query/QueryDimension.java +++ b/src/org/olap4j/query/QueryDimension.java @@ -10,6 +10,7 @@ package org.olap4j.query; 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; @@ -344,7 +345,7 @@ public void clearExclusions() { } public static String[] getNameParts(String sel) { - List list = IdentifierNode.parseIdentifier(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(); diff --git a/testsrc/org/olap4j/ConnectionTest.java b/testsrc/org/olap4j/ConnectionTest.java index 9f44d3d..fd1b391 100644 --- a/testsrc/org/olap4j/ConnectionTest.java +++ b/testsrc/org/olap4j/ConnectionTest.java @@ -2588,8 +2588,7 @@ private void buildQuery( SelectNode query = new SelectNode(); ParseTreeNode cubeNode; if (useCubeObject) { - cubeNode = new IdentifierNode( - IdentifierNode.parseIdentifier(cube.getUniqueName())); + cubeNode = IdentifierNode.parseIdentifier(cube.getUniqueName()); } else { cubeNode = new CubeNode(null, cube); } @@ -2599,15 +2598,13 @@ private void buildQuery( null, false, Axis.COLUMNS, null, new CallNode( null, "MEMBERS", Syntax.Property, - new IdentifierNode( - IdentifierNode.parseIdentifier("[Gender]")))); + IdentifierNode.parseIdentifier("[Gender]"))); AxisNode rowAxis = new AxisNode( null, false, Axis.ROWS, null, new CallNode( null, "CHILDREN", Syntax.Property, - new IdentifierNode( - IdentifierNode.parseIdentifier("[Customers].[USA]")))); + IdentifierNode.parseIdentifier("[Customers].[USA]"))); query.getAxisList().add(columnAxis); query.getAxisList().add(rowAxis); OlapStatement statement = olapConnection.createStatement(); diff --git a/testsrc/org/olap4j/impl/Olap4jUtilTest.java b/testsrc/org/olap4j/impl/Olap4jUtilTest.java index eed5892..72a57d1 100644 --- a/testsrc/org/olap4j/impl/Olap4jUtilTest.java +++ b/testsrc/org/olap4j/impl/Olap4jUtilTest.java @@ -228,7 +228,7 @@ public void testUnmodifiableArrayList() { assertEquals("y", copyList.get(1)); // test the of(Collection) method - final ArrayList arrayList = new ArrayList(); + final List arrayList = new ArrayList(); arrayList.add("foo"); arrayList.add("bar"); final UnmodifiableArrayList list3 = @@ -348,24 +348,24 @@ private void checkParseFormattedCellValue( */ public void testParseIdentifier() { List segments = - IdentifierNode.parseIdentifier( + IdentifierParser.parseIdentifier( "[string].[with].[a [bracket]] in it]"); assertEquals(3, segments.size()); assertEquals("a [bracket] in it", segments.get(2).getName()); segments = - IdentifierNode.parseIdentifier( + IdentifierParser.parseIdentifier( "[Worklog].[All].[calendar-[LANGUAGE]].js]"); assertEquals(3, segments.size()); assertEquals("calendar-[LANGUAGE].js", segments.get(2).getName()); // allow spaces before, after and between - segments = IdentifierNode.parseIdentifier(" [foo] . [bar].[baz] "); + segments = IdentifierParser.parseIdentifier(" [foo] . [bar].[baz] "); assertEquals(3, segments.size()); assertEquals("foo", segments.get(0).getName()); // first segment not quoted - segments = IdentifierNode.parseIdentifier("Time.1997.[Q3]"); + segments = IdentifierParser.parseIdentifier("Time.1997.[Q3]"); assertEquals(3, segments.size()); assertEquals("Time", segments.get(0).getName()); assertEquals("1997", segments.get(1).getName()); @@ -373,7 +373,7 @@ public void testParseIdentifier() { // spaces ignored after unquoted segment segments = - IdentifierNode.parseIdentifier("[Time . Weekly ] . 1997 . [Q3]"); + IdentifierParser.parseIdentifier("[Time . Weekly ] . 1997 . [Q3]"); assertEquals(3, segments.size()); assertEquals("Time . Weekly ", segments.get(0).getName()); assertEquals("1997", segments.get(1).getName()); @@ -381,7 +381,7 @@ public void testParseIdentifier() { // identifier ending in '.' is invalid try { - segments = IdentifierNode.parseIdentifier("[foo].[bar]."); + segments = IdentifierParser.parseIdentifier("[foo].[bar]."); fail("expected exception, got " + segments); } catch (IllegalArgumentException e) { assertEquals( @@ -391,7 +391,7 @@ public void testParseIdentifier() { } try { - segments = IdentifierNode.parseIdentifier("[foo].[bar"); + segments = IdentifierParser.parseIdentifier("[foo].[bar"); fail("expected exception, got " + segments); } catch (IllegalArgumentException e) { assertEquals( @@ -400,7 +400,7 @@ public void testParseIdentifier() { } try { - segments = IdentifierNode.parseIdentifier("[Foo].[Bar], [Baz]"); + segments = IdentifierParser.parseIdentifier("[Foo].[Bar], [Baz]"); fail("expected exception, got " + segments); } catch (IllegalArgumentException e) { assertEquals( @@ -409,7 +409,7 @@ public void testParseIdentifier() { } // test case for bug 3036629, "Patch 328 breaks test". - segments = IdentifierNode.parseIdentifier( + segments = IdentifierParser.parseIdentifier( "[ProductFilterDim].[Product Main Group Name].&[Maingroup (xyz)]"); assertEquals(3, segments.size()); final IdentifierNode.Segment s0 = segments.get(0); @@ -447,7 +447,7 @@ public void testParseIdentifierAdvanced() { // ** Sub-segment #2 is UNQUOTED, name "USA" // * Segment #3 is a KEY. It has 1 sub-segment: // ** Sub-segment #0 is QUOTED, name "cust1234" - segments = IdentifierNode.parseIdentifier( + segments = IdentifierParser.parseIdentifier( "[Customers].[City].&[San Francisco]&CA&USA.&[cust1234]"); assertEquals(4, segments.size()); final IdentifierNode.Segment s0 = segments.get(0); diff --git a/testsrc/org/olap4j/mdx/MdxTest.java b/testsrc/org/olap4j/mdx/MdxTest.java index cbe222e..bf3c0df 100644 --- a/testsrc/org/olap4j/mdx/MdxTest.java +++ b/testsrc/org/olap4j/mdx/MdxTest.java @@ -79,7 +79,7 @@ public void testImplode() { public void testParseIdentifier() { List segments = IdentifierNode.parseIdentifier( - "[string].[with].[a [bracket]] in it]"); + "[string].[with].[a [bracket]] in it]").getSegmentList(); assertEquals(3, segments.size()); assertEquals( "a [bracket] in it", @@ -89,13 +89,13 @@ public void testParseIdentifier() { segments.get(2).getQuoting()); segments = IdentifierNode.parseIdentifier( - "[Worklog].[All].[calendar-[LANGUAGE]].js]"); + "[Worklog].[All].[calendar-[LANGUAGE]].js]").getSegmentList(); assertEquals(3, segments.size()); assertEquals( "calendar-[LANGUAGE].js", segments.get(2).getName()); - segments = IdentifierNode.parseIdentifier("[foo].bar"); + segments = IdentifierNode.parseIdentifier("[foo].bar").getSegmentList(); assertEquals(2, segments.size()); assertEquals( IdentifierNode.Quoting.QUOTED, @@ -105,7 +105,8 @@ public void testParseIdentifier() { segments.get(1).getQuoting()); try { - segments = IdentifierNode.parseIdentifier("[foo].[bar"); + segments = + IdentifierNode.parseIdentifier("[foo].[bar").getSegmentList(); fail("expected exception, got " + segments); } catch (RuntimeException e) { assertEquals( diff --git a/testsrc/org/olap4j/test/ParserTest.java b/testsrc/org/olap4j/test/ParserTest.java index 1ff6664..55d31ce 100644 --- a/testsrc/org/olap4j/test/ParserTest.java +++ b/testsrc/org/olap4j/test/ParserTest.java @@ -19,6 +19,7 @@ import java.sql.SQLException; import java.sql.Connection; +import java.util.Collections; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -543,7 +544,7 @@ public void testIsNull() { assertParseExpr( "(x is null) + 56 > 6", - "((((x IS NULL)) + 56.0) > 6.0)"); + "(((x IS NULL) + 56.0) > 6.0)"); // FIXME: Should be: // "(((((x IS NULL) AND (a = b)) OR ((c = (d + 5.0))) IS NULL) + 5.0)" @@ -835,11 +836,57 @@ public void testInnerSelect() { "SELECT\n" + "FROM (\n" + " SELECT\n" - + " ({[ProductDim].[Product Group].&[Mobile Phones]}) ON COLUMNS\n" + + " {[ProductDim].[Product Group].&[Mobile Phones]} ON COLUMNS\n" + " FROM [cube])\n" + "CELL PROPERTIES VALUE"); } + /** + * Test case for adding to WITH clause. + */ + public void testWithAdd() { + SelectNode selectNode = new SelectNode(); + IdentifierNode startDate = + new IdentifierNode( + new IdentifierNode.NameSegment("Date"), + new IdentifierNode.NameSegment("2010-01-03")); + IdentifierNode endDate = + new IdentifierNode( + new IdentifierNode.NameSegment("Date"), + new IdentifierNode.NameSegment("2010-10-03")); + IdentifierNode name = + new IdentifierNode( + new IdentifierNode.NameSegment("Date"), + new IdentifierNode.NameSegment("Date Range")); + CallNode cn = new CallNode(null, ":", Syntax.Infix, startDate, endDate); + ParseTreeNode exp = + new CallNode( + null, + "Aggregate", + Syntax.Function, + new CallNode( + null, + "{}", + Syntax.Braces, + cn)); + WithMemberNode withMemberNode = + new WithMemberNode( + null, name, exp, Collections.emptyList()); + selectNode.setFrom( + IdentifierNode.parseIdentifier("Sales")); + selectNode.getWithList().add(withMemberNode); + final String queryString = selectNode.toString(); + TestContext.assertEqualsVerbose( + "WITH\n" + + "MEMBER [Date].[Date Range] AS\n" + + " Aggregate({([Date].[2010-01-03] : [Date].[2010-10-03])})\n" + + "SELECT\n" + + "FROM Sales", + queryString); + // check that unparsed string is valid + assertParseQuery(queryString, TestContext.unfold(queryString)); + } + /** * Parses an MDX query and asserts that the result is as expected when * unparsed.