1 /* 2 * Copyright (c) 1995, 2005, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package es.accv.arangi.base.util.base64; 26 27 import java.io.InputStream; 28 import java.io.ByteArrayInputStream; 29 import java.io.OutputStream; 30 import java.io.ByteArrayOutputStream; 31 import java.io.PrintStream; 32 import java.io.IOException; 33 import java.nio.ByteBuffer; 34 35 36 /** 37 * This class defines the encoding half of character encoders. 38 * A character encoder is an algorithim for transforming 8 bit binary 39 * data into text (generally 7 bit ASCII or 8 bit ISO-Latin-1 text) 40 * for transmition over text channels such as e-mail and network news. 41 * 42 * The character encoders have been structured around a central theme 43 * that, in general, the encoded text has the form: 44 * 45 * <pre> 46 * [Buffer Prefix] 47 * [Line Prefix][encoded data atoms][Line Suffix] 48 * [Buffer Suffix] 49 * </pre> 50 * 51 * In the CharacterEncoder and CharacterDecoder classes, one complete 52 * chunk of data is referred to as a <i>buffer</i>. Encoded buffers 53 * are all text, and decoded buffers (sometimes just referred to as 54 * buffers) are binary octets. 55 * 56 * To create a custom encoder, you must, at a minimum, overide three 57 * abstract methods in this class. 58 * <DL> 59 * <DD>bytesPerAtom which tells the encoder how many bytes to 60 * send to encodeAtom 61 * <DD>encodeAtom which encodes the bytes sent to it as text. 62 * <DD>bytesPerLine which tells the encoder the maximum number of 63 * bytes per line. 64 * </DL> 65 * 66 * Several useful encoders have already been written and are 67 * referenced in the See Also list below. 68 * 69 * @author Chuck McManis 70 * @see CharacterDecoder; 71 * @see UCEncoder 72 * @see UUEncoder 73 * @see BASE64Encoder 74 */ 75 public abstract class CharacterEncoder { 76 77 /** Stream that understands "printing" */ 78 protected PrintStream pStream; 79 80 /** Return the number of bytes per atom of encoding */ 81 abstract protected int bytesPerAtom(); 82 83 /** Return the number of bytes that can be encoded per line */ 84 abstract protected int bytesPerLine(); 85 86 /** 87 * Encode the prefix for the entire buffer. By default is simply 88 * opens the PrintStream for use by the other functions. 89 */ 90 protected void encodeBufferPrefix(OutputStream aStream) throws IOException { 91 pStream = new PrintStream(aStream); 92 } 93 94 /** 95 * Encode the suffix for the entire buffer. 96 */ 97 protected void encodeBufferSuffix(OutputStream aStream) throws IOException { 98 } 99 100 /** 101 * Encode the prefix that starts every output line. 102 */ 103 protected void encodeLinePrefix(OutputStream aStream, int aLength) 104 throws IOException { 105 } 106 107 /** 108 * Encode the suffix that ends every output line. By default 109 * this method just prints a <newline> into the output stream. 110 */ 111 protected void encodeLineSuffix(OutputStream aStream) throws IOException { 112 pStream.println(); 113 } 114 115 /** Encode one "atom" of information into characters. */ 116 abstract protected void encodeAtom(OutputStream aStream, byte someBytes[], 117 int anOffset, int aLength) throws IOException; 118 119 /** 120 * This method works around the bizarre semantics of BufferedInputStream's 121 * read method. 122 */ 123 protected int readFully(InputStream in, byte buffer[]) 124 throws java.io.IOException { 125 for (int i = 0; i < buffer.length; i++) { 126 int q = in.read(); 127 if (q == -1) 128 return i; 129 buffer[i] = (byte)q; 130 } 131 return buffer.length; 132 } 133 134 /** 135 * Encode bytes from the input stream, and write them as text characters 136 * to the output stream. This method will run until it exhausts the 137 * input stream, but does not print the line suffix for a final 138 * line that is shorter than bytesPerLine(). 139 */ 140 public void encode(InputStream inStream, OutputStream outStream) 141 throws IOException { 142 int j; 143 int numBytes; 144 byte tmpbuffer[] = new byte[bytesPerLine()]; 145 146 encodeBufferPrefix(outStream); 147 148 while (true) { 149 numBytes = readFully(inStream, tmpbuffer); 150 if (numBytes == 0) { 151 break; 152 } 153 encodeLinePrefix(outStream, numBytes); 154 for (j = 0; j < numBytes; j += bytesPerAtom()) { 155 156 if ((j + bytesPerAtom()) <= numBytes) { 157 encodeAtom(outStream, tmpbuffer, j, bytesPerAtom()); 158 } else { 159 encodeAtom(outStream, tmpbuffer, j, (numBytes)- j); 160 } 161 } 162 if (numBytes < bytesPerLine()) { 163 break; 164 } else { 165 encodeLineSuffix(outStream); 166 } 167 } 168 encodeBufferSuffix(outStream); 169 } 170 171 /** 172 * Encode the buffer in <i>aBuffer</i> and write the encoded 173 * result to the OutputStream <i>aStream</i>. 174 */ 175 public void encode(byte aBuffer[], OutputStream aStream) 176 throws IOException { 177 ByteArrayInputStream inStream = new ByteArrayInputStream(aBuffer); 178 encode(inStream, aStream); 179 } 180 181 /** 182 * A 'streamless' version of encode that simply takes a buffer of 183 * bytes and returns a string containing the encoded buffer. 184 */ 185 public String encode(byte aBuffer[]) { 186 ByteArrayOutputStream outStream = new ByteArrayOutputStream(); 187 ByteArrayInputStream inStream = new ByteArrayInputStream(aBuffer); 188 String retVal = null; 189 try { 190 encode(inStream, outStream); 191 // explicit ascii->unicode conversion 192 retVal = outStream.toString("8859_1"); 193 } catch (Exception IOException) { 194 // This should never happen. 195 throw new Error("CharacterEncoder.encode internal error"); 196 } 197 return (retVal); 198 } 199 200 /** 201 * Return a byte array from the remaining bytes in this ByteBuffer. 202 * <P> 203 * The ByteBuffer's position will be advanced to ByteBuffer's limit. 204 * <P> 205 * To avoid an extra copy, the implementation will attempt to return the 206 * byte array backing the ByteBuffer. If this is not possible, a 207 * new byte array will be created. 208 */ 209 private byte [] getBytes(ByteBuffer bb) { 210 /* 211 * This should never return a BufferOverflowException, as we're 212 * careful to allocate just the right amount. 213 */ 214 byte [] buf = null; 215 216 /* 217 * If it has a usable backing byte buffer, use it. Use only 218 * if the array exactly represents the current ByteBuffer. 219 */ 220 if (bb.hasArray()) { 221 byte [] tmp = bb.array(); 222 if ((tmp.length == bb.capacity()) && 223 (tmp.length == bb.remaining())) { 224 buf = tmp; 225 bb.position(bb.limit()); 226 } 227 } 228 229 if (buf == null) { 230 /* 231 * This class doesn't have a concept of encode(buf, len, off), 232 * so if we have a partial buffer, we must reallocate 233 * space. 234 */ 235 buf = new byte[bb.remaining()]; 236 237 /* 238 * position() automatically updated 239 */ 240 bb.get(buf); 241 } 242 243 return buf; 244 } 245 246 /** 247 * Encode the <i>aBuffer</i> ByteBuffer and write the encoded 248 * result to the OutputStream <i>aStream</i>. 249 * <P> 250 * The ByteBuffer's position will be advanced to ByteBuffer's limit. 251 */ 252 public void encode(ByteBuffer aBuffer, OutputStream aStream) 253 throws IOException { 254 byte [] buf = getBytes(aBuffer); 255 encode(buf, aStream); 256 } 257 258 /** 259 * A 'streamless' version of encode that simply takes a ByteBuffer 260 * and returns a string containing the encoded buffer. 261 * <P> 262 * The ByteBuffer's position will be advanced to ByteBuffer's limit. 263 */ 264 public String encode(ByteBuffer aBuffer) { 265 byte [] buf = getBytes(aBuffer); 266 return encode(buf); 267 } 268 269 /** 270 * Encode bytes from the input stream, and write them as text characters 271 * to the output stream. This method will run until it exhausts the 272 * input stream. It differs from encode in that it will add the 273 * line at the end of a final line that is shorter than bytesPerLine(). 274 */ 275 public void encodeBuffer(InputStream inStream, OutputStream outStream) 276 throws IOException { 277 int j; 278 int numBytes; 279 byte tmpbuffer[] = new byte[bytesPerLine()]; 280 281 encodeBufferPrefix(outStream); 282 283 while (true) { 284 numBytes = readFully(inStream, tmpbuffer); 285 if (numBytes == 0) { 286 break; 287 } 288 encodeLinePrefix(outStream, numBytes); 289 for (j = 0; j < numBytes; j += bytesPerAtom()) { 290 if ((j + bytesPerAtom()) <= numBytes) { 291 encodeAtom(outStream, tmpbuffer, j, bytesPerAtom()); 292 } else { 293 encodeAtom(outStream, tmpbuffer, j, (numBytes)- j); 294 } 295 } 296 encodeLineSuffix(outStream); 297 if (numBytes < bytesPerLine()) { 298 break; 299 } 300 } 301 encodeBufferSuffix(outStream); 302 } 303 304 /** 305 * Encode the buffer in <i>aBuffer</i> and write the encoded 306 * result to the OutputStream <i>aStream</i>. 307 */ 308 public void encodeBuffer(byte aBuffer[], OutputStream aStream) 309 throws IOException { 310 ByteArrayInputStream inStream = new ByteArrayInputStream(aBuffer); 311 encodeBuffer(inStream, aStream); 312 } 313 314 /** 315 * A 'streamless' version of encode that simply takes a buffer of 316 * bytes and returns a string containing the encoded buffer. 317 */ 318 public String encodeBuffer(byte aBuffer[]) { 319 ByteArrayOutputStream outStream = new ByteArrayOutputStream(); 320 ByteArrayInputStream inStream = new ByteArrayInputStream(aBuffer); 321 try { 322 encodeBuffer(inStream, outStream); 323 } catch (Exception IOException) { 324 // This should never happen. 325 throw new Error("CharacterEncoder.encodeBuffer internal error"); 326 } 327 return (outStream.toString()); 328 } 329 330 /** 331 * Encode the <i>aBuffer</i> ByteBuffer and write the encoded 332 * result to the OutputStream <i>aStream</i>. 333 * <P> 334 * The ByteBuffer's position will be advanced to ByteBuffer's limit. 335 */ 336 public void encodeBuffer(ByteBuffer aBuffer, OutputStream aStream) 337 throws IOException { 338 byte [] buf = getBytes(aBuffer); 339 encodeBuffer(buf, aStream); 340 } 341 342 /** 343 * A 'streamless' version of encode that simply takes a ByteBuffer 344 * and returns a string containing the encoded buffer. 345 * <P> 346 * The ByteBuffer's position will be advanced to ByteBuffer's limit. 347 */ 348 public String encodeBuffer(ByteBuffer aBuffer) { 349 byte [] buf = getBytes(aBuffer); 350 return encodeBuffer(buf); 351 } 352 353 }