All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.log4jdbc.sql.resultsetcollector.DefaultResultSetCollector Maven / Gradle / Ivy

The newest version!
/**
 * Copyright 2010 Tim Azzopardi
 *
 * Licensed 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 net.sf.log4jdbc.sql.resultsetcollector;

import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import net.sf.log4jdbc.sql.jdbcapi.ResultSetSpy;


public class DefaultResultSetCollector implements ResultSetCollector {

  private static final String NULL_RESULT_SET_VAL = "[null]";
  private static final String UNREAD = "[unread]";
  private static final String UNREAD_ERROR = "[unread!]";
  private boolean fillInUnreadValues = false;

  public DefaultResultSetCollector(boolean fillInUnreadValues) {
	  this.reset();
      this.fillInUnreadValues = fillInUnreadValues;
      this.lastValueReturnedByNext = true;
  }
  /**
   * A {@code boolean} defining whether parameters of this {@code DefaultResultSetColector} 
   * has already been obtained from a {@code ResultSetMetaData}.
   */
  private boolean loaded;
  /**
   * An int representing the number of columns 
   * in the real ResultSet object, obtained by calling 
   * getColumnCount on the related ResultSetMetaData object.
   * This attribute is an Integer rather than an int, 
   * so that we can distinguish the case where the column count has not yet been obtained, 
   * from the case when the column count is 0 (should never happend, but still...)
   */
  private int columnCount;
  /**
   * A Map where the key is an Integer representing 
   * the index of a column, and the corresponding value is a String 
   * being the label of the column. 
   * This Map is populated by calling getColumnLabel 
   * on the ResultSetMetaData object of the real ResultSet object, 
   * for each column (see {@link #columnCount}).
   */
  private Map columnLabels;
  /**
   * A Map where the key is an Integer representing 
   * the index of a column, and the corresponding value is a String 
   * being the name of the column. 
   * This Map is populated by calling getColumnLabel 
   * on the ResultSetMetaData object of the real ResultSet object, 
   * for each column (see {@link #columnCount}).
   */
  private Map columnNames;
  /**
   * A boolean that is the last value returned by a call 
   * to next (or first) on the related ResultSet. 
   * This allows to know if this ResultSetCollector must be printed 
   * when close is called on the related ResultSet: 
   * if the previous call to next returned true, 
   * then this ResultSetCollector must be printed 
   * on the call to close; otherwise, it was already printed following 
   * the previous call to next, and should not be printed 
   * on the call to close.
   * 

* This attribute must not be reset after a printing (the other attributes are, through a call * to reset), in case the related ResultSet is reused. *

* Default value at initialization is true (ResultSetCollector * not printed). */ private boolean lastValueReturnedByNext; private List row; private List> rows; private Map colNameToColIndex; private int colIndex; private static final List GETTERS = Arrays.asList(new String[] { "getString", "getLong", "getInt", "getDate", "getTimestamp", "getTime", "getBigDecimal", "getFloat", "getDouble", "getByte", "getShort", "getObject", "getBoolean", }); public List> getRows() { return rows; } public int getColumnCount() { return columnCount; } public void reset() { loaded = false; rows = null; row = null; colNameToColIndex = null; colIndex = -1;// Useful for wasNull calls columnCount = 0; columnLabels = new HashMap(); columnNames = new HashMap(); } @Override public void loadMetaDataIfNeeded(ResultSet rs) { //if data already loaded if (this.loaded) { return; } //otherwise, get all data now, so that we don't need //to use the ResultSetMetaData later (with some drivers, //it cannot be used once the ResultSet has been closed) try { if (!rs.isClosed()) { this.loadMetaDataIfNeeded(rs.getMetaData()); } } catch (SQLException e) { throw new RuntimeException(e); } this.loaded = true; } /** * Perform the operations requested by {@link #loadMetaDataIfNeeded(ResultSet)}, * using directly the related ResultSetMetaData. * @param metaData The ResultSetMetaData through which the information * needed by {@link #loadMetaDataIfNeeded(ResultSet)} are obtained. */ private void loadMetaDataIfNeeded(ResultSetMetaData metaData) { //if data already loaded if (this.loaded) { return; } //otherwise, get all data now, so that we don't need //to use the ResultSetMetaData later (with some drivers, //it cannot be used once the ResultSet has been closed) try { if (metaData == null) { this.columnCount = 0; } else { this.columnCount = metaData.getColumnCount(); } this.colNameToColIndex = new HashMap(this.columnCount); for (int column = 1; column <= this.columnCount; column++) { String label = metaData.getColumnLabel(column).toLowerCase(); String name = metaData.getColumnName(column).toLowerCase(); this.columnLabels.put(column, label); this.columnNames.put(column, name); colNameToColIndex.put(label, column); colNameToColIndex.put(name, column); } this.loaded = true; } catch (SQLException e) { throw new RuntimeException(e); } } public String getColumnName(int column) { return this.columnNames.get(column); } public String getColumnLabel(int column) { return this.columnLabels.get(column); } /* * (non-Javadoc) * * @see net.sf.log4jdbc.ResultSetCollector#methodReturned(net.sf.log4jdbc. * ResultSetSpy, java.lang.String, java.lang.Object, java.lang.Object, * java.lang.Object) */ @Override public boolean methodReturned(ResultSetSpy resultSetSpy, String methodCall, Object returnValue, Object targetObject, Object... methodParams) { if (methodCall.startsWith("get") && methodParams != null && methodParams.length == 1) { String methodName = methodCall.substring(0, methodCall.indexOf('(')); if (GETTERS.contains(methodName) && getColumnCount() != 0) { setColIndexFromGetXXXMethodParams(methodParams); makeRowIfNeeded(); row.set(colIndex - 1, returnValue); } } if (methodCall.equals("wasNull()") && getColumnCount() != 0) { if (Boolean.TRUE.equals(returnValue)) { row.set(colIndex - 1, NULL_RESULT_SET_VAL); } } if ("next()".equals(methodCall) || "first()".equals(methodCall)) { this.lastValueReturnedByNext = (Boolean) returnValue; } if ("next()".equals(methodCall) || "first()".equals(methodCall) || "close()".equals(methodCall)) { loadMetaDataIfNeeded(resultSetSpy.getRealResultSet()); boolean isEndOfResultSet = //"close" triggers a printing only if the previous call to next //did not return false (end of result set already reached) ("close()".equals(methodCall) && this.lastValueReturnedByNext != false) || Boolean.FALSE.equals(returnValue) ; if (row != null) { if (rows == null) rows = new ArrayList>(); rows.add(row); row = null; } if (isEndOfResultSet) { return true; } } // TODO: Tim: if prev() called, warn about no support for reverse cursors if ("getMetaData()".equals(methodCall)) { // If the client code calls getMetaData then we don't have to //here we assume that the real ResultSet is not yet closed. this.loadMetaDataIfNeeded((ResultSetMetaData) returnValue); } return false; } private void makeRowIfNeeded() { if (row == null) { row = new ArrayList(getColumnCount()); for (int i = 0; i < getColumnCount(); ++i) { row.add(UNREAD); } } } private void setColIndexFromGetXXXMethodParams(Object... methodParams) { Object param1 = methodParams[0]; if (param1 == null) { throw new RuntimeException("ResultSet.getXXX() first param null? "); } if (param1 instanceof Integer) { colIndex = (Integer) param1; } else if (param1 instanceof String) { if (colNameToColIndex == null) { throw new RuntimeException("ResultSet.getXXX(colName): colNameToColIndex null"); } Integer idx = colNameToColIndex.get(((String) param1).toLowerCase()); if (idx == null) { throw new RuntimeException("ResultSet.getXXX(colName): could not look up name"); } colIndex = idx; } else { throw new RuntimeException("ResultSet.getXXX called with: " + param1.getClass().getName()); } } @Override public void preMethod(ResultSetSpy resultSetSpy, String methodCall, Object... methodParams) { if ((methodCall.equals("next()") || methodCall.equals("close()")) && fillInUnreadValues) { if (row != null) { int colIndex = 0; for (Object v : row) { if (v != null && v.toString().equals(UNREAD)) { Object resultSetValue = null; try { // Fill in any unread data resultSetValue = JdbcUtils.getResultSetValue(resultSetSpy.getRealResultSet(),colIndex+1); } catch (SQLException e) { resultSetValue = UNREAD_ERROR; } if (resultSetValue!=null) { row.set(colIndex, resultSetValue); } else { row.set(colIndex, NULL_RESULT_SET_VAL); } } colIndex++; } } } } }