/* * 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 2005 - 2009 Pentaho Corporation. All rights reserved. * * * Created Sep 12, 2005 * @author wseyler */ 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() { // TODO Auto-generated method stub return false; } /* * (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; } }