001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    package org.apache.geronimo.javamail.store.imap.connection;
019    
020    import java.io.ByteArrayOutputStream;
021    
022    /**
023     * Simple extension to the ByteArrayOutputStream to allow inspection
024     * of the data while it is being accumulated.
025     */
026    public class IMAPResponseBuffer extends ByteArrayOutputStream {
027    
028        public IMAPResponseBuffer() {
029            super();
030        }
031    
032    
033        /**
034         * Read a character from the byte array output stream buffer
035         * at the give position.
036         *
037         * @param index  The requested index.
038         *
039         * @return The byte at the target index, or -1 if the index is out of
040         *         bounds.
041         */
042        public int read(int index) {
043            if (index >= size()) {
044                return -1;
045            }
046            return buf[index];
047        }
048    
049        /**
050         * Read a buffer of data from the output stream's accumulator
051         * buffer.  This will copy the data into a target byte arrain.
052         *
053         * @param buffer The target byte array for returning the data.
054         * @param offset The offset of the source data within the output stream buffer.
055         * @param length The desired length.
056         *
057         * @return The count of bytes transferred into the buffer.
058         */
059        public int read(byte[] buffer, int offset, int length) {
060    
061            int available = size() - offset;
062            length = Math.min(length, available);
063            // nothing to return?   quit now.
064            if (length <= 0) {
065                return 0;
066            }
067            System.arraycopy(buf, offset, buffer, 0, length);
068            return length;
069        }
070    
071        /**
072         * Search backwards through the buffer for a given byte.
073         *
074         * @param target The search character.
075         *
076         * @return The index relative to the buffer start of the given byte.
077         *         Returns -1 if not found.
078         */
079        public int lastIndex(byte target) {
080            for (int i = size() - 1; i > 0; i--) {
081                if (buf[i] == target) {
082                    return i;
083                }
084            }
085            return -1;
086        }
087    
088    
089        /**
090         * Return the last byte written to the output stream.  Returns
091         * -1 if the stream is empty.
092         *
093         * @return The last byte written (or -1 if the stream is empty).
094         */
095        public int lastByte() {
096            if (size() > 0) {
097                return buf[size() - 1];
098            }
099            return -1;
100        }
101    
102    
103        /**
104         * Retrieve an IMAP literal length value from the buffer.  We
105         * have a literal length value IFF the last characters written
106         * to the buffer have the form "{nnnn}".  This returns the
107         * integer value of the info inside the curly braces.  Returns -1
108         * if a valid literal length is not found.
109         *
110         * @return A literal length value, or -1 if we don't have a literal
111         *         signature at the end.
112         */
113        public int getLiteralLength() {
114            // was the last byte before the line break the close of the literal length?
115            if (lastByte() == '}') {
116                // locate the length start
117                int literalStart = lastIndex((byte)'{');
118                // no matching start, this can't be a literal.
119                if (literalStart == -1) {
120                    return -1;
121                }
122    
123                String lenString = new String(buf, literalStart + 1, size() - (literalStart + 2));
124                try {
125                    return Integer.parseInt(lenString);
126                } catch (NumberFormatException e) {
127                    e.printStackTrace(); 
128                }
129            }
130            // not a literal
131            return -1;
132        }
133    }
134