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