Adding JMCR-Stable version
[Benchmarks_CSolver.git] / JMCR-Stable / real-world application / derby-10.3.2.1 / java / engine / org / apache / derby / iapi / types / ReaderToUTF8Stream.java
diff --git a/JMCR-Stable/real-world application/derby-10.3.2.1/java/engine/org/apache/derby/iapi/types/ReaderToUTF8Stream.java b/JMCR-Stable/real-world application/derby-10.3.2.1/java/engine/org/apache/derby/iapi/types/ReaderToUTF8Stream.java
new file mode 100644 (file)
index 0000000..574933e
--- /dev/null
@@ -0,0 +1,412 @@
+/*\r
+\r
+   Derby - Class org.apache.derby.iapi.types.ReaderToUTF8Stream\r
+\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to you under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   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, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+\r
+ */\r
+\r
+package org.apache.derby.iapi.types;\r
+\r
+import java.io.InputStream;\r
+import java.io.IOException;\r
+import java.io.EOFException;\r
+import java.io.Reader;\r
+import java.io.UTFDataFormatException;\r
+import org.apache.derby.iapi.reference.SQLState;\r
+import org.apache.derby.iapi.services.i18n.MessageService;\r
+import org.apache.derby.iapi.services.io.DerbyIOException;\r
+import org.apache.derby.iapi.services.io.LimitReader;\r
+import org.apache.derby.iapi.types.TypeId;\r
+\r
+/**\r
+       Converts a java.io.Reader to the on-disk UTF8 format used by Derby\r
+    for character types.\r
+*/\r
+public final class ReaderToUTF8Stream\r
+       extends InputStream\r
+{\r
+    /**\r
+     * Application's reader wrapped in a LimitReader.\r
+     */\r
+       private LimitReader reader;\r
+\r
+       private byte[] buffer;\r
+       private int boff;\r
+       private int blen;\r
+       private boolean eof;\r
+       private boolean multipleBuffer;\r
+    // buffer to hold the data read from stream \r
+    // and converted to UTF8 format\r
+    private final static int BUFSIZE = 32768;\r
+    \r
+    /** Number of characters to truncate from this stream\r
+     The SQL standard allows for truncation of trailing spaces \r
+     for clobs,varchar,char.\r
+     If zero, no characters are truncated.\r
+     */\r
+    private final int charsToTruncate;\r
+    private static final char SPACE = ' ';\r
+    \r
+    /**\r
+     * Length of the final value, after truncation if any,\r
+     * in characters.\r
+     this stream needs to fit into a column of colWidth\r
+     if truncation error happens ,then the error message includes \r
+     information about the column width.\r
+    */\r
+    private final int valueLength; \r
+    /** The maximum allowed length of the stream. */\r
+    private final int maximumLength;\r
+    /** The type name for the column data is inserted into. */\r
+    private final String typeName;\r
+    \r
+    /**\r
+     * Create a stream that will truncate trailing blanks if required/allowed.\r
+     *\r
+     * If the stream must be truncated, the number of blanks to truncate\r
+     * is specified to allow the stream to be checked for exact length, as\r
+     * required by JDBC 3.0. If the stream is shorter or longer than specified,\r
+     * an exception is thrown during read.\r
+     *\r
+     * @param appReader application reader\r
+     * @param valueLength the length of the reader in characters\r
+     * @param numCharsToTruncate the number of trailing blanks to truncate\r
+     * @param typeName type name of the column data is inserted into\r
+     */\r
+    public ReaderToUTF8Stream(Reader appReader,\r
+                              int valueLength,\r
+                              int numCharsToTruncate,\r
+                              String typeName) {\r
+        this.reader = new LimitReader(appReader);\r
+        reader.setLimit(valueLength);\r
+        buffer = new byte[BUFSIZE];\r
+        blen = -1;        \r
+        this.charsToTruncate = numCharsToTruncate;\r
+        this.valueLength = valueLength;\r
+        this.maximumLength = -1;\r
+        this.typeName = typeName;\r
+    }\r
+\r
+    /**\r
+     * Create a UTF-8 stream for a length less application reader.\r
+     *\r
+     * A limit is placed on the length of the reader. If the reader exceeds\r
+     * the maximum length, truncation of trailing blanks is attempted. If\r
+     * truncation fails, an exception is thrown.\r
+     *\r
+     * @param appReader application reader\r
+     * @param maximumLength maximum allowed length in number of characters for\r
+     *      the reader\r
+     * @param typeName type name of the column data is inserted into\r
+     * @throws IllegalArgumentException if maximum length is negative, or type\r
+     *      name is <code>null<code>\r
+     */\r
+    public ReaderToUTF8Stream(Reader appReader,\r
+                              int maximumLength,\r
+                              String typeName) {\r
+        if (maximumLength < 0) {\r
+            throw new IllegalArgumentException("Maximum length for a capped " +\r
+                    "stream cannot be negative: " + maximumLength);\r
+        }\r
+        if (typeName == null) {\r
+            throw new IllegalArgumentException("Type name cannot be null");\r
+        }\r
+        this.reader = new LimitReader(appReader);\r
+        reader.setLimit(maximumLength);\r
+        buffer = new byte[BUFSIZE];\r
+        blen = -1;\r
+        this.maximumLength = maximumLength;\r
+        this.typeName = typeName;\r
+        this.charsToTruncate = -1;\r
+        this.valueLength = -1;\r
+    }\r
+\r
+    /**\r
+     * read from stream; characters converted to utf-8 derby specific encoding.\r
+     * If stream has been read, and eof reached, in that case any subsequent\r
+     * read will throw an EOFException\r
+     * @see java.io.InputStream#read()\r
+     */\r
+       public int read() throws IOException {\r
+\r
+        // when stream has been read and eof reached, stream is closed\r
+        // and buffer is set to null ( see close() method)\r
+        // since stream cannot be re-used, check if stream is closed and \r
+        // if so throw an EOFException\r
+        if ( buffer == null)\r
+            throw new EOFException(MessageService.getTextMessage(SQLState.STREAM_EOF));\r
+\r
+        \r
+               // first read\r
+               if (blen < 0)\r
+                       fillBuffer(2);\r
+\r
+               while (boff == blen)\r
+               {\r
+                       // reached end of buffer, read more?\r
+                       if (eof)\r
+            {\r
+               // we have reached the end of this stream\r
+               // cleanup here and return -1 indicating \r
+               // eof of stream\r
+               close();\r
+               return -1;\r
+            }\r
+                \r
+\r
+                       fillBuffer(0);\r
+               }\r
+\r
+               return buffer[boff++] & 0xff;\r
+\r
+       }\r
+\r
+       public int read(byte b[], int off, int len) throws IOException {\r
+        \r
+        // when stream has been read and eof reached, stream is closed\r
+        // and buffer is set to null ( see close() method)\r
+        // since stream cannot be re-used, check if stream is closed and \r
+        // if so throw an EOFException\r
+        if ( buffer == null )\r
+            throw new EOFException(MessageService.getTextMessage\r
+                    (SQLState.STREAM_EOF));\r
+\r
+        // first read\r
+               if (blen < 0)\r
+                       fillBuffer(2);\r
+\r
+               int readCount = 0;\r
+\r
+               while (len > 0)\r
+               {\r
+\r
+                       int copyBytes = blen - boff;\r
+\r
+                       // buffer empty?\r
+                       if (copyBytes == 0)\r
+                       {\r
+                               if (eof)\r
+                               {\r
+                    if (readCount > 0)\r
+                    {\r
+                        return readCount;\r
+                    }\r
+                    else\r
+                    {\r
+                        // we have reached the eof, so close the stream\r
+                        close();\r
+                        return -1;  \r
+                    }\r
+                    \r
+                               }\r
+                               fillBuffer(0);\r
+                               continue;\r
+                       }\r
+\r
+                       if (len < copyBytes)\r
+                               copyBytes = len;\r
+\r
+                       System.arraycopy(buffer, boff, b, off, copyBytes);\r
+                       boff += copyBytes;\r
+                       len -= copyBytes;\r
+                       readCount += copyBytes;\r
+            off += copyBytes;\r
+\r
+               }\r
+               return readCount;\r
+       }\r
+\r
+       private void fillBuffer(int startingOffset) throws IOException\r
+       {\r
+               int off = boff = startingOffset;\r
+\r
+               if (off == 0)\r
+                       multipleBuffer = true;\r
+\r
+               // 6! need to leave room for a three byte UTF8 encoding\r
+               // and 3 bytes for our special end of file marker.\r
+               for (; off <= buffer.length - 6; )\r
+               {\r
+                       int c = reader.read();\r
+                       if (c < 0) {\r
+                               eof = true;\r
+                               break;\r
+                       }\r
+\r
+                       if ((c >= 0x0001) && (c <= 0x007F))\r
+            {\r
+                               buffer[off++] = (byte) c;\r
+                       }\r
+            else if (c > 0x07FF)\r
+            {\r
+                               buffer[off++] = (byte) (0xE0 | ((c >> 12) & 0x0F));\r
+                               buffer[off++] = (byte) (0x80 | ((c >>  6) & 0x3F));\r
+                               buffer[off++] = (byte) (0x80 | ((c >>  0) & 0x3F));\r
+                       }\r
+            else\r
+            {\r
+                               buffer[off++] = (byte) (0xC0 | ((c >>  6) & 0x1F));\r
+                               buffer[off++] = (byte) (0x80 | ((c >>  0) & 0x3F));\r
+                       }\r
+               }\r
+\r
+               blen = off;\r
+               boff = 0;\r
+\r
+               if (eof)\r
+                       checkSufficientData();\r
+       }\r
+\r
+    /**\r
+     * Validate the length of the stream, take corrective action if allowed.\r
+     *\r
+     * JDBC 3.0 (from tutorial book) requires that an input stream has the\r
+     * correct number of bytes in the stream.\r
+     * If the stream is too long, trailing blank truncation is attempted if\r
+     * allowed. If truncation fails, or is disallowed, an exception is thrown.\r
+     *\r
+     * @throws IOException if an errors occurs in the application stream\r
+     * @throws DerbyIOException if Derby finds a problem with the stream;\r
+     *      stream is too long and cannot be truncated, or the stream length\r
+     *      does not match the specified length\r
+     */\r
+       private void checkSufficientData() throws IOException\r
+       {\r
+               // now that we finished reading from the stream; the amount\r
+        // of data that we can insert,start check for trailing spaces\r
+        if (charsToTruncate > 0)\r
+        {\r
+            reader.setLimit(charsToTruncate);\r
+            truncate();\r
+        }\r
+        \r
+        // A length less stream that is capped, will return 0 even if there\r
+        // are more bytes in the application stream.\r
+        int remainingBytes = reader.clearLimit();\r
+        if (remainingBytes > 0 && valueLength > 0) {\r
+            // If we had a specified length, throw exception.\r
+            throw new DerbyIOException(\r
+                    MessageService.getTextMessage(\r
+                        SQLState.SET_STREAM_INEXACT_LENGTH_DATA),\r
+                    SQLState.SET_STREAM_INEXACT_LENGTH_DATA);\r
+        }\r
+\r
+               // if we had a limit try reading one more character.\r
+               // JDBC 3.0 states the stream must have the correct number of\r
+        // characters in it.\r
+        if (remainingBytes == 0 && reader.read() >= 0) {\r
+            if (valueLength > -1) {\r
+                throw new DerbyIOException(\r
+                        MessageService.getTextMessage(\r
+                            SQLState.SET_STREAM_INEXACT_LENGTH_DATA),\r
+                        SQLState.SET_STREAM_INEXACT_LENGTH_DATA);\r
+            } else {\r
+                // Stream was capped (length less) and too long.\r
+                // Try to truncate if allowed, or else throw exception.\r
+                if (canTruncate()) {\r
+                    truncate();\r
+                } else {\r
+                    throw new DerbyIOException(\r
+                            MessageService.getTextMessage(\r
+                                SQLState.LANG_STRING_TRUNCATION),\r
+                            SQLState.LANG_STRING_TRUNCATION);\r
+                }\r
+            }\r
+        }\r
+               \r
+               // can put the correct length into the stream.\r
+               if (!multipleBuffer)\r
+               {\r
+                       int utflen = blen - 2;\r
+\r
+                       buffer[0] = (byte) ((utflen >>> 8) & 0xFF);\r
+                       buffer[1] = (byte) ((utflen >>> 0) & 0xFF);\r
+\r
+               }\r
+               else\r
+               {\r
+                       buffer[blen++] = (byte) 0xE0;\r
+                       buffer[blen++] = (byte) 0x00;\r
+                       buffer[blen++] = (byte) 0x00;\r
+               }\r
+       }\r
+\r
+    /**\r
+     * Determine if trailing blank truncation is allowed.\r
+     */\r
+    private boolean canTruncate() {\r
+        // Only a few types can be truncated, default is to not allow.\r
+        if (typeName.equals(TypeId.CLOB_NAME)) {\r
+            return true;\r
+        } else if (typeName.equals(TypeId.VARCHAR_NAME)) {\r
+            return true;\r
+        }\r
+        return false;\r
+    }\r
+\r
+    /**\r
+     * Attempt to truncate the stream by removing trailing blanks.\r
+     */\r
+    private void truncate()\r
+            throws IOException {\r
+        int c = 0;\r
+        for (;;) {\r
+            c = reader.read();\r
+\r
+            if (c < 0) {\r
+                break;\r
+            } else if (c != SPACE) {\r
+                throw new DerbyIOException(\r
+                    MessageService.getTextMessage(\r
+                        SQLState.LANG_STRING_TRUNCATION,\r
+                        typeName, \r
+                        "XXXX", \r
+                        String.valueOf(valueLength)),\r
+                    SQLState.LANG_STRING_TRUNCATION);\r
+            }\r
+        }\r
+    }\r
+\r
+    /**\r
+     * return resources \r
+     */\r
+       public void close() throws IOException\r
+       {\r
+        // since stream has been read and eof reached, return buffer back to \r
+        // the vm.\r
+        // Instead of using another variable to indicate stream is closed\r
+        // a check on (buffer==null) is used instead. \r
+        buffer = null;\r
+       }\r
+\r
+    /**\r
+     * Return an optimized version of bytes available to read from \r
+     * the stream \r
+     * Note, it is not exactly per java.io.InputStream#available()\r
+     */\r
+    public final int available()\r
+    {\r
+       int remainingBytes = reader.getLimit();\r
+       // this object buffers BUFSIZE bytes that can be read \r
+       // and when that is finished it reads the next available bytes\r
+       // from the reader object \r
+       // reader.getLimit() returns the remaining bytes available\r
+       // on this stream\r
+       return (BUFSIZE > remainingBytes ? remainingBytes : BUFSIZE);\r
+    }\r
+  }\r
+\r