/* * Hibernate, Relational Persistence for Idiomatic Java * * Copyright (c) 2010, Red Hat Inc. or third-party contributors as * indicated by the @author tags or express copyright attribution * statements applied by the authors. All third-party contributions are * distributed under license by Red Hat Inc. * * This copyrighted material is made available to anyone wishing to use, modify, * copy, or redistribute it subject to the terms and conditions of the GNU * Lesser General Public License, as published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU Lesser General Public License * along with this distribution; if not, write to: * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ package org.hibernate.type.descriptor.java; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.io.StringReader; import org.jboss.logging.Logger; import org.hibernate.HibernateException; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.util.ReflectHelper; import org.hibernate.type.descriptor.BinaryStream; /** * A help for dealing with BLOB and CLOB data * * @author Steve Ebersole */ public class DataHelper { private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, DataHelper.class.getName()); private static Class nClobClass; static { try { // NClobs are only JDBC 4 (JDK 1.6) and higher nClobClass = ReflectHelper.classForName( "java.sql.NClob", DataHelper.class ); } catch ( ClassNotFoundException e ) { LOG.unableToLocateNClobClass(); } } public static boolean isNClob(Class type) { return nClobClass != null && nClobClass.isAssignableFrom( type ); } /** * Extract the contents of the given reader/stream as a string. * * @param reader The reader for the content * * @return The content as string */ public static String extractString(Reader reader) { // read the Reader contents into a buffer and return the complete string final StringBuilder stringBuilder = new StringBuilder(); try { char[] buffer = new char[2048]; while (true) { int amountRead = reader.read( buffer, 0, buffer.length ); if ( amountRead == -1 ) { break; } stringBuilder.append( buffer, 0, amountRead ); } } catch ( IOException ioe) { throw new HibernateException( "IOException occurred reading text", ioe ); } finally { try { reader.close(); } catch (IOException e) { LOG.unableToCloseStream(e); } } return stringBuilder.toString(); } /** * Extracts a portion of the contents of the given reader/stream as a string. * * @param characterStream The reader for the content * @param start The start position/offset (0-based, per general stream/reader contracts). * @param length The amount to extract * * @return The content as string */ private static String extractString(Reader characterStream, long start, int length) { StringBuilder stringBuilder = new StringBuilder( length ); try { long skipped = characterStream.skip( start ); if ( skipped != start ) { throw new HibernateException( "Unable to skip needed bytes" ); } char[] buffer = new char[2048]; int charsRead = 0; while ( true ) { int amountRead = characterStream.read( buffer, 0, buffer.length ); if ( amountRead == -1 ) { break; } stringBuilder.append( buffer, 0, amountRead ); if ( amountRead < buffer.length ) { // we have read up to the end of stream break; } charsRead += amountRead; if ( charsRead >= length ) { break; } } } catch ( IOException ioe ) { throw new HibernateException( "IOException occurred reading a binary value", ioe ); } return stringBuilder.toString(); } /** * Extract a portion of a reader, wrapping the portion in a new reader. * * @param characterStream The reader for the content * @param start The start position/offset (0-based, per general stream/reader contracts). * @param length The amount to extract * * @return The content portion as a reader */ public static Object subStream(Reader characterStream, long start, int length) { return new StringReader( extractString( characterStream, start, length ) ); } /** * Extract by bytes from the given stream. * * @param inputStream The stream of bytes. * * @return The contents as a {@code byte[]} */ public static byte[] extractBytes(InputStream inputStream) { if ( BinaryStream.class.isInstance( inputStream ) ) { return ( (BinaryStream ) inputStream ).getBytes(); } // read the stream contents into a buffer and return the complete byte[] ByteArrayOutputStream outputStream = new ByteArrayOutputStream(2048); try { byte[] buffer = new byte[2048]; while (true) { int amountRead = inputStream.read( buffer ); if ( amountRead == -1 ) { break; } outputStream.write( buffer, 0, amountRead ); } } catch ( IOException ioe ) { throw new HibernateException( "IOException occurred reading a binary value", ioe ); } finally { try { inputStream.close(); } catch ( IOException e ) { LOG.unableToCloseInputStream(e); } try { outputStream.close(); } catch ( IOException e ) { LOG.unableToCloseOutputStream(e); } } return outputStream.toByteArray(); } /** * Extract a portion of the bytes from the given stream. * * @param inputStream The stream of bytes. * @param start The start position/offset (0-based, per general stream/reader contracts). * @param length The amount to extract * * @return The extracted bytes */ public static byte[] extractBytes(InputStream inputStream, long start, int length) { if ( BinaryStream.class.isInstance( inputStream ) && Integer.MAX_VALUE > start ) { byte[] data = ( (BinaryStream ) inputStream ).getBytes(); int size = Math.min( length, data.length ); byte[] result = new byte[size]; System.arraycopy( data, (int) start, result, 0, size ); return result; } ByteArrayOutputStream outputStream = new ByteArrayOutputStream( length ); try { long skipped = inputStream.skip( start ); if ( skipped != start ) { throw new HibernateException( "Unable to skip needed bytes" ); } byte[] buffer = new byte[2048]; int bytesRead = 0; while ( true ) { int amountRead = inputStream.read( buffer ); if ( amountRead == -1 ) { break; } outputStream.write( buffer, 0, amountRead ); if ( amountRead < buffer.length ) { // we have read up to the end of stream break; } bytesRead += amountRead; if ( bytesRead >= length ) { break; } } } catch ( IOException ioe ) { throw new HibernateException( "IOException occurred reading a binary value", ioe ); } return outputStream.toByteArray(); } /** * Extract a portion of the bytes from the given stream., wrapping them in a new stream. * * @param inputStream The stream of bytes. * @param start The start position/offset (0-based, per general stream/reader contracts). * @param length The amount to extract * * @return The extracted bytes as a stream */ public static InputStream subStream(InputStream inputStream, long start, int length) { return new BinaryStreamImpl( extractBytes( inputStream, start, length ) ); } }