--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.jdbc.ClobUpdatableReader\r
+\r
+ Licensed to the Apache Software Foundation (ASF) under one\r
+ or more contributor license agreements. See the NOTICE file\r
+ distributed with this work for additional information\r
+ regarding copyright ownership. The ASF licenses this file\r
+ to you under the Apache License, Version 2.0 (the\r
+ "License"); you may not use this file except in compliance\r
+ with the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing,\r
+ software distributed under the License is distributed on an\r
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\r
+ KIND, either express or implied. See the License for the\r
+ specific language governing permissions and limitations\r
+ under the License.\r
+\r
+ */\r
+package org.apache.derby.impl.jdbc;\r
+\r
+import java.io.EOFException;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.io.Reader;\r
+import java.sql.SQLException;\r
+import org.apache.derby.iapi.reference.SQLState;\r
+import org.apache.derby.iapi.services.i18n.MessageService;\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+\r
+/**\r
+ * <code>ClobUpdatableReader</code> is used to create a <code>Reader</code>\r
+ * over a <code>LOBInputStream</code>.\r
+ * <p>\r
+ * This class is aware that the underlying stream can be modified and\r
+ * reinitializes itself if it detects any change in the stream. This\r
+ * invalidates the cache so the changes are reflected immediately.\r
+ * \r
+ * @see LOBInputStream\r
+ */\r
+final class ClobUpdatableReader extends Reader {\r
+ \r
+ /** Reader accessing the Clob data and doing the work. */\r
+ private Reader streamReader;\r
+ /** Character position of this reader. */\r
+ private long pos;\r
+ /** Underlying stream of byte data. */\r
+ private InputStream stream = null;\r
+ /** Connection object used to obtain synchronization-object. */\r
+ private ConnectionChild conChild;\r
+ /** flag to indicate if its associated with materialized clob */\r
+ private boolean materialized;\r
+ /** clob object this object is associated */\r
+ private final EmbedClob clob;\r
+ /**\r
+ * Position in Clob where to stop reading unless EOF is reached first.\r
+ */\r
+ private final long maxPos;\r
+ \r
+ \r
+ /**\r
+ * Constructs a <code>Reader</code> over a <code>LOBInputStream</code>.\r
+ * @param stream underlying stream of byte data\r
+ * @param conChild a connection object used to obtain synchronization-object\r
+ * @throws IOException\r
+ */\r
+ ClobUpdatableReader (LOBInputStream stream, ConnectionChild conChild)\r
+ throws IOException {\r
+ clob = null;\r
+ materialized = true;\r
+ this.conChild = conChild;\r
+ this.stream = stream;\r
+ //The subset of the Clob has not been requested. \r
+ //Hence set maxPos to infinity (or as close as we get).\r
+ this.maxPos = Long.MAX_VALUE;\r
+\r
+ init (stream, 0);\r
+ }\r
+\r
+ /**\r
+ * Constructs a <code>Reader</code> over a <code>LOBInputStream</code>.\r
+ * @param clob EmbedClob this Reader is associated to.\r
+ * @throws IOException\r
+ * @throws SQLException\r
+ */\r
+ ClobUpdatableReader (EmbedClob clob) throws IOException, SQLException {\r
+ // A subset of the Clob has not been requested.\r
+ // Hence set length to infinity (or as close as we get).\r
+ this(clob, 0L, Long.MAX_VALUE);\r
+ }\r
+ \r
+ /**\r
+ * Construct an <code>ClobUpdatableReader<code> using the \r
+ * <code>EmbedClob</code> received as parameter. The initial\r
+ * position in the stream is set to <code>pos</code> and the\r
+ * stream is restricted to a length of <code>len</code>.\r
+ * \r
+ * @param clob EmbedClob this stream is associated with.\r
+ * @param pos initial position. The position starts from 0.\r
+ * @param len The length to which the underlying <code>InputStream</code>\r
+ * has to be restricted.\r
+ * @throws IOException\r
+ * @throws SQLException\r
+ */\r
+ ClobUpdatableReader (EmbedClob clob, long pos, long len) \r
+ throws IOException, SQLException {\r
+ this.clob = clob;\r
+ this.conChild = clob;\r
+ this.maxPos = pos + len;\r
+\r
+ InternalClob internalClob = clob.getInternalClob();\r
+ materialized = internalClob.isWritable(); \r
+ if (materialized) {\r
+ long byteLength = internalClob.getByteLength();\r
+ this.stream = internalClob.getRawByteStream();\r
+ // Position the stream on pos using the init method.\r
+ init ((LOBInputStream)stream, pos);\r
+ } else {\r
+ if (SanityManager.DEBUG) {\r
+ SanityManager.ASSERT(internalClob instanceof StoreStreamClob,\r
+ "Wrong type of internal clob representation: " +\r
+ internalClob.toString());\r
+ }\r
+ // Since this representation is read-only, the stream never has to\r
+ // update itself, until the Clob representation itself has been\r
+ // changed. That even will be detected by {@link #updateIfRequired}.\r
+ this.streamReader = internalClob.getReader(pos + 1);\r
+ this.pos = pos;\r
+ }\r
+ }\r
+ \r
+ /**\r
+ * Reads chars into the cbuf char array. Changes made in uderlying storage \r
+ * will be reflected immidiatly from the corrent position.\r
+ * @param cbuf buffer to read into\r
+ * @param off offet of the cbuf array to start writing read chars\r
+ * @param len number of chars to be read\r
+ * @return number of bytes read\r
+ * @throws IOException\r
+ */\r
+ public int read(char[] cbuf, int off, int len) throws IOException { \r
+ updateIfRequired();\r
+ \r
+ //If the stream has exceeded maxPos the read should return -1\r
+ //signifying end of stream.\r
+ if (pos >= maxPos) {\r
+ return -1;\r
+ }\r
+\r
+ int actualLength = (int) Math.min(len, maxPos - pos);\r
+ int ret = streamReader.read (cbuf, off, actualLength);\r
+ if (ret >= 0) {\r
+ pos += ret;\r
+ }\r
+ return ret;\r
+ }\r
+\r
+ /**\r
+ * Closes the reader.\r
+ * @throws IOException\r
+ */\r
+ public void close() throws IOException {\r
+ streamReader.close();\r
+ }\r
+ \r
+ /**\r
+ * Initializes the streamReader and skips to the given position.\r
+ * @param skip number of characters to skip to reach initial position\r
+ * @throws IOException if a streaming error occurs\r
+ */\r
+ private void init(LOBInputStream stream, long skip) \r
+ throws IOException {\r
+ streamReader = new UTF8Reader (stream, 0, stream.length (), \r
+ conChild, \r
+ conChild.getConnectionSynchronization());\r
+ long remainToSkip = skip;\r
+ while (remainToSkip > 0) {\r
+ long skipBy = streamReader.skip(remainToSkip);\r
+ if (skipBy == 0) {\r
+ if (streamReader.read() == -1) {\r
+ throw new EOFException (\r
+ MessageService.getCompleteMessage (\r
+ SQLState.STREAM_EOF, new Object [0]));\r
+ }\r
+ skipBy = 1;\r
+ }\r
+ remainToSkip -= skipBy;\r
+ }\r
+ pos = skip;\r
+ } \r
+\r
+ /**\r
+ * Updates the stream if underlying clob is modified since\r
+ * this reader was created. \r
+ * If the stream is associated with a materialized clob, it \r
+ * checks if the underlying clob is updated since last read and \r
+ * updates itself if it is. If the stream is associated with \r
+ * non materialized clob and clob is materialized since last read it \r
+ * fetches the stream again and sets the position to current position.\r
+ * @throws IOException\r
+ */\r
+ private void updateIfRequired () throws IOException {\r
+ if (materialized) {\r
+ LOBInputStream lobStream = (LOBInputStream) stream;\r
+ if (lobStream.isObsolete()) {\r
+ lobStream.reInitialize();\r
+ init (lobStream, pos);\r
+ }\r
+ }\r
+ else {\r
+ //clob won't be null if the stream wasn't materialized\r
+ //but still try to be safe\r
+ if (SanityManager.DEBUG) {\r
+ SanityManager.ASSERT (!(clob == null), \r
+ "Internal error while updating stream");\r
+ }\r
+ if (clob.getInternalClob().isWritable()) {\r
+ try {\r
+ stream = clob.getInternalClob().getRawByteStream();\r
+ }\r
+ catch (SQLException e) {\r
+ IOException ioe = new IOException (e.getMessage());\r
+ ioe.initCause (e);\r
+ throw ioe;\r
+ }\r
+ init ((LOBInputStream) stream, pos);\r
+ materialized = true;\r
+ }\r
+ }\r
+ }\r
+}\r