/* * 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 15, 2005 * @author wseyler */ package org.pentaho.platform.plugin.services.connections.xquery; 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; 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; /** * @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.convert(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; } }