/* * ModeShape (http://www.modeshape.org) * * 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 org.modeshape.jdbc.util; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; import java.io.BufferedOutputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.SQLXML; import java.sql.Types; import javax.jcr.Value; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; import org.hamcrest.core.IsInstanceOf; import org.modeshape.jdbc.JcrResultSet; import org.modeshape.jdbc.JcrType; /** * This object wraps/extends a SQL ResultSet object as Reader object. Once the ResultSet can read as reader then it can be * persisted, printed or compared easily without lot of code hassle of walking it every time. * <p> * PS: remember this is a Reader not InputStream, so all the fields read going to be converted to strings before they returned. */ public class ResultSetReader extends StringLineReader { ResultSet source = null; ResultSetMetaData metadata = null; // Number of columns in the result set int columnCount = 0; // delimiter between the fields while reading each row String delimiter = null; boolean firstTime = true; int[] columnTypes = null; private int rowCount = 0; private boolean compareColumns = true; public ResultSetReader( ResultSet in, String delimiter, boolean compareCols ) { this.source = in; this.delimiter = delimiter; this.compareColumns = compareCols; } /** * @see java.io.Reader#close() */ @Override public void close() throws IOException { try { source.close(); } catch (SQLException e) { } super.close(); } /** * Get the next line of results from the ResultSet. The first line will be the metadata of the resultset and then followed by * the result rows. Each row will be returned as one line. * * @return next result line from result set. * @throws IOException */ @Override protected String nextLine() throws IOException, SQLException { if (firstTime) { firstTime = false; this.metadata = source.getMetaData(); columnCount = metadata.getColumnCount(); columnTypes = new int[columnCount]; for (int i = 0; i < columnCount; i++) { columnTypes[i] = metadata.getColumnType(i + 1); } return resultSetMetaDataToString(metadata, delimiter); } // if you get here then we are ready to read the results. if (source.next()) { rowCount++; final StringBuilder sb = new StringBuilder(); // Walk through column values in this row for (int col = 1; col <= columnCount; col++) { // this does not work when database metadata is being queried final Object anObj = source.getObject(col); if (compareColumns) compareColumn(col, anObj); if (columnTypes[col - 1] == Types.CLOB) { sb.append(anObj != null ? anObj : "null"); //$NON-NLS-1$ } else if (columnTypes[col - 1] == Types.BLOB) { sb.append(anObj != null ? "BLOB" : "null"); //$NON-NLS-1$ //$NON-NLS-2$ } else if (columnTypes[col - 1] == Types.SQLXML) { final SQLXML xml = (SQLXML)anObj; sb.append(anObj != null ? prettyPrint(xml) : "null"); //$NON-NLS-1$ } else { sb.append(anObj != null ? anObj : "null"); //$NON-NLS-1$ } if (col != columnCount) { sb.append(delimiter); } } sb.append("\n"); //$NON-NLS-1$ return sb.toString(); } return null; } private void compareColumn( int col, Object objIdx ) throws SQLException { String colName = metadata.getColumnName(col); Object objName = source.getObject(colName); assertThat(objIdx, is(objName)); if (objIdx == null) return; if (source instanceof JcrResultSet) { Value v = ((JcrResultSet)source).getValue(col); JcrType jcrType = JcrType.typeInfo(v.getType()); assertThat(objIdx, IsInstanceOf.instanceOf(jcrType.getRepresentationClass())); } } public int getRowCount() { return rowCount; } /** * Get the first line from the result set. This is the resultset metadata line where we gather the column names and their * types. * * @param metadata * @param delimiter * @return String * @throws SQLException */ public static String resultSetMetaDataToString( ResultSetMetaData metadata, String delimiter ) throws SQLException { StringBuilder sb = new StringBuilder(); int columnCount = metadata.getColumnCount(); for (int col = 1; col <= columnCount; col++) { String colName = metadata.getColumnName(col); String colTypeName = metadata.getColumnTypeName(col); /** * performing specific checks to make sure these are defined as expected. */ if (colName.equalsIgnoreCase("jcr:score")) { JcrType jcrType = JcrType.typeInfo(JcrType.DefaultDataTypes.DOUBLE); assertThat(colTypeName, is(jcrType.getJcrName())); } else if (colName.equalsIgnoreCase("mode:depth")) { JcrType jcrType = JcrType.typeInfo(JcrType.DefaultDataTypes.LONG); assertThat(colTypeName, is(jcrType.getJcrName())); } else if (colName.equalsIgnoreCase("mode:id")) { JcrType jcrType = JcrType.typeInfo(JcrType.DefaultDataTypes.STRING); assertThat(colTypeName, is(jcrType.getJcrName())); } sb.append(colName).append("[") //$NON-NLS-1$ .append(colTypeName).append("]"); //$NON-NLS-1$ if (col != columnCount) { sb.append(delimiter); } } sb.append("\n"); //$NON-NLS-1$ return sb.toString(); } public static String prettyPrint( SQLXML xml ) throws SQLException { try { TransformerFactory transFactory = TransformerFactory.newInstance(); transFactory.setAttribute("indent-number", new Integer(2)); //$NON-NLS-1$ Transformer tf = transFactory.newTransformer(); tf.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); //$NON-NLS-1$ tf.setOutputProperty(OutputKeys.ENCODING, "UTF-8");//$NON-NLS-1$ tf.setOutputProperty(OutputKeys.INDENT, "yes");//$NON-NLS-1$ tf.setOutputProperty(OutputKeys.METHOD, "xml");//$NON-NLS-1$ tf.setOutputProperty(OutputKeys.STANDALONE, "yes");//$NON-NLS-1$ tf.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); //$NON-NLS-1$ //$NON-NLS-2$ ByteArrayOutputStream out = new ByteArrayOutputStream(); StreamResult xmlOut = new StreamResult(new BufferedOutputStream(out)); tf.transform(xml.getSource(StreamSource.class), xmlOut); return out.toString(); } catch (Exception e) { return xml.getString(); } } }