001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements.  See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership.  The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the
007     * "License"); you may not use this file except in compliance
008     * with the License.  You may obtain a copy of the License at
009     *
010     *  http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing,
013     * software distributed under the License is distributed on an
014     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     * KIND, either express or implied.  See the License for the
016     * specific language governing permissions and limitations
017     * under the License.
018     */
019    
020    package org.apache.geronimo.mail.util;
021    
022    import java.io.IOException;
023    import java.io.OutputStream;
024    
025    public class XTextEncoder
026        implements Encoder
027    {
028        protected final byte[] encodingTable =
029            {
030                (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6', (byte)'7',
031                (byte)'8', (byte)'9', (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F'
032            };
033    
034        /*
035         * set up the decoding table.
036         */
037        protected final byte[] decodingTable = new byte[128];
038    
039        protected void initialiseDecodingTable()
040        {
041            for (int i = 0; i < encodingTable.length; i++)
042            {
043                decodingTable[encodingTable[i]] = (byte)i;
044            }
045        }
046    
047        public XTextEncoder()
048        {
049            initialiseDecodingTable();
050        }
051    
052        /**
053         * encode the input data producing an XText output stream.
054         *
055         * @return the number of bytes produced.
056         */
057        public int encode(
058            byte[]                data,
059            int                    off,
060            int                    length,
061            OutputStream    out)
062            throws IOException
063        {
064            int bytesWritten = 0;
065    
066            for (int i = off; i < (off + length); i++)
067            {
068                int    v = data[i] & 0xff;
069                // character tha must be encoded?  Prefix with a '+' and encode in hex.
070                if (v < 33 || v > 126 || v == '+' || v == '+') {
071                    out.write((byte)'+');
072                    out.write(encodingTable[(v >>> 4)]);
073                    out.write(encodingTable[v & 0xf]);
074                    bytesWritten += 3;
075                }
076                else {
077                    // add unchanged.
078                    out.write((byte)v);
079                    bytesWritten++;
080                }
081            }
082    
083            return bytesWritten;
084        }
085    
086    
087        /**
088         * decode the xtext encoded byte data writing it to the given output stream
089         *
090         * @return the number of bytes produced.
091         */
092        public int decode(
093            byte[]                data,
094            int                    off,
095            int                    length,
096            OutputStream    out)
097            throws IOException
098        {
099            byte[]    bytes;
100            byte    b1, b2;
101            int        outLen = 0;
102    
103            int        end = off + length;
104    
105            int i = off;
106            while (i < end)
107            {
108                byte v = data[i++];
109                // a plus is a hex character marker, need to decode a hex value.
110                if (v == '+') {
111                    b1 = decodingTable[data[i++]];
112                    b2 = decodingTable[data[i++]];
113                    out.write((b1 << 4) | b2);
114                }
115                else {
116                    // copied over unchanged.
117                    out.write(v);
118                }
119                // always just one byte added
120                outLen++;
121            }
122    
123            return outLen;
124        }
125    
126        /**
127         * decode the xtext encoded String data writing it to the given output stream.
128         *
129         * @return the number of bytes produced.
130         */
131        public int decode(
132            String                data,
133            OutputStream    out)
134            throws IOException
135        {
136            byte[]    bytes;
137            byte    b1, b2, b3, b4;
138            int        length = 0;
139    
140            int        end = data.length();
141    
142            int i = 0;
143            while (i < end)
144            {
145                char v = data.charAt(i++);
146                if (v == '+') {
147                    b1 = decodingTable[data.charAt(i++)];
148                    b2 = decodingTable[data.charAt(i++)];
149    
150                    out.write((b1 << 4) | b2);
151                }
152                else {
153                    out.write((byte)v);
154                }
155                length++;
156            }
157    
158            return length;
159        }
160    }
161