From 1728c118a9c7173308a586e64529dc05c1d47aa6 Mon Sep 17 00:00:00 2001 From: mkambol Date: Tue, 18 Jun 2013 17:03:46 -0400 Subject: [PATCH] [MONDRIAN-1581] XmlaOlap4jCellSet was not handling certain datatypes correctly (e.g. xsd:decimal). --- .../olap4j/driver/xmla/XmlaOlap4jCellSet.java | 91 ++- .../olap4j/driver/xmla/XmlaOlap4jUtil.java | 20 + .../driver/xmla/XmlaOlap4jCellSetTest.java | 698 ++++++++++++++++++ 3 files changed, 792 insertions(+), 17 deletions(-) create mode 100644 testsrc/org/olap4j/driver/xmla/XmlaOlap4jCellSetTest.java diff --git a/src/org/olap4j/driver/xmla/XmlaOlap4jCellSet.java b/src/org/olap4j/driver/xmla/XmlaOlap4jCellSet.java index 4842630..4cd39c8 100644 --- a/src/org/olap4j/driver/xmla/XmlaOlap4jCellSet.java +++ b/src/org/olap4j/driver/xmla/XmlaOlap4jCellSet.java @@ -19,7 +19,7 @@ import org.olap4j.*; import org.olap4j.impl.Olap4jUtil; -import org.olap4j.mdx.ParseTreeNode; +import org.olap4j.mdx.*; import org.olap4j.metadata.*; import org.w3c.dom.Document; @@ -46,6 +46,44 @@ * @since May 24, 2007 */ abstract class XmlaOlap4jCellSet implements CellSet { + private static final String VALUE_TAG = "Value"; + enum XsdTypes { + XSD_INT("xsd:int"), + XSD_INTEGER("xsd:integer"), + XSD_DOUBLE("xsd:double"), + XSD_POSITIVEINTEGER("xsd:positiveInteger"), + XSD_DECIMAL("xsd:decimal"), + XSD_SHORT("xsd:short"), + XSD_FLOAT("xsd:float"), + XSD_LONG("xsd:long"), + XSD_BOOLEAN("xsd:boolean"), + XSD_BYTE("xsd:byte"), + XSD_UNSIGNEDBYTE("xsd:unsignedByte"), + XSD_UNSIGNEDSHORT("xsd:unsignedShort"), + XSD_UNSIGNEDLONG("xsd:unsignedLong"), + XSD_UNSIGNEDINT("xsd:unsignedInt"), + XSD_STRING("xsd:string"); + + private final String name; + private static final Map TYPES; + static { + Map typesInitial = new HashMap(); + for (XsdTypes type : values()) { + typesInitial.put(type.name, type); + } + TYPES = Collections.unmodifiableMap(typesInitial); + } + + XsdTypes(String name) { + this.name = name; + } + + public static XsdTypes fromString(String name) { + XsdTypes type = TYPES.get(name); + return type == null ? XSD_STRING : type; + } + } + final XmlaOlap4jStatement olap4jStatement; protected boolean closed; private XmlaOlap4jCellSetMetaData metaData; @@ -338,7 +376,7 @@ void populate() throws OlapException { * RELAX * NG, Chapter 19 for a full list of possible data types. * - *

This method does not currently support all types; must numeric types + *

This method does not currently support all types; most numeric types * are supported, but no dates are yet supported. Those not supported * fall back to Strings. * @@ -347,7 +385,7 @@ void populate() throws OlapException { * @throws OlapException if any error is encountered while casting the cell * value */ - private Object getTypedValue(Element cell) throws OlapException { + private Object getTypedValue(Element cell) throws OlapException { Element elm = findChild(cell, MDDATASET_NS, "Value"); if (elm == null) { // Cell is null. @@ -356,21 +394,40 @@ private Object getTypedValue(Element cell) throws OlapException { // The object type is contained in xsi:type attribute. String type = elm.getAttribute("xsi:type"); + XsdTypes xsdType = XsdTypes.fromString(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"); - } else if (type.equals("xsd:double")) { - return XmlaOlap4jUtil.doubleElement(cell, "Value"); - } else if (type.equals("xsd:float")) { - return XmlaOlap4jUtil.floatElement(cell, "Value"); - } else if (type.equals("xsd:long")) { - return XmlaOlap4jUtil.longElement(cell, "Value"); - } else if (type.equals("xsd:boolean")) { - return XmlaOlap4jUtil.booleanElement(cell, "Value"); - } else { - return XmlaOlap4jUtil.stringElement(cell, "Value"); + switch (xsdType) { + case XSD_BOOLEAN: + return XmlaOlap4jUtil.booleanElement(cell, VALUE_TAG); + case XSD_INT: + return XmlaOlap4jUtil.intElement(cell, VALUE_TAG); + case XSD_INTEGER: + return XmlaOlap4jUtil.bigIntegerElement(cell, VALUE_TAG); + case XSD_DOUBLE: + return XmlaOlap4jUtil.doubleElement(cell, VALUE_TAG); + case XSD_POSITIVEINTEGER: + return XmlaOlap4jUtil.bigIntegerElement(cell, VALUE_TAG); + case XSD_DECIMAL: + return XmlaOlap4jUtil.bigDecimalElement(cell, VALUE_TAG); + case XSD_SHORT: + return XmlaOlap4jUtil.shortElement(cell, VALUE_TAG); + case XSD_FLOAT: + return XmlaOlap4jUtil.floatElement(cell, VALUE_TAG); + case XSD_LONG: + return XmlaOlap4jUtil.longElement(cell, VALUE_TAG); + case XSD_BYTE: + return XmlaOlap4jUtil.byteElement(cell, VALUE_TAG); + case XSD_UNSIGNEDBYTE: + return XmlaOlap4jUtil.shortElement(cell, VALUE_TAG); + case XSD_UNSIGNEDSHORT: + return XmlaOlap4jUtil.intElement(cell, VALUE_TAG); + case XSD_UNSIGNEDLONG: + return XmlaOlap4jUtil.bigDecimalElement(cell, VALUE_TAG); + case XSD_UNSIGNEDINT: + return XmlaOlap4jUtil.longElement(cell, VALUE_TAG); + default: + return XmlaOlap4jUtil.stringElement(cell, VALUE_TAG); } } catch (Exception e) { throw getHelper().createException( diff --git a/src/org/olap4j/driver/xmla/XmlaOlap4jUtil.java b/src/org/olap4j/driver/xmla/XmlaOlap4jUtil.java index e131f7e..b5999eb 100644 --- a/src/org/olap4j/driver/xmla/XmlaOlap4jUtil.java +++ b/src/org/olap4j/driver/xmla/XmlaOlap4jUtil.java @@ -26,6 +26,7 @@ import org.xml.sax.*; import java.io.*; +import java.math.*; import java.util.*; /** @@ -292,6 +293,15 @@ static Integer integerElement(Element row, String name) { } } + static byte byteElement(Element row, String name) { + return Byte.valueOf(stringElement(row, name)).byteValue(); + } + + static short shortElement(Element row, String name) { + return Short.valueOf(stringElement(row, name)); + } + + static int intElement(Element row, String name) { return integerElement(row, name).intValue(); } @@ -300,6 +310,10 @@ static Double doubleElement(Element row, String name) { return Double.valueOf(stringElement(row, name)); } + static BigDecimal bigDecimalElement(Element row, String name) { + return new BigDecimal(stringElement(row, name)); + } + static boolean booleanElement(Element row, String name) { return "true".equals(stringElement(row, name)); } @@ -312,6 +326,10 @@ static long longElement(Element row, String name) { return Long.valueOf(stringElement(row, name)).longValue(); } + static Object bigIntegerElement(Element row, String name) { + return new BigInteger(stringElement(row, name)); + } + static List childElements(Element memberNode) { final List list = new ArrayList(); final NodeList childNodes = memberNode.getChildNodes(); @@ -405,6 +423,8 @@ public static String toString(Node node, boolean prettyPrint) { } } + + /** * Error handler plus helper methods. */ diff --git a/testsrc/org/olap4j/driver/xmla/XmlaOlap4jCellSetTest.java b/testsrc/org/olap4j/driver/xmla/XmlaOlap4jCellSetTest.java new file mode 100644 index 0000000..57dfb81 --- /dev/null +++ b/testsrc/org/olap4j/driver/xmla/XmlaOlap4jCellSetTest.java @@ -0,0 +1,698 @@ +/* +// Licensed to Julian Hyde under one or more contributor license +// agreements. See the NOTICE file distributed with this work for +// additional information regarding copyright ownership. +// +// Julian Hyde licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + */ +package org.olap4j.driver.xmla; + +import org.olap4j.*; +import org.olap4j.test.TestContext; + +import junit.framework.TestCase; + +import java.io.*; +import java.lang.reflect.InvocationTargetException; +import java.math.*; +import java.sql.*; +import java.util.*; + + +/** + * Test class for XmlaOlap4jCellSet + * + */ +public class XmlaOlap4jCellSetTest extends TestCase { + + public void testTypedValues() + throws SQLException, ClassNotFoundException, + NoSuchMethodException, InvocationTargetException, + InstantiationException, IllegalAccessException + { + Map valueTags = new HashMap(); + + valueTags.put( + "true", + Boolean.class); + valueTags.put( + "39431.6712", + Double.class); + valueTags.put( + "3943", + Integer.class); + valueTags.put( + "39431", + BigInteger.class); + valueTags.put( + "39431", + BigInteger.class); + valueTags.put( + "39431.6712", + BigDecimal.class); + valueTags.put( + "3943", + Short.class); + valueTags.put( + "39431.6712", + Float.class); + valueTags.put( + "39431", + Long.class); + valueTags.put( + "31", + Byte.class); + valueTags.put( + "3943", + Short.class); + valueTags.put( + "39431", + Integer.class); + valueTags.put( + "39431.6712", + BigDecimal.class); + valueTags.put( + "39431", + Long.class); + valueTags.put( + "39431", + String.class); + valueTags.put( + "Unknown", + String.class); + + MockOlap4jStatement statement = new MockOlap4jStatement( + (XmlaOlap4jConnection) new XmlaTester(TestContext.instance()) + .createConnection()); + + XmlaOlap4jCellSet cellSet = new StubbedOlap4jCellSet(statement); + + for (String value : valueTags.keySet()) { + statement.setResponse( + templateResponse.replace( + "${VALUE}", value)); + + cellSet.populate(); + assertEquals( + "Incorrect datatype conversion for value tag: \n" + + value + "\n", + valueTags.get(value), + cellSet.getCell(0).getValue().getClass()); + } + } + + + class MockOlap4jStatement extends XmlaOlap4jStatement { + + private String response; + + MockOlap4jStatement(XmlaOlap4jConnection olap4jConnection) { + super(olap4jConnection); + } + + void setResponse(String response) { + this.response = response; + } + + byte[] getBytes() throws OlapException { + return response.getBytes(); + } + + public void closeOnCompletion() throws SQLException { + } + + public boolean isCloseOnCompletion() throws SQLException { + return false; + } + } + + class StubbedOlap4jCellSet extends XmlaOlap4jCellSet { + + public StubbedOlap4jCellSet( + XmlaOlap4jStatement olap4jStatement) + { + super(olap4jStatement); + } + + public RowId getRowId( + int columnIndex) throws SQLException + { + return null; + } + + public RowId getRowId( + String columnLabel) throws SQLException + { + return null; + } + + public void updateRowId( + int columnIndex, RowId x) throws SQLException + { + } + + public void updateRowId( + String columnLabel, RowId x) throws SQLException + { + } + + public int getHoldability() throws SQLException + { + return 0; + } + + public boolean isClosed() throws SQLException + { + return false; + } + + public void updateNString( + int columnIndex, String nString) throws SQLException + { + } + + public void updateNString( + String columnLabel, String nString) throws SQLException + { + } + + public void updateNClob( + int columnIndex, NClob nClob) throws SQLException + { + } + + public void updateNClob( + String columnLabel, NClob nClob) throws SQLException + { + } + + public NClob getNClob( + int columnIndex) throws SQLException + { + return null; + } + + public NClob getNClob( + String columnLabel) throws SQLException + { + return null; + } + + public SQLXML getSQLXML( + int columnIndex) throws SQLException + { + return null; + } + + public SQLXML getSQLXML( + String columnLabel) throws SQLException + { + return null; + } + + public void updateSQLXML( + int columnIndex, SQLXML xmlObject) throws SQLException + { + } + + public void updateSQLXML( + String columnLabel, SQLXML xmlObject) throws SQLException + { + } + + public String getNString( + int columnIndex) throws SQLException + { + return null; + } + + public String getNString( + String columnLabel) throws SQLException + { + return null; + } + + public Reader getNCharacterStream( + int columnIndex) throws SQLException + { + return null; + } + + public Reader getNCharacterStream( + String columnLabel) throws SQLException + { + return null; + } + + public void updateNCharacterStream( + int columnIndex, Reader x, long length) throws SQLException + { + } + + public void updateNCharacterStream( + String columnLabel, Reader reader, long length) throws SQLException + { + } + + public void updateAsciiStream( + int columnIndex, InputStream x, long length) throws SQLException + { + } + + public void updateBinaryStream( + int columnIndex, InputStream x, long length) throws SQLException + { + } + + public void updateCharacterStream( + int columnIndex, Reader x, long length) throws SQLException + { + } + + public void updateAsciiStream( + String columnLabel, InputStream x, long length) throws SQLException + { + } + + public void updateBinaryStream( + String columnLabel, InputStream x, long length) throws SQLException + { + } + + public void updateCharacterStream( + String columnLabel, Reader reader, long length) throws SQLException + { + } + + public void updateBlob( + int columnIndex, InputStream inputStream, + long length) throws SQLException + { + } + + public void updateBlob( + String columnLabel, InputStream inputStream, + long length) throws SQLException + { + } + + public void updateClob( + int columnIndex, Reader reader, long length) throws SQLException + { + } + + public void updateClob( + String columnLabel, Reader reader, long length) throws SQLException + { + } + + public void updateNClob( + int columnIndex, Reader reader, long length) throws SQLException + { + } + + public void updateNClob( + String columnLabel, Reader reader, long length) throws SQLException + { + } + + public void updateNCharacterStream( + int columnIndex, Reader x) throws SQLException + { + } + + public void updateNCharacterStream( + String columnLabel, Reader reader) throws SQLException + { + } + + public void updateAsciiStream( + int columnIndex, InputStream x) throws SQLException + { + } + + public void updateBinaryStream( + int columnIndex, InputStream x) throws SQLException + { + } + + public void updateCharacterStream( + int columnIndex, Reader x) throws SQLException + { + } + + public void updateAsciiStream( + String columnLabel, InputStream x) throws SQLException + { + } + + public void updateBinaryStream( + String columnLabel, InputStream x) throws SQLException + { + } + + public void updateCharacterStream( + String columnLabel, Reader reader) throws SQLException + { + } + + public void updateBlob( + int columnIndex, InputStream inputStream) throws SQLException + { + } + + public void updateBlob( + String columnLabel, InputStream inputStream) throws SQLException + { + } + + public void updateClob( + int columnIndex, Reader reader) throws SQLException + { + } + + public void updateClob( + String columnLabel, Reader reader) throws SQLException + { + } + + public void updateNClob( + int columnIndex, Reader reader) throws SQLException + { + } + + public void updateNClob( + String columnLabel, Reader reader) throws SQLException + { + } + + public T getObject( + int columnIndex, Class type) throws SQLException + { + return null; + } + + public T getObject( + String columnLabel, Class type) throws SQLException + { + return null; + } + } + + + private static final String templateResponse = + "" + + "\n" + + " \n" + + " n" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " HR\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " [Measures].[Org Salary]\n" + + " Org Salary\n" + + " [Measures].[MeasuresLevel]\n" + + " 0\n" + + " 0\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " [Employees].[All Employees]\n" + + " All Employees\n" + + " [Employees].[(All)]\n" + + " 0\n" + + " 65537\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " [Employees].[Sheri Nowmer]\n" + + " Sheri Nowmer\n" + + " [Employees].[Employee Id]\n" + + " 1\n" + + " 7\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " [Time].[1997]\n" + + " 1997\n" + + " [Time].[Year]\n" + + " 0\n" + + " 4\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " ${VALUE}\n" + + " \n" + + " \n" + + " \n" + + " \n" + + "" + + "\n"; + +} + +// End XmlaOlap4jCellSetTest.java \ No newline at end of file