/*! * This program is free software; you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software * Foundation. * * You should have received a copy of the GNU Lesser General Public License along with this * program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html * or from the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * Copyright (c) 2002-2013 Pentaho Corporation.. All rights reserved. */ package org.pentaho.platform.plugin.services.connections.mondrian; import mondrian.olap.Connection; import mondrian.olap.Result; import org.pentaho.commons.connection.IMultiDimensionalResultSet; import org.pentaho.commons.connection.IPeekable; import org.pentaho.commons.connection.IPentahoMetaData; import org.pentaho.commons.connection.IPentahoResultSet; import org.pentaho.commons.connection.memory.MemoryMetaData; import org.pentaho.commons.connection.memory.MemoryResultSet; /** * @author wseyler * * TODO To change the template for this generated type comment go to Window - Preferences - Java - Code Style - * Code Templates */ public class MDXResultSet implements IPentahoResultSet, IPeekable, IMultiDimensionalResultSet { private Result nativeResultSet; private Connection nativeConnection; private int rowIndex = 0; private MDXMetaData mdxMetaData = null; private Object[] peekRow; private boolean formattedCellValues = false; // should we return formatted or plain cell values public MDXResultSet() { } /** * @param useExtendedColumnNames * if true, columnNames will follow the format: "[dimension_name].[hierarchy_name].[level_name]" otherwise * the format for column names will be: "hierarchy_name{column_number}" * * Implemented as a flag to allow reports prior to platform version 2.1 (Liberty) to continue to execute as * expected with the short column names, but if the developer sets the extendedColumnNames flag to true, can * overcome the bug in BISERVER-1266. * * @param returnNullCells * if true, returns null instead of 0.000000012345. This is configurable for backwards compatibility * */ public MDXResultSet( final Result nativeResultSet, final Connection nativeConnection, boolean useExtendedColumnNames ) { super(); this.nativeResultSet = nativeResultSet; this.nativeConnection = nativeConnection; mdxMetaData = new MDXMetaData( this.nativeResultSet, useExtendedColumnNames ); } /* * (non-Javadoc) * * @see org.pentaho.connection.IPentahoResultSet#getMetaData() */ public IPentahoMetaData getMetaData() { return mdxMetaData; } public Object[] peekRowHeaders() { int peekRowNo = rowIndex; if ( peekRowNo < getRowCount() ) { return mdxMetaData.getRowHeaders()[peekRowNo]; } return null; } public Object[] peek() { if ( peekRow == null ) { peekRow = next(); } return peekRow; } /* * (non-Javadoc) * * @see org.pentaho.connection.IPentahoResultSet#next() */ public Object[] next() { if ( peekRow != null ) { Object[] row = peekRow; peekRow = null; return row; } Object[] currentRow = null; int columnCount = getColumnCount(); if ( rowIndex < getRowCount() ) { currentRow = new Object[columnCount]; for ( int i = 0; i < columnCount; i++ ) { currentRow[i] = getValueAt( rowIndex, i ); } rowIndex++; } return currentRow; } /* * (non-Javadoc) * * @see org.pentaho.connection.IPentahoResultSet#close() */ public void close() { nativeResultSet.close(); } /* * (non-Javadoc) * * @see org.pentaho.connection.IPentahoResultSet#closeConnection() */ public void closeConnection() { nativeResultSet.close(); nativeConnection.close(); } /* * (non-Javadoc) * * @see org.pentaho.core.runtime.IDisposable#dispose() */ public void dispose() { closeConnection(); } /* * (non-Javadoc) * * @see org.pentaho.connection.IPentahoResultSet#isScrollable() */ public boolean isScrollable() { return true; } /* * (non-Javadoc) * * @see org.pentaho.connection.IPentahoResultSet#getValueAt(int, int) */ public Object getValueAt( final int row, final int column ) { int[] key = new int[2]; key[0] = column; key[1] = row; if ( formattedCellValues ) { return nativeResultSet.getCell( key ).getFormattedValue(); } else { return nativeResultSet.getCell( key ).getValue(); } } /* * (non-Javadoc) * * @see org.pentaho.connection.IPentahoResultSet#getRowCount() */ public int getRowCount() { return mdxMetaData.getRowHeaders().length; } /* * (non-Javadoc) * * @see org.pentaho.connection.IPentahoResultSet#getColumnCount() */ public int getColumnCount() { return mdxMetaData.getColumnCount(); } public IPentahoResultSet memoryCopy() { try { IPentahoMetaData metadata = getMetaData(); Object[][] columnHeaders = metadata.getColumnHeaders(); Object[][] rowHeaders = metadata.getRowHeaders(); MemoryMetaData cachedMetaData = new MemoryMetaData( columnHeaders, rowHeaders ); MemoryResultSet cachedResultSet = new MemoryResultSet( cachedMetaData ); Object[] rowObjects = next(); while ( rowObjects != null ) { cachedResultSet.addRow( rowObjects ); rowObjects = next(); } return cachedResultSet; } finally { close(); } } public void beforeFirst() { rowIndex = 0; } public Object[] getDataColumn( final int column ) { int oldIndex = rowIndex; // save our current iteration location beforeFirst(); Object[] result = new Object[getRowCount()]; int index = 0; Object[] rowData = next(); while ( rowData != null ) { result[index] = rowData[column]; index++; rowData = next(); } rowIndex = oldIndex; // restore the old iteration location return result; } public Object[] getDataRow( final int row ) { int oldIndex = rowIndex; // save our current iteration location rowIndex = row; Object[] rowData = next(); rowIndex = oldIndex; return rowData; } protected Result getNativeResultSet() { return nativeResultSet; } protected void setNativeResultSet( Result nativeResultSet ) { this.nativeResultSet = nativeResultSet; } protected Connection getNativeConnection() { return nativeConnection; } protected void setNativeConnection( Connection nativeConnection ) { this.nativeConnection = nativeConnection; } protected MDXMetaData getMdxMetaData() { return mdxMetaData; } protected void setMdxMetaData( MDXMetaData mdxMetaData ) { this.mdxMetaData = mdxMetaData; } protected Object[] getPeekRow() { return peekRow; } protected void setPeekRow( Object[] peekRow ) { this.peekRow = peekRow; } public int getRowIndex() { return rowIndex; } public Object[] nextFlattened() { Object[][] rowHeaders = mdxMetaData.getRowHeaders(); if ( rowHeaders == null ) { // we have no row headers so we can call the regular next() return next(); } // get the row Object[] row = next(); if ( row == null ) { // we have got to the end return null; } // do we have row headers to return also? if ( rowIndex <= rowHeaders.length ) { // pull out the right row headers Object[] rowHeads = rowHeaders[rowIndex - 1]; // create the flattened row Object[] flatRow = new Object[rowHeads.length + row.length]; // copy in the row headers and row objects System.arraycopy( rowHeads, 0, flatRow, 0, rowHeads.length ); System.arraycopy( row, 0, flatRow, rowHeads.length, row.length ); return flatRow; } return row; } public Object[] peekFlattened() { Object[][] rowHeaders = mdxMetaData.getRowHeaders(); if ( rowHeaders == null ) { // we have no row headers so we can call the regular peek() return peek(); } // get the row Object[] row = peek(); if ( row == null ) { // we have got to the end return null; } // do we have row headers to return also? if ( rowIndex <= rowHeaders.length ) { // pull out the right row headers Object[] rowHeads = rowHeaders[rowIndex - 1]; // create the flattened row Object[] flatRow = new Object[rowHeads.length + row.length]; // copy in the row headers and row objects System.arraycopy( rowHeads, 0, flatRow, 0, rowHeads.length ); System.arraycopy( row, 0, flatRow, rowHeads.length, row.length ); return flatRow; } return row; } /** * Sets the 'formatted cell values' flag. If this flag is set calls to getValueAt (and methods like next() and peek() * that use getValueAt) returns the formatted value of the cell instead of the plain number. BISERVER-3543 * * @param formattedCellValues */ public void setFormattedCellValues( boolean formattedCellValues ) { this.formattedCellValues = formattedCellValues; } }