/*!
* 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.xquery;
import net.sf.saxon.om.Axis;
import net.sf.saxon.om.AxisIterator;
import net.sf.saxon.om.Item;
import net.sf.saxon.om.SequenceIterator;
import net.sf.saxon.query.DynamicQueryContext;
import net.sf.saxon.query.XQueryExpression;
import net.sf.saxon.tinytree.TinyNodeImpl;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.type.Type;
import net.sf.saxon.value.Value;
import org.apache.commons.collections.OrderedMap;
import org.apache.commons.collections.map.ListOrderedMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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;
import java.math.BigDecimal;
import java.sql.Date;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* @author wseyler
*
* TODO To change the template for this generated type comment go to Window - Preferences - Java - Code Style -
* Code Templates
*/
public class XQResultSet implements IPentahoResultSet, IPeekable {
protected static final Log logger = LogFactory.getLog( XQResultSet.class );
protected XQueryExpression exp = null;
protected DynamicQueryContext dynamicContext = null;
protected XQMetaData metaData = null;
protected static final String DELIM = ", "; //$NON-NLS-1$
protected static final String EMPTY_STR = ""; //$NON-NLS-1$
protected Object[] peekRow;
Iterator iter = null;
protected String[] columnTypes = null;
protected XQConnection connection;
private List evaluatedList;
/**
* @param exp
* @param dynamicContext
* @param columnTypes
* @throws XPathException
*/
public XQResultSet( final XQConnection xqConnection, final XQueryExpression exp,
final DynamicQueryContext dynamicContext, final String[] columnTypes ) throws XPathException {
super();
this.columnTypes = columnTypes;
this.exp = exp;
this.dynamicContext = dynamicContext;
this.connection = xqConnection;
init();
}
protected void init() throws XPathException {
if ( evaluatedList == null ) {
evaluatedList = evaluate();
}
if ( this.metaData == null ) {
iter = evaluatedList.iterator();
this.metaData = new XQMetaData( connection, iter );
}
iter = evaluatedList.iterator();
}
protected List evaluate() throws XPathException {
SequenceIterator sequenceiterator = exp.iterator( dynamicContext );
List rtn = new ArrayList( 100 );
int rowCount = 0;
int maxRows = ( this.connection != null ) ? this.connection.getMaxRows() : -1;
Item item = null;
while ( ( item = sequenceiterator.next() ) != null ) {
if ( ( item == null ) ) {
break;
}
rowCount++;
if ( ( maxRows >= 0 ) && ( rowCount > maxRows ) ) {
break;
}
rtn.add( Value.convertToJava( item ) );
}
return rtn;
}
/*
* (non-Javadoc)
*
* @see org.pentaho.connection.IPentahoResultSet#getMetaData()
*/
public IPentahoMetaData getMetaData() {
return metaData;
}
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;
}
// Create a map of the headers and assign empty string to them
OrderedMap resultList = new ListOrderedMap();
for ( int i = 0; i < metaData.getColumnCount(); i++ ) {
resultList.put( metaData.getColumnHeaders()[0][i], XQResultSet.EMPTY_STR );
}
// Get the next row of data
if ( iter.hasNext() ) {
Object o = iter.next();
decodeNode( o, resultList );
}
// get the values
Object[] currentRow = new Object[resultList.size()];
Iterator keyIter = resultList.keySet().iterator();
int i = 0;
while ( keyIter.hasNext() ) {
currentRow[i] = resultList.get( keyIter.next() );
i++;
}
// if all the values are the empty string then we're done.
boolean done = true;
for ( Object element : currentRow ) {
if ( !( "".equals( element ) ) ) { //$NON-NLS-1$
done = false;
}
}
if ( done ) {
return null;
}
return currentRow;
}
protected void decodeNode( final Object obj, final Map retValue ) {
if ( obj instanceof TinyNodeImpl ) {
AxisIterator aIter = ( (TinyNodeImpl) obj ).iterateAxis( Axis.DESCENDANT );
Object descendent = aIter.next();
boolean processedChildren = false;
int columnIndex = 0;
while ( descendent != null ) {
if ( ( descendent instanceof TinyNodeImpl ) && ( ( (TinyNodeImpl) descendent )
.getNodeKind() == Type.ELEMENT ) ) {
TinyNodeImpl descNode = (TinyNodeImpl) descendent;
Object value = retValue.get( descNode.getDisplayName() );
if ( value == null ) {
value = XQResultSet.EMPTY_STR;
}
if ( !( XQResultSet.EMPTY_STR.equals( value ) ) ) {
value = value.toString() + XQResultSet.DELIM;
}
value = value.toString() + descNode.getStringValue();
if ( ( value != null )
&& !value.equals( "" ) && ( columnTypes != null ) && ( columnIndex >= 0 )
&& ( columnIndex < columnTypes.length ) ) { //$NON-NLS-1$
String columnType = columnTypes[columnIndex].trim();
if ( columnType.equals( "java.math.BigDecimal" ) ) { //$NON-NLS-1$
value = new BigDecimal( value.toString() );
} else if ( columnType.equals( "java.sql.Timestamp" ) ) { //$NON-NLS-1$
value = new Timestamp( Long.parseLong( value.toString() ) );
} else if ( columnType.equals( "java.sql.Date" ) ) { //$NON-NLS-1$
value = new Date( Long.parseLong( value.toString() ) );
} else if ( columnType.equals( "java.lang.Integer" ) ) { //$NON-NLS-1$
value = new Integer( Integer.parseInt( value.toString() ) );
} else if ( columnType.equals( "java.lang.Double" ) ) { //$NON-NLS-1$
value = new Double( Double.parseDouble( value.toString() ) );
} else if ( columnType.equals( "java.lang.Long" ) ) { //$NON-NLS-1$
value = new Long( Long.parseLong( value.toString() ) );
}
}
retValue.put( descNode.getDisplayName(), value );
processedChildren = true;
columnIndex++;
}
descendent = aIter.next();
}
if ( !processedChildren ) {
Object key = ( (TinyNodeImpl) obj ).getDisplayName();
Object value = ( (TinyNodeImpl) obj ).getStringValue();
retValue.put( key, value );
}
} else {
retValue.put( XQMetaData.DEFAULT_COLUMN_NAME, obj.toString() );
}
}
/*
* (non-Javadoc)
*
* @see org.pentaho.connection.IPentahoResultSet#close()
*/
public void close() {
// TODO Auto-generated method stub
}
/*
* (non-Javadoc)
*
* @see org.pentaho.connection.IPentahoResultSet#closeConnection()
*/
public void closeConnection() {
// TODO Auto-generated method stub
}
/*
* (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 ) {
Object[] rowarr = getDataRow( row );
if ( ( rowarr != null ) && ( column >= 0 ) && ( column < rowarr.length ) ) {
return rowarr[column];
}
return null;
}
/*
* (non-Javadoc)
*
* @see org.pentaho.connection.IPentahoResultSet#getRowCount()
*/
public int getRowCount() {
return metaData.getRowCount();
}
/*
* (non-Javadoc)
*
* @see org.pentaho.connection.IPentahoResultSet#getColumnCount()
*/
public int getColumnCount() {
return metaData.getColumnCount();
}
/*
* (non-Javadoc)
*
* @see org.pentaho.core.runtime.IDisposable#dispose()
*/
public void dispose() {
// TODO Auto-generated method stub
}
public IPentahoResultSet memoryCopy() {
try {
IPentahoMetaData metadata = getMetaData();
Object[][] columnHeaders = metadata.getColumnHeaders();
MemoryMetaData cachedMetaData = new MemoryMetaData( columnHeaders, null );
// set column types of cachedMetaData
String[] columnTypeClones = new String[columnTypes.length];
System.arraycopy( columnTypes, 0, columnTypeClones, 0, columnTypes.length );
cachedMetaData.setColumnTypes( columnTypeClones );
MemoryResultSet cachedResultSet = new MemoryResultSet( cachedMetaData );
Object[] rowObjects = next();
while ( rowObjects != null ) {
cachedResultSet.addRow( rowObjects );
rowObjects = next();
}
return cachedResultSet;
} finally {
close();
}
}
public void beforeFirst() {
try {
init();
} catch ( XPathException e ) {
XQResultSet.logger.error( "Cannot initialize XQResultSet", e );
}
}
/*
* (non-Javadoc)
*
* @see org.pentaho.connection.IPentahoResultSet#getDataColumn(int)
*
* NOTE: calling this will move the cursor to the top of the result stack
*/
public Object[] getDataColumn( final int column ) {
if ( column >= getColumnCount() ) {
return null;
}
beforeFirst(); // go to top just in case we called this after some
// next()s
Object[] result = new Object[getRowCount()];
int rowIndex = 0;
Object[] rowData = next();
while ( rowData != null ) {
result[rowIndex] = rowData[column];
rowIndex++;
rowData = next();
}
beforeFirst();
return result;
}
public Object[] getDataRow( final int row ) {
beforeFirst(); // go to top
int count = 0;
while ( count++ < row ) {
next();
}
Object[] dataRow = next();
beforeFirst();
return dataRow;
}
}