From ecad30cfaa63ebc1a0c511846207c4efc8b0ae29 Mon Sep 17 00:00:00 2001 From: Julian Hyde Date: Thu, 17 Apr 2008 07:41:10 +0000 Subject: [PATCH] Ensure that members of the Measures dimension implement Measure interface, and that list inclues calculated members defined against the cube. git-svn-id: https://olap4j.svn.sourceforge.net/svnroot/olap4j/trunk@92 c6a108a4-781c-0410-a6c6-c2d559e19af0 --- .../olap4j/driver/xmla/XmlaOlap4jCatalog.java | 2 + .../olap4j/driver/xmla/XmlaOlap4jCellSet.java | 42 ++++++------ .../driver/xmla/XmlaOlap4jConnection.java | 18 ++++-- .../olap4j/driver/xmla/XmlaOlap4jCube.java | 26 +++++++- .../driver/xmla/XmlaOlap4jDimension.java | 2 +- .../olap4j/driver/xmla/XmlaOlap4jMeasure.java | 9 ++- testsrc/org/olap4j/ConnectionTest.java | 64 ++++++++++++++++++- 7 files changed, 133 insertions(+), 30 deletions(-) diff --git a/src/org/olap4j/driver/xmla/XmlaOlap4jCatalog.java b/src/org/olap4j/driver/xmla/XmlaOlap4jCatalog.java index c426992..9420fd6 100644 --- a/src/org/olap4j/driver/xmla/XmlaOlap4jCatalog.java +++ b/src/org/olap4j/driver/xmla/XmlaOlap4jCatalog.java @@ -31,6 +31,8 @@ class XmlaOlap4jCatalog implements Catalog, Named { XmlaOlap4jDatabaseMetaData olap4jDatabaseMetaData, String name) { + assert olap4jDatabaseMetaData != null; + assert name != null; this.olap4jDatabaseMetaData = olap4jDatabaseMetaData; this.name = name; this.schemas = diff --git a/src/org/olap4j/driver/xmla/XmlaOlap4jCellSet.java b/src/org/olap4j/driver/xmla/XmlaOlap4jCellSet.java index e8dbbb6..ef4c286 100644 --- a/src/org/olap4j/driver/xmla/XmlaOlap4jCellSet.java +++ b/src/org/olap4j/driver/xmla/XmlaOlap4jCellSet.java @@ -305,37 +305,43 @@ void populate() throws OlapException { * are supported, but no dates are yet supported. Those not supported * fall back to Strings. * - *

