diff --git a/src/org/olap4j/Axis.java b/src/org/olap4j/Axis.java
index 53e69b7..8a7526f 100644
--- a/src/org/olap4j/Axis.java
+++ b/src/org/olap4j/Axis.java
@@ -24,60 +24,155 @@
* @version $Id$
* @since Oct 23, 2006
*/
-public enum Axis {
- UNUSED,
- FILTER,
- COLUMNS,
- ROWS,
- PAGES,
- CHAPTERS,
- SECTIONS;
+public interface Axis {
/**
- * Returns the ordinal which is to be used for retrieving this axis from
- * the {@link org.olap4j.CellSet#getAxes()}, or retrieving its
- * coordinate from {@link Cell#getCoordinateList()}.
+ * Returns the name of this axis, e.g. "COLUMNS", "SLICER", "AXIS(17)".
*
- *
The axis ordinal is two less than the {@link #ordinal} value which
- * every enum
value possesses. Hence, {@link #UNUSED} is -2
- * and {@link #FILTER} is -1 (because they are not treated the same as the
- * other axes), {@link #COLUMNS} is 0, {@link #ROWS} is 1, and so forth.
- *
- * @return Axis ordinal
+ * @return Name of the axis
*/
- public int axisOrdinal() {
- return axisOrdinal;
- }
+ String name();
/**
- * Returns localized name for this Axis.
+ * Returns whether this is the filter (slicer) axis.
*
- * @param locale Locale for which to give the name
- * @return localized name for this Axis
+ * @return whether this is the filter axis
*/
- public String getCaption(Locale locale) {
- // todo: localize
- return name();
- }
+ boolean isFilter();
/**
- * Returns the axis with a given {@link #axisOrdinal()}.
- *
- * @param axisOrdinal Axis ordinal
- * @return Axis whose {@link #axisOrdinal()} is as given
+ * @deprecated Will be removed before olap4j 1.0.
*/
- public static Axis forOrdinal(int axisOrdinal) {
- Axis axis = values()[axisOrdinal + 2];
- assert axis.axisOrdinal() == axisOrdinal;
- return axis;
+ public static final Standard UNUSED = null;
+
+ /**
+ * @deprecated Will be removed before olap4j 1.0.
+ */
+ public static final Standard NONE = null;
+
+ /**
+ * Abbreviation for {@link org.olap4j.Axis.Standard#FILTER}.
+ */
+ public static final Standard FILTER = Standard.FILTER;
+ public static final Standard COLUMNS = Standard.COLUMNS;
+ public static final Standard ROWS = Standard.ROWS;
+ public static final Standard PAGES = Standard.PAGES;
+ public static final Standard SECTIONS = Standard.SECTIONS;
+ public static final Standard CHAPTERS = Standard.CHAPTERS;
+
+ /**
+ * Enumeration of standard, named axes descriptors.
+ */
+ public enum Standard implements Axis {
+ /** Filter axis. */
+ FILTER,
+
+ /** COLUMNS axis, also known as X axis and AXIS(0). */
+ COLUMNS,
+
+ /** ROWS axis, also known as Y axis and AXIS(1). */
+ ROWS,
+
+ /** PAGES axis, also known as AXIS(2). */
+ PAGES,
+
+ /** CHAPTERS axis, also known as AXIS(3). */
+ CHAPTERS,
+
+ /** SECTIONS axis, also known as AXIS(4). */
+ SECTIONS;
+
+ public int axisOrdinal() {
+ return ordinal() - 1;
+ }
+
+ public boolean isFilter() {
+ return this == FILTER;
+ }
+
+ public String getCaption(Locale locale) {
+ // TODO: localize
+ return name();
+ }
}
- private final int axisOrdinal = ordinal() - 2;
+ class Factory {
+ private static final Standard[] STANDARD_VALUES = Standard.values();
+
+ /**
+ * Returns the axis with a given {@code axisOrdinal}.
+ *
+ *
For example, {@code forOrdinal(0)} returns the COLUMNS axis;
+ * {@code forOrdinal(-1)} returns the SLICER axis;
+ * {@code forOrdinal(100)} returns AXIS(100).
+ *
+ * @param ordinal Axis ordinal
+ * @return Axis whose ordinal is as given
+ */
+ public static Axis forOrdinal(final int ordinal) {
+ if (ordinal < -1) {
+ throw new IllegalArgumentException(
+ "Axis ordinal must be -1 or higher");
+ }
+ if (ordinal + 1 < STANDARD_VALUES.length) {
+ return STANDARD_VALUES[ordinal + 1];
+ }
+ return new Axis() {
+ public String toString() {
+ return name();
+ }
+
+ public String name() {
+ return "AXIS(" + ordinal + ")";
+ }
+
+ public boolean isFilter() {
+ return false;
+ }
+
+ public int axisOrdinal() {
+ return ordinal;
+ }
+
+ public String getCaption(Locale locale) {
+ // TODO: localize
+ return name();
+ }
+ };
+ }
+ }
/**
- * The largest legal value for {@link #forOrdinal(int)}.
+ * Returns the ordinal which is to be used for retrieving this axis from
+ * the {@link org.olap4j.CellSet#getAxes()}, or retrieving its
+ * coordinate from {@link Cell#getCoordinateList()}.
+ *
+ *
For example:
+ *
+ * - -1 {@link org.olap4j.Axis.Standard#FILTER FILTER}
+ * - 0 {@link org.olap4j.Axis.Standard#COLUMNS COLUMNS}
+ * - 1 {@link org.olap4j.Axis.Standard#ROWS ROWS}
+ * - 2 {@link org.olap4j.Axis.Standard#PAGES PAGES}
+ * - 3 {@link org.olap4j.Axis.Standard#CHAPTERS CHAPTERS}
+ * - 4 {@link org.olap4j.Axis.Standard#SECTIONS SECTIONS}
+ * - 5 {@link org.olap4j.Axis.Standard#SECTIONS SECTIONS}
+ * - 6 AXES(6)
+ * - 123 AXES(123)
+ *
+ *
+ * @return ordinal of this axis
+ */
+ int axisOrdinal();
+
+ /**
+ * Returns localized name for this Axis.
+ *
+ * Examples: "FILTER", "ROWS", "COLUMNS", "AXIS(10)".
+ *
+ * @param locale Locale for which to give the name
+ * @return localized name for this Axis
*/
- public static final int MAX_ORDINAL = SECTIONS.axisOrdinal();
+ String getCaption(Locale locale);
}
// End Axis.java
diff --git a/src/org/olap4j/driver/xmla/XmlaOlap4jCellSet.java b/src/org/olap4j/driver/xmla/XmlaOlap4jCellSet.java
index 4123f80..44e2c07 100644
--- a/src/org/olap4j/driver/xmla/XmlaOlap4jCellSet.java
+++ b/src/org/olap4j/driver/xmla/XmlaOlap4jCellSet.java
@@ -13,8 +13,8 @@
import org.olap4j.impl.Olap4jUtil;
import static org.olap4j.driver.xmla.XmlaOlap4jUtil.*;
import org.olap4j.metadata.*;
+
import org.w3c.dom.*;
-import org.w3c.dom.ls.*;
import org.xml.sax.SAXException;
import java.io.*;
@@ -178,13 +178,10 @@ void populate() throws OlapException {
final Axis axis = lookupAxis(axisName);
final XmlaOlap4jCellSetAxis cellSetAxis =
new XmlaOlap4jCellSetAxis(this, axis);
- switch (axis) {
- case FILTER:
+ if (axis.isFilter()) {
filterAxis = cellSetAxis;
- break;
- default:
+ } else {
axisList.add(cellSetAxis);
- break;
}
final Element tuplesNode =
findChild(axisNode, MDDATASET_NS, "Tuples");
@@ -431,13 +428,10 @@ private XmlaOlap4jCellSetMetaData createMetaData(Element root)
axis,
hierarchyList,
propertyList);
- switch (axis) {
- case FILTER:
+ if (axis.isFilter()) {
filterAxisMetaData = axisMetaData;
- break;
- default:
+ } else {
axisMetaDataList.add(axisMetaData);
- break;
}
}
final Element cellInfo =
@@ -500,7 +494,7 @@ private Axis lookupAxis(String axisName) {
if (axisName.startsWith("Axis")) {
final Integer ordinal =
Integer.valueOf(axisName.substring("Axis".length()));
- return Axis.values()[Axis.COLUMNS.ordinal() + ordinal];
+ return Axis.Factory.forOrdinal(ordinal);
} else {
return Axis.FILTER;
}
diff --git a/src/org/olap4j/driver/xmla/XmlaOlap4jCellSetAxis.java b/src/org/olap4j/driver/xmla/XmlaOlap4jCellSetAxis.java
index 685f40b..61b7674 100644
--- a/src/org/olap4j/driver/xmla/XmlaOlap4jCellSetAxis.java
+++ b/src/org/olap4j/driver/xmla/XmlaOlap4jCellSetAxis.java
@@ -27,6 +27,12 @@ class XmlaOlap4jCellSetAxis implements CellSetAxis {
private final List immutablePositions =
Collections.unmodifiableList(positions);
+ /**
+ * Creates an XmlaOlap4jCellSetAxis.
+ *
+ * @param olap4jCellSet Cell set
+ * @param axis Axis identifier
+ */
public XmlaOlap4jCellSetAxis(
XmlaOlap4jCellSet olap4jCellSet,
Axis axis)
@@ -45,10 +51,9 @@ public CellSet getCellSet() {
public CellSetAxisMetaData getAxisMetaData() {
final CellSetMetaData cellSetMetaData = olap4jCellSet.getMetaData();
- switch (axis) {
- case FILTER:
+ if (axis.isFilter()) {
return cellSetMetaData.getFilterAxisMetaData();
- default:
+ } else {
return cellSetMetaData.getAxesMetaData().get(
axis.axisOrdinal());
}
diff --git a/src/org/olap4j/mdx/parser/impl/DefaultMdxParser.cup b/src/org/olap4j/mdx/parser/impl/DefaultMdxParser.cup
index 19af14b..ae342e9 100644
--- a/src/org/olap4j/mdx/parser/impl/DefaultMdxParser.cup
+++ b/src/org/olap4j/mdx/parser/impl/DefaultMdxParser.cup
@@ -155,11 +155,11 @@ parser code {:
null :
new AxisNode(
filter.getRegion(), false, Axis.FILTER,
- Collections.EMPTY_LIST, filter);
+ Collections.emptyList(), filter);
// sort axes by ordinal
Collections.sort(axisList, new Comparator() {
public int compare(AxisNode o1, AxisNode o2) {
- return o1.getAxis().ordinal() - o2.getAxis().ordinal();
+ return o1.getAxis().axisOrdinal() - o2.getAxis().axisOrdinal();
}
});
return new SelectNode(
@@ -277,6 +277,8 @@ terminal
CASE,
CAST,
CELL,
+ CHAPTERS,
+ COLUMNS,
DIMENSION,
ELSE,
EMPTY,
@@ -291,7 +293,10 @@ terminal
NULL,
ON,
OR,
+ PAGES,
PROPERTIES,
+ ROWS,
+ SECTIONS,
SELECT,
SET,
THEN,
@@ -360,8 +365,9 @@ non terminal IdentifierNode
cube_specification,
member_name,
set_name;
+non terminal Axis.Standard
+ axis_name;
non terminal String
- axis_name,
comp_op,
keyword;
non terminal IdentifierNode.Segment
@@ -1431,25 +1437,25 @@ set_name ::= compound_id ;
// ::= [NON EMPTY] [] ON
axis_specification ::=
non_empty_opt:b expression:s dim_props_opt:dp ON axis_name:a {:
- Axis axis = Axis.valueOf(a.toUpperCase());
ParseRegion region = createRegion(
bleft, bright, sleft, sright, dpleft, dpright, aleft, aright);
- RESULT = new AxisNode(region, b, axis, emptyList(dp), s);
+ RESULT = new AxisNode(region, b, a, emptyList(dp), s);
:}
| non_empty_opt:b expression:s dim_props_opt:dp ON axis_number:n {:
double d = n.doubleValue();
int index = (int)d;
- // Legal axis ordinals run from 0 (COLUMS) to 4 (SECTIONS),
- // inclusive.
- if (index < 0 || index != d || index > Axis.MAX_ORDINAL) {
+ // AxisOrdinal values go from -2 to 4 for standard axis, but higher
+ // ordinals are allowed. The negative values represent
+ // special cases, so are ignored.
+ if (index < 0 || index != d) {
throw new MdxParseException(
createRegion(nleft, nright),
- "Invalid axis specification. The axis number must be an integer between 0 and " +
- Axis.MAX_ORDINAL + ", but it was " + d + ".");
+ "Invalid axis specification. The axis number must be a " +
+ "non-negative integer, but it was " + d + ".");
}
- Axis axis = Axis.forOrdinal(index);
+ Axis axis = Axis.Factory.forOrdinal(index);
ParseRegion region = createRegion(
bleft, bright, sleft, sright, dpleft, dpright, nleft, nright);
RESULT = new AxisNode(region, b, axis, emptyList(dp), s);
@@ -1476,8 +1482,20 @@ dim_props_opt ::=
// | SECTIONS
// | AXIS()
axis_name ::=
- identifier:i {:
- RESULT = i.getName();
+ COLUMNS {:
+ RESULT = Axis.COLUMNS;
+ :}
+ | ROWS {:
+ RESULT = Axis.ROWS;
+ :}
+ | PAGES {:
+ RESULT = Axis.PAGES;
+ :}
+ | SECTIONS {:
+ RESULT = Axis.SECTIONS;
+ :}
+ | CHAPTERS {:
+ RESULT = Axis.CHAPTERS;
:}
;
diff --git a/src/org/olap4j/mdx/parser/impl/Scanner.java b/src/org/olap4j/mdx/parser/impl/Scanner.java
index 1412461..0066b98 100644
--- a/src/org/olap4j/mdx/parser/impl/Scanner.java
+++ b/src/org/olap4j/mdx/parser/impl/Scanner.java
@@ -238,9 +238,9 @@ private void initReswords() {
initResword(DefaultMdxParserSym.CASE ,"CASE");
initResword(DefaultMdxParserSym.CELL ,"CELL");
// initResword(DefaultMdxParserSym.CELL_ORDINAL ,"CELL_ORDINAL");
-// initResword(DefaultMdxParserSym.CHAPTERS ,"CHAPTERS");
+ initResword(DefaultMdxParserSym.CHAPTERS ,"CHAPTERS");
// initResword(DefaultMdxParserSym.CHILDREN ,"CHILDREN");
-// initResword(DefaultMdxParserSym.COLUMNS ,"COLUMNS");
+ initResword(DefaultMdxParserSym.COLUMNS ,"COLUMNS");
// initResword(DefaultMdxParserSym.DESC ,"DESC");
initResword(DefaultMdxParserSym.DIMENSION ,"DIMENSION");
initResword(DefaultMdxParserSym.ELSE ,"ELSE");
@@ -270,13 +270,13 @@ private void initReswords() {
initResword(DefaultMdxParserSym.NULL ,"NULL");
initResword(DefaultMdxParserSym.ON ,"ON");
initResword(DefaultMdxParserSym.OR ,"OR");
-// initResword(DefaultMdxParserSym.PAGES ,"PAGES");
+ initResword(DefaultMdxParserSym.PAGES ,"PAGES");
// initResword(DefaultMdxParserSym.PARENT ,"PARENT");
// initResword(DefaultMdxParserSym.PREVMEMBER ,"PREVMEMBER");
initResword(DefaultMdxParserSym.PROPERTIES ,"PROPERTIES");
// initResword(DefaultMdxParserSym.RECURSIVE ,"RECURSIVE");
-// initResword(DefaultMdxParserSym.ROWS ,"ROWS");
-// initResword(DefaultMdxParserSym.SECTIONS ,"SECTIONS");
+ initResword(DefaultMdxParserSym.ROWS ,"ROWS");
+ initResword(DefaultMdxParserSym.SECTIONS ,"SECTIONS");
initResword(DefaultMdxParserSym.SELECT ,"SELECT");
initResword(DefaultMdxParserSym.SET ,"SET");
// initResword(DefaultMdxParserSym.SOLVE_ORDER ,"SOLVE_ORDER");
diff --git a/testsrc/org/olap4j/ConnectionTest.java b/testsrc/org/olap4j/ConnectionTest.java
index 2cd8181..a0be2b0 100644
--- a/testsrc/org/olap4j/ConnectionTest.java
+++ b/testsrc/org/olap4j/ConnectionTest.java
@@ -707,7 +707,7 @@ private void checkCellSetMetaData(
: cellSetMetaData.getAxesMetaData())
{
++k;
- assertEquals(Axis.forOrdinal(k), axisMetaData.getAxisOrdinal());
+ assertEquals(Axis.Factory.forOrdinal(k), axisMetaData.getAxisOrdinal());
assertEquals(k, axisMetaData.getAxisOrdinal().axisOrdinal());
assertTrue(axisMetaData.getHierarchies().size() > 0);
for (Hierarchy hierarchy : axisMetaData.getHierarchies()) {
diff --git a/testsrc/org/olap4j/OlapTest.java b/testsrc/org/olap4j/OlapTest.java
index 4fc0d64..16da5ac 100644
--- a/testsrc/org/olap4j/OlapTest.java
+++ b/testsrc/org/olap4j/OlapTest.java
@@ -672,7 +672,7 @@ public static void axisToXml(
Element dimensionsNode = doc.createElement("dimensions");
root.appendChild(dimensionsNode);
- switch (axis.getLocation()) {
+ switch ((Axis.Standard) axis.getLocation()) {
case COLUMNS:
addAttribute("location", "across", root);
break;
diff --git a/testsrc/org/olap4j/test/ParserTest.java b/testsrc/org/olap4j/test/ParserTest.java
index a75e78c..46bead9 100644
--- a/testsrc/org/olap4j/test/ParserTest.java
+++ b/testsrc/org/olap4j/test/ParserTest.java
@@ -138,7 +138,11 @@ public void testNegativeCases() throws Exception {
assertParseQueryFails(
"select [member] on ^axis(1.7)^ from sales",
- "(?s).*The axis number must be an integer.*");
+ "(?s).*The axis number must be a non-negative integer, but it was 1.7.");
+
+ assertParseQueryFails(
+ "select [member] on ^foobar^ from sales",
+ "Syntax error at \\[1:20, 1:25\\], token 'foobar'");
assertParseQueryFails(
"select [member] on axis(-^ ^1) from sales",
@@ -148,21 +152,28 @@ public void testNegativeCases() throws Exception {
"select [member] on axis(-^1^) from sales",
"Syntax error at \\[1:26\\], token '-'");
- assertParseQueryFails(
- "select [member] on ^axis(5)^ from sales",
- "Invalid axis specification\\. The axis number must be an integer between 0 and 4, but it was 5\\.0\\.");
+ // used to be an error, but no longer
+ assertParseQuery(
+ "select [member] on axis(5) from sales",
+ TestContext.fold(
+ "SELECT\n" +
+ "[member] ON AXIS(5)\n" +
+ "FROM sales"));
assertParseQueryFails(
- "select [member] on axes(^0^) from sales",
- "Syntax error at \\[1:25\\], token '\\('");
+ "select [member] on ^axes^(0) from sales",
+ "Syntax error at \\[1:20, 1:23\\], token 'axes'");
assertParseQueryFails(
"select [member] on ^0.5^ from sales",
- "Invalid axis specification\\. The axis number must be an integer between 0 and 4, but it was 0\\.5\\.");
+ "Invalid axis specification\\. The axis number must be a non-negative integer, but it was 0\\.5\\.");
- assertParseQueryFails(
- "select [member] on ^555^ from sales",
- "Invalid axis specification\\. The axis number must be an integer between 0 and 4, but it was 555\\.0\\.");
+ assertParseQuery(
+ "select [member] on 555 from sales",
+ TestContext.fold(
+ "SELECT\n" +
+ "[member] ON AXIS(555)\n" +
+ "FROM sales"));
}
public void testScannerPunc() {
@@ -227,7 +238,7 @@ private void checkFails(MdxParser p, String query, String expected) {
final ParseRegion.RegionAndSource ras = ParseRegion.findPos(query);
try {
SelectNode selectNode = p.parseSelect(ras.source);
- fail("Must return an error");
+ fail("Must return an error, got " + selectNode);
} catch (Exception e) {
checkEx(e, expected, ras);
}
@@ -403,10 +414,14 @@ public void testMultipleAxes() throws Exception {
List axes = select.getAxisList();
assertEquals("Number of axes", 2, axes.size());
- assertEquals("Axis index name must be correct",
- Axis.forOrdinal(0), axes.get(0).getAxis());
- assertEquals("Axis index name must be correct",
- Axis.forOrdinal(1), axes.get(1).getAxis());
+ assertEquals(
+ "Axis index name must be correct",
+ Axis.Factory.forOrdinal(0),
+ axes.get(0).getAxis());
+ assertEquals(
+ "Axis index name must be correct",
+ Axis.Factory.forOrdinal(1),
+ axes.get(1).getAxis());
// now a similar query with axes reversed
@@ -417,10 +432,14 @@ public void testMultipleAxes() throws Exception {
axes = select.getAxisList();
assertEquals("Number of axes", 2, axes.size());
- assertEquals("Axis index name must be correct",
- Axis.forOrdinal(0), axes.get(0).getAxis());
- assertEquals("Axis index name must be correct",
- Axis.forOrdinal(1), axes.get(1).getAxis());
+ assertEquals(
+ "Axis index name must be correct",
+ Axis.Factory.forOrdinal(0),
+ axes.get(0).getAxis());
+ assertEquals(
+ "Axis index name must be correct",
+ Axis.Factory.forOrdinal(1),
+ axes.get(1).getAxis());
ParseTreeNode colsSetExpr = axes.get(0).getExpression();
assertNotNull("Column tuples", colsSetExpr);