If any exception is encountered, it returns null. - * * @param cell The cell of which we want the casted object. * @return The object with a correct value. - * @throws OlapException gets thrown if any error is encountered while casting the cell value. + * @throws OlapException if any error is encountered while casting the cell + * value */ private Object getTypedValue(Element cell) throws OlapException { - try { - Element elm = findChild(cell, MDDATASET_NS, "Value"); + Element elm = findChild(cell, MDDATASET_NS, "Value"); + if (elm == null) { + // Cell is null. + return null; + } - // The object type is contained in xsi:type attribute. - String type = elm.getAttribute("xsi:type"); - if (type.equals( "xsd:int")) { - return XmlaOlap4jUtil.intElement(cell, "Value"); + // The object type is contained in xsi:type attribute. + String type = elm.getAttribute("xsi:type"); + try { + if (type.equals( "xsd:int")) { + return XmlaOlap4jUtil.intElement(cell, "Value"); } else if (type.equals( "xsd:integer")) { - return XmlaOlap4jUtil.integerElement(cell, "Value"); + return XmlaOlap4jUtil.integerElement(cell, "Value"); } else if (type.equals( "xsd:double")) { - return XmlaOlap4jUtil.doubleElement(cell, "Value"); + return XmlaOlap4jUtil.doubleElement(cell, "Value"); } else if (type.equals( "xsd:float")) { - return XmlaOlap4jUtil.floatElement(cell, "Value"); + return XmlaOlap4jUtil.floatElement(cell, "Value"); } else if (type.equals( "xsd:long")) { - return XmlaOlap4jUtil.longElement(cell, "Value"); + return XmlaOlap4jUtil.longElement(cell, "Value"); } else if (type.equals( "xsd:boolean")) { - return XmlaOlap4jUtil.booleanElement(cell, "Value"); + return XmlaOlap4jUtil.booleanElement(cell, "Value"); } else { - return XmlaOlap4jUtil.stringElement(cell, "Value"); + return XmlaOlap4jUtil.stringElement(cell, "Value"); } } catch (Exception e) { - throw new OlapException("En exception was encountered while casting a cell value to it's correct data type.", e); //$NON-NLS-1$ - } - } + throw new OlapException( + "Error while casting a cell value to the correct java type for" + + " its XSD type " + type, + e); + } + } /** * Creates metadata for a cell set, given the DOM of the XMLA result. diff --git a/src/org/olap4j/driver/xmla/XmlaOlap4jConnection.java b/src/org/olap4j/driver/xmla/XmlaOlap4jConnection.java index 70381e3..7716ff0 100644 --- a/src/org/olap4j/driver/xmla/XmlaOlap4jConnection.java +++ b/src/org/olap4j/driver/xmla/XmlaOlap4jConnection.java @@ -256,7 +256,9 @@ String getDataSourceInfo() throws OlapException { throw new OlapException("Datasource name not found.", e); } finally { try { - rSet.close(); + if (rSet != null) { + rSet.close(); + } } catch (SQLException e) { // ignore } @@ -985,11 +987,14 @@ public void handle(Element row, Context context, List list) Olap4jUtil.discard(measureMembers); } Member member = - context.getCube(row).getMetadataReader().lookupMemberByUniqueName( - measureUniqueName); - int ordinal = -1; + context.getCube(row).getMetadataReader() + .lookupMemberByUniqueName( + measureUniqueName); + final int ordinal; if (member != null) { ordinal = member.getOrdinal(); + } else { + ordinal = -1; } list.add( @@ -1055,6 +1060,11 @@ public void handle(Element row, Context context, List list) { stringElement(row, "MEMBER_CAPTION"); int childrenCardinality = integerElement(row, "CHILDREN_CARDINALITY"); + // If this member is a measure, we want to return an object that + // implements the Measure interface to all API calls. But we also + // need to retrieve the properties that occur in MDSCHEMA_MEMBERS + // that are not available in MDSCHEMA_MEASURES, so we create a + // member for internal use. list.add( new XmlaOlap4jMember( context.getLevel(row), memberUniqueName, memberName, diff --git a/src/org/olap4j/driver/xmla/XmlaOlap4jCube.java b/src/org/olap4j/driver/xmla/XmlaOlap4jCube.java index 57f7afd..b378acf 100644 --- a/src/org/olap4j/driver/xmla/XmlaOlap4jCube.java +++ b/src/org/olap4j/driver/xmla/XmlaOlap4jCube.java @@ -59,9 +59,7 @@ class XmlaOlap4jCube implements Cube, Named this.description = description; this.metadataReader = new CachingMetadataReader( - new RawMetadataReader() - ) - ; + new RawMetadataReader()); final XmlaOlap4jConnection olap4jConnection = olap4jSchema.olap4jCatalog.olap4jDatabaseMetaData.olap4jConnection; @@ -115,6 +113,13 @@ class XmlaOlap4jCube implements Cube, Named XmlaOlap4jConnection.MetadataRequest.MDSCHEMA_MEASURES, new XmlaOlap4jConnection.MeasureHandler(), restrictions); + // replace temporary member versions of measures in cache with final + // measures + for (XmlaOlap4jMeasure measure : measures) { + ((CachingMetadataReader) metadataReader).memberMap.put( + measure.getUniqueName(), + new SoftReference(measure)); + } // populate named sets olap4jConnection.populateList( namedSets, context, @@ -436,6 +441,21 @@ public List getLevelMembers( final XmlaOlap4jConnection.Context context = new XmlaOlap4jConnection.Context(level); List list = new ArrayList(); + // If this is a level in the [Measures] dimension, we want to + // return objects that implement the Measure interface. During + // bootstrap, the list will be empty, and we need to return the + // regular Member objects which have the extra properties that are + // returned by MSCHEMA_MEMBERS but not MDSCHEMA_MEASURES. + switch (level.getDimension().getDimensionType()) { + case MEASURE: + if (!level.olap4jHierarchy.olap4jDimension.olap4jCube.measures + .isEmpty()) { + return Olap4jUtil.cast( + level.olap4jHierarchy.olap4jDimension.olap4jCube + .measures); + } + break; + } olap4jSchema.olap4jCatalog.olap4jDatabaseMetaData.olap4jConnection .populateList( list, diff --git a/src/org/olap4j/driver/xmla/XmlaOlap4jDimension.java b/src/org/olap4j/driver/xmla/XmlaOlap4jDimension.java index d59f8df..85288b5 100644 --- a/src/org/olap4j/driver/xmla/XmlaOlap4jDimension.java +++ b/src/org/olap4j/driver/xmla/XmlaOlap4jDimension.java @@ -25,7 +25,7 @@ class XmlaOlap4jDimension implements Dimension, Named { final XmlaOlap4jCube olap4jCube; - private final Type type; + final Type type; final NamedList hierarchies = new NamedListImpl(); private final String defaultHierarchyUniqueName; diff --git a/src/org/olap4j/driver/xmla/XmlaOlap4jMeasure.java b/src/org/olap4j/driver/xmla/XmlaOlap4jMeasure.java index 9a01448..6466449 100644 --- a/src/org/olap4j/driver/xmla/XmlaOlap4jMeasure.java +++ b/src/org/olap4j/driver/xmla/XmlaOlap4jMeasure.java @@ -9,8 +9,7 @@ package org.olap4j.driver.xmla; import org.olap4j.impl.Named; -import org.olap4j.metadata.Datatype; -import org.olap4j.metadata.Measure; +import org.olap4j.metadata.*; /** * Implementation of {@link org.olap4j.metadata.Measure} @@ -42,7 +41,11 @@ class XmlaOlap4jMeasure { super( olap4jLevel, uniqueName, name, caption, description, - parentMemberUniqueName, Type.MEASURE, 0, ordinal); + parentMemberUniqueName, + aggregator == Aggregator.CALCULATED ? Type.FORMULA : Type.MEASURE, + 0, ordinal); + assert olap4jLevel.olap4jHierarchy.olap4jDimension.type + == Dimension.Type.MEASURE; this.aggregator = aggregator; this.datatype = datatype; this.visible = visible; diff --git a/testsrc/org/olap4j/ConnectionTest.java b/testsrc/org/olap4j/ConnectionTest.java index 40a2268..a421b73 100644 --- a/testsrc/org/olap4j/ConnectionTest.java +++ b/testsrc/org/olap4j/ConnectionTest.java @@ -129,6 +129,11 @@ void assertIsValid(Connection connection, int timeout) { } else { throw new RuntimeException(e); } + } catch (AbstractMethodError e) { + // This happens in commons-dbcp. Somehow the method exists in + // the connection class, but it fails later. Not the fault of + // olap4j or the olapj driver, so ignore the error. + Olap4jUtil.discard(e); } } @@ -204,7 +209,7 @@ public void testConnection() throws ClassNotFoundException, SQLException { case DBCP: // DBCP complains if you close a connection twice. Even though the // JDBC spec is clear that it is OK. - break; + break; default: connection.close(); break; @@ -348,6 +353,32 @@ public void testStatement() throws SQLException { connection.close(); } + public void testAxes() throws SQLException { + connection = tester.createConnection(); + Statement statement = connection.createStatement(); + + OlapStatement olapStatement = + tester.getWrapper().unwrap(statement, OlapStatement.class); + + CellSet cellSet = + olapStatement.executeOlapQuery( + "SELECT {[Measures].[Unit Sales]} on 0,\n" + + "{[Store].Children} on 1\n" + + "FROM [Sales]"); + List axesList = cellSet.getAxes(); + assertEquals(2, axesList.size()); + final Member rowsMember = + axesList.get(0).getPositions().get(0).getMembers().get(0); + assertTrue( + rowsMember.getUniqueName(), + rowsMember instanceof Measure); + final Member columnsMember = + axesList.get(1).getPositions().get(0).getMembers().get(0); + assertTrue( + columnsMember.getUniqueName(), + !(columnsMember instanceof Measure)); + } + public void testInvalidStatement() throws SQLException { connection = tester.createConnection(); Statement statement = connection.createStatement(); @@ -1313,6 +1344,9 @@ public void testMetadata() throws Exception { for (Member rootMember : rootMemberList) { assertNull(rootMember.getParentMember()); } + assertEquals( + rootMemberList, + hierarchy.getLevels().get(0).getMembers()); assertNotNull(hierarchy.getDefaultMember()); assertNotNull(hierarchy.getName()); assertNotNull(hierarchy.getUniqueName()); @@ -1328,6 +1362,10 @@ public void testMetadata() throws Exception { for (Member member : level.getMembers()) { assertNotNull(member.getName()); assertEquals(level, member.getLevel()); + if (dimension.getDimensionType() + == Dimension.Type.MEASURE) { + assertTrue(member instanceof Measure); + } if (++k > 3) { break; } @@ -1422,6 +1460,7 @@ public void testMetadata() throws Exception { // Measures int k = -1; + Set measureNameSet = new HashSet(); for (Measure measure : cube.getMeasures()) { ++k; // The first measure is [Unit Sales], because the list must be @@ -1429,10 +1468,33 @@ public void testMetadata() throws Exception { if (k == 0) { assertEquals("Unit Sales", measure.getName()); } + if (measure.getName().equals("Profit Growth") + || measure.getName().equals("Profit last Period") + || measure.getName().equals("Profit")) { + assertEquals(Member.Type.FORMULA, measure.getMemberType()); + assertTrue(measure.isCalculated()); + } else { + assertEquals(Member.Type.MEASURE, measure.getMemberType()); + assertFalse(measure.isCalculated()); + } assertNotNull(measure.getName()); assertNotNull(measure.getAggregator()); assertTrue(measure.getDatatype() != null); + measureNameSet.add(measure.getName()); } + assertEquals( + new HashSet( + Arrays.asList( + "Unit Sales", + "Customer Count", + "Profit last Period", + "Profit", + "Profit Growth", + "Promotion Sales", + "Sales Count", + "Store Sales", + "Store Cost")), + measureNameSet); } /**