You are viewing a plain text version of this content. The canonical link for it is here.
Posted to jaxme-dev@ws.apache.org by jo...@apache.org on 2007/01/30 17:11:22 UTC
svn commit: r501438 - in /webservices/jaxme/trunk/ws-jaxme:
jaxme-jm/src/main/java/org/apache/ws/jaxme/util/Base64Binary.java status.xml
Author: jochen
Date: Tue Jan 30 08:11:21 2007
New Revision: 501438
URL: http://svn.apache.org/viewvc?view=rev&rev=501438
Log:
The base64 handling was dependent on the sun.* classes, which are
locked in late Java versions.
Modified:
webservices/jaxme/trunk/ws-jaxme/jaxme-jm/src/main/java/org/apache/ws/jaxme/util/Base64Binary.java
webservices/jaxme/trunk/ws-jaxme/status.xml
Modified: webservices/jaxme/trunk/ws-jaxme/jaxme-jm/src/main/java/org/apache/ws/jaxme/util/Base64Binary.java
URL: http://svn.apache.org/viewvc/webservices/jaxme/trunk/ws-jaxme/jaxme-jm/src/main/java/org/apache/ws/jaxme/util/Base64Binary.java?view=diff&rev=501438&r1=501437&r2=501438
==============================================================================
--- webservices/jaxme/trunk/ws-jaxme/jaxme-jm/src/main/java/org/apache/ws/jaxme/util/Base64Binary.java (original)
+++ webservices/jaxme/trunk/ws-jaxme/jaxme-jm/src/main/java/org/apache/ws/jaxme/util/Base64Binary.java Tue Jan 30 08:11:21 2007
@@ -16,11 +16,15 @@
*/
package org.apache.ws.jaxme.util;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import java.io.OutputStream;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.lang.reflect.UndeclaredThrowableException;
-import sun.misc.BASE64Decoder;
-import sun.misc.BASE64Encoder;
-
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
/** A utility class for working with base64 encoding.
*/
@@ -33,17 +37,508 @@
return result;
}
- /** Converts the string <code>pValue</code> into a
- * base64 encoded byte array.
+ /** An exception of this type is thrown, if the decoded
+ * character stream contains invalid input.
+ */
+ public static class DecodingException extends IOException {
+ private static final long serialVersionUID = 3257006574836135478L;
+ DecodingException(String pMessage) { super(pMessage); }
+ }
+
+ /** An exception of this type is thrown by the {@link SAXEncoder},
+ * if writing to the target handler causes a SAX exception.
+ * This class is required, because the {@link IOException}
+ * allows no cause until Java 1.3.
+ */
+ public static class SAXIOException extends IOException {
+ private static final long serialVersionUID = 3258131345216451895L;
+ final SAXException saxException;
+ SAXIOException(SAXException e) {
+ super();
+ saxException = e;
+ }
+ /** Returns the encapsulated {@link SAXException}.
+ * @return An exception, which was thrown when invoking
+ * {@link ContentHandler#characters(char[], int, int)}.
+ */
+ public SAXException getSAXException() { return saxException; }
+ }
+
+
+ /** Default line separator: \n
+ */
+ public static final String LINE_SEPARATOR = "\n";
+
+ /** Default size for line wrapping.
+ */
+ public static final int LINE_SIZE = 0;
+
+ /**
+ * This array is a lookup table that translates 6-bit positive integer
+ * index values into their "Base64 Alphabet" equivalents as specified
+ * in Table 1 of RFC 2045.
+ */
+ static final char intToBase64[] = {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
+ 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
+ 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
+ };
+
+ /**
+ * This array is a lookup table that translates unicode characters
+ * drawn from the "Base64 Alphabet" (as specified in Table 1 of RFC 2045)
+ * into their 6-bit positive integer equivalents. Characters that
+ * are not in the Base64 alphabet but fall within the bounds of the
+ * array are translated to -1.
+ */
+ static final byte base64ToInt[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54,
+ 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34,
+ 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
+ };
+
+ /** An encoder is an object, which is able to encode byte array
+ * in blocks of three bytes. Any such block is converted into an
+ * array of four bytes.
+ */
+ public static abstract class Encoder {
+ private int num, numBytes;
+ private final char[] charBuffer;
+ private int charOffset;
+ private final int wrapSize;
+ private final int skipChars;
+ private final String sep;
+ private int lineChars = 0;
+ /** Creates a new instance.
+ * @param pBuffer The encoders buffer. The encoder will
+ * write to the buffer as long as possible. If the
+ * buffer is full or the end of data is signaled, then
+ * the method {@link #writeBuffer(char[], int, int)}
+ * will be invoked.
+ * @param pWrapSize A nonzero value indicates, that a line
+ * wrap should be performed after the given number of
+ * characters. The value must be a multiple of 4. Zero
+ * indicates, that no line wrap should be performed.
+ * @param pSep The eol sequence being used to terminate
+ * a line in case of line wraps. May be null, in which
+ * case the default value {@link Base64#LINE_SEPARATOR}
+ * is being used.
+ */
+ protected Encoder(char[] pBuffer, int pWrapSize, String pSep) {
+ charBuffer = pBuffer;
+ sep = pSep == null ? null : LINE_SEPARATOR;
+ skipChars = pWrapSize == 0 ? 4 : 4 + sep.length();
+ wrapSize = skipChars == 4 ? 0 : pWrapSize;
+ if (wrapSize < 0 || wrapSize %4 > 0) {
+ throw new IllegalArgumentException("Illegal argument for wrap size: " + pWrapSize
+ + "(Expected nonnegative multiple of 4)");
+ }
+ if (pBuffer.length < skipChars) {
+ throw new IllegalArgumentException("The buffer must contain at least " + skipChars
+ + " characters, but has " + pBuffer.length);
+ }
+ }
+ /** Called for writing the buffer contents to the target.
+ * @param pChars The buffer being written.
+ * @param pOffset Offset of first character being written.
+ * @param pLen Number of characters being written.
+ * @throws IOException Writing to the destination failed.
+ */
+ protected abstract void writeBuffer(char[] pChars, int pOffset, int pLen) throws IOException;
+
+ private void wrap() {
+ for (int j = 0; j < sep.length(); j++) {
+ charBuffer[charOffset++] = sep.charAt(j);
+ }
+ lineChars = 0;
+ }
+
+ /** Encodes the given byte array.
+ * @param pBuffer Byte array being encoded.
+ * @param pOffset Offset of first byte being encoded.
+ * @param pLen Number of bytes being encoded.
+ * @throws IOException Invoking the {@link #writeBuffer(char[],int,int)} method
+ * for writing the encoded data failed.
+ */
+ public void write(byte[] pBuffer, int pOffset, int pLen) throws IOException {
+ int offset = pOffset;
+ for(int i = 0; i < pLen; i++) {
+ int b = pBuffer[offset++];
+ if (b < 0) { b += 256; }
+ num = (num << 8) + b;
+ if (++numBytes == 3) {
+ charBuffer[charOffset++] = intToBase64[num >> 18];
+ charBuffer[charOffset++] = intToBase64[(num >> 12) & 0x3f];
+ charBuffer[charOffset++] = intToBase64[(num >> 6) & 0x3f];
+ charBuffer[charOffset++] = intToBase64[num & 0x3f];
+ if (wrapSize > 0) {
+ lineChars += 4;
+ if (lineChars >= wrapSize) {
+ wrap();
+ }
+ }
+ num = 0;
+ numBytes = 0;
+ if (charOffset + skipChars > charBuffer.length) {
+ writeBuffer(charBuffer, 0, charOffset);
+ charOffset = 0;
+ }
+ }
+ }
+ }
+ /** Writes any currently buffered data to the destination.
+ * @throws IOException Invoking the {@link #writeBuffer(char[],int,int)}
+ * method for writing the encoded data failed.
+ */
+ public void flush() throws IOException {
+ if (numBytes > 0) {
+ if (numBytes == 1) {
+ charBuffer[charOffset++] = intToBase64[num >> 2];
+ charBuffer[charOffset++] = intToBase64[(num << 4) & 0x3f];
+ charBuffer[charOffset++] = '=';
+ charBuffer[charOffset++] = '=';
+ } else {
+ charBuffer[charOffset++] = intToBase64[num >> 10];
+ charBuffer[charOffset++] = intToBase64[(num >> 4) & 0x3f];
+ charBuffer[charOffset++] = intToBase64[(num << 2) & 0x3f];
+ charBuffer[charOffset++] = '=';
+ }
+ lineChars += 4;
+ num = 0;
+ numBytes = 0;
+ }
+ if (wrapSize > 0 && lineChars > 0) {
+ wrap();
+ }
+ if (charOffset > 0) {
+ writeBuffer(charBuffer, 0, charOffset);
+ charOffset = 0;
+ }
+ }
+ }
+
+ /** An {@link OutputStream}, which is writing to the given
+ * {@link Encoder}.
+ */
+ public static class EncoderOutputStream extends OutputStream {
+ private final Encoder encoder;
+ /** Creates a new instance, which is creating
+ * output using the given {@link Encoder}.
+ * @param pEncoder The base64 encoder being used.
+ */
+ public EncoderOutputStream(Encoder pEncoder) {
+ encoder = pEncoder;
+ }
+ private final byte[] oneByte = new byte[1];
+ public void write(int b) throws IOException {
+ oneByte[0] = (byte) b;
+ encoder.write(oneByte, 0, 1);
+ }
+ public void write(byte[] pBuffer, int pOffset, int pLen) throws IOException {
+ encoder.write(pBuffer, pOffset, pLen);
+ }
+ public void close() throws IOException {
+ encoder.flush();
+ }
+ }
+
+ /** Returns an {@link OutputStream}, that encodes its input in Base64
+ * and writes it to the given {@link Writer}. If the Base64 stream
+ * ends, then the output streams {@link OutputStream#close()} method
+ * must be invoked. Note, that this will <em>not</em> close the
+ * target {@link Writer}!
+ * @param pWriter Target writer.
+ * @return An output stream, encoding its input in Base64 and writing
+ * the output to the writer <code>pWriter</code>.
+ */
+ public static OutputStream newEncoder(Writer pWriter) {
+ return newEncoder(pWriter, LINE_SIZE, LINE_SEPARATOR);
+ }
+
+ /** Returns an {@link OutputStream}, that encodes its input in Base64
+ * and writes it to the given {@link Writer}. If the Base64 stream
+ * ends, then the output streams {@link OutputStream#close()} method
+ * must be invoked. Note, that this will <em>not</em> close the
+ * target {@link Writer}!
+ * @param pWriter Target writer.
+ * @param pLineSize Size of one line in characters, must be a multiple
+ * of four. Zero indicates, that no line wrapping should occur.
+ * @param pSeparator Line separator or null, in which case the default value
+ * {@link #LINE_SEPARATOR} is used.
+ * @return An output stream, encoding its input in Base64 and writing
+ * the output to the writer <code>pWriter</code>.
+ */
+ public static OutputStream newEncoder(final Writer pWriter, int pLineSize, String pSeparator) {
+ final Encoder encoder = new Encoder(new char[4096], pLineSize, pSeparator){
+ protected void writeBuffer(char[] pBuffer, int pOffset, int pLen) throws IOException {
+ pWriter.write(pBuffer, pOffset, pLen);
+ }
+ };
+ return new EncoderOutputStream(encoder);
+ }
+
+ /** An {@link Encoder}, which is writing to a SAX content handler.
+ * This is typically used for embedding a base64 stream into an
+ * XML document.
+ */
+ public static class SAXEncoder extends Encoder {
+ private final ContentHandler handler;
+ /** Creates a new instance.
+ * @param pBuffer The encoders buffer.
+ * @param pWrapSize A nonzero value indicates, that a line
+ * wrap should be performed after the given number of
+ * characters. The value must be a multiple of 4. Zero
+ * indicates, that no line wrap should be performed.
+ * @param pSep The eol sequence being used to terminate
+ * a line in case of line wraps. May be null, in which
+ * case the default value {@link Base64#LINE_SEPARATOR}
+ * is being used.
+ * @param pHandler The target handler.
+ */
+ public SAXEncoder(char[] pBuffer, int pWrapSize, String pSep,
+ ContentHandler pHandler) {
+ super(pBuffer, pWrapSize, pSep);
+ handler = pHandler;
+ }
+ /** Writes to the content handler.
+ * @throws SAXIOException Writing to the content handler
+ * caused a SAXException.
+ */
+ protected void writeBuffer(char[] pChars, int pOffset, int pLen) throws IOException {
+ try {
+ handler.characters(pChars, pOffset, pLen);
+ } catch (SAXException e) {
+ throw new SAXIOException(e);
+ }
+ }
+ }
+
+ /** Converts the given byte array into a base64 encoded character
+ * array.
+ * @param pBuffer The buffer being encoded.
+ * @param pOffset Offset in buffer, where to begin encoding.
+ * @param pLength Number of bytes being encoded.
+ * @return Character array of encoded bytes.
+ */
+ public static String encode(byte[] pBuffer, int pOffset, int pLength) {
+ return encode(pBuffer, pOffset, pLength, LINE_SIZE, LINE_SEPARATOR);
+ }
+
+ /** Converts the given byte array into a base64 encoded character
+ * array.
+ * @param pBuffer The buffer being encoded.
+ * @param pOffset Offset in buffer, where to begin encoding.
+ * @param pLength Number of bytes being encoded.
+ * @param pLineSize Size of one line in characters, must be a multiple
+ * of four. Zero indicates, that no line wrapping should occur.
+ * @param pSeparator Line separator or null, in which case the default value
+ * {@link #LINE_SEPARATOR} is used.
+ * @return Character array of encoded bytes.
+ */
+ public static String encode(byte[] pBuffer, int pOffset, int pLength,
+ int pLineSize, String pSeparator) {
+ StringWriter sw = new StringWriter();
+ OutputStream ostream = newEncoder(sw, pLineSize, pSeparator);
+ try {
+ ostream.write(pBuffer, pOffset, pLength);
+ ostream.close();
+ } catch (IOException e) {
+ throw new UndeclaredThrowableException(e);
+ }
+ return sw.toString();
+ }
+
+ /** Converts the given byte array into a base64 encoded character
+ * array with the line size {@link #LINE_SIZE} and the separator
+ * {@link #LINE_SEPARATOR}.
+ * @param pBuffer The buffer being encoded.
+ * @return Character array of encoded bytes.
+ */
+ public static String encode(byte[] pBuffer) {
+ return encode(pBuffer, 0, pBuffer.length);
+ }
+
+ /** An encoder is an object, which is able to decode char arrays
+ * in blocks of four bytes. Any such block is converted into a
+ * array of three bytes.
+ */
+ public static abstract class Decoder {
+ private final byte[] byteBuffer;
+ private int byteBufferOffset;
+ private int num, numBytes;
+ private int eofBytes;
+ /** Creates a new instance.
+ * @param pBufLen The decoders buffer size. The decoder will
+ * store up to this number of decoded bytes before invoking
+ * {@link #writeBuffer(byte[],int,int)}.
+ */
+ protected Decoder(int pBufLen) {
+ byteBuffer = new byte[pBufLen];
+ }
+ /** Called for writing the decoded bytes to the destination.
+ * @param pBuffer The byte array being written.
+ * @param pOffset Offset of the first byte being written.
+ * @param pLen Number of bytes being written.
+ * @throws IOException Writing to the destination failed.
+ */
+ protected abstract void writeBuffer(byte[] pBuffer, int pOffset, int pLen) throws IOException;
+ /** Converts the Base64 encoded character array.
+ * @param pData The character array being decoded.
+ * @param pOffset Offset of first character being decoded.
+ * @param pLen Number of characters being decoded.
+ * @throws DecodingException Decoding failed.
+ * @throws IOException An invocation of the {@link #writeBuffer(byte[],int,int)}
+ * method failed.
+ */
+ public void write(char[] pData, int pOffset, int pLen) throws IOException {
+ int offset = pOffset;
+ for (int i = 0; i < pLen; i++) {
+ char c = pData[offset++];
+ if (Character.isWhitespace(c)) {
+ continue;
+ }
+ if (c == '=') {
+ ++eofBytes;
+ num = num << 6;
+ switch(++numBytes) {
+ case 1:
+ case 2:
+ throw new DecodingException("Unexpected end of stream character (=)");
+ case 3:
+ // Wait for the next '='
+ break;
+ case 4:
+ byteBuffer[byteBufferOffset++] = (byte) (num >> 16);
+ if (eofBytes == 1) {
+ byteBuffer[byteBufferOffset++] = (byte) (num >> 8);
+ }
+ writeBuffer(byteBuffer, 0, byteBufferOffset);
+ byteBufferOffset = 0;
+ break;
+ case 5:
+ throw new DecodingException("Trailing garbage detected");
+ default:
+ throw new IllegalStateException("Invalid value for numBytes");
+ }
+ } else {
+ if (eofBytes > 0) {
+ throw new DecodingException("Base64 characters after end of stream character (=) detected.");
+ }
+ int result;
+ if (c >= 0 && c < base64ToInt.length) {
+ result = base64ToInt[c];
+ if (result >= 0) {
+ num = (num << 6) + result;
+ if (++numBytes == 4) {
+ byteBuffer[byteBufferOffset++] = (byte) (num >> 16);
+ byteBuffer[byteBufferOffset++] = (byte) ((num >> 8) & 0xff);
+ byteBuffer[byteBufferOffset++] = (byte) (num & 0xff);
+ if (byteBufferOffset + 3 > byteBuffer.length) {
+ writeBuffer(byteBuffer, 0, byteBufferOffset);
+ byteBufferOffset = 0;
+ }
+ num = 0;
+ numBytes = 0;
+ }
+ continue;
+ }
+ }
+ if (!Character.isWhitespace(c)) {
+ throw new DecodingException("Invalid Base64 character: " + (int) c);
+ }
+ }
+ }
+ }
+ /** Indicates, that no more data is being expected. Writes all currently
+ * buffered data to the destination by invoking {@link #writeBuffer(byte[],int,int)}.
+ * @throws DecodingException Decoding failed (Unexpected end of file).
+ * @throws IOException An invocation of the {@link #writeBuffer(byte[],int,int)} method failed.
+ */
+ public void flush() throws IOException {
+ if (numBytes != 0 && numBytes != 4) {
+ throw new DecodingException("Unexpected end of file");
+ }
+ if (byteBufferOffset > 0) {
+ writeBuffer(byteBuffer, 0, byteBufferOffset);
+ byteBufferOffset = 0;
+ }
+ }
+ }
+
+ /** Returns a {@link Writer}, that decodes its Base64 encoded
+ * input and writes it to the given {@link OutputStream}.
+ * Note, that the writers {@link Writer#close()} method will
+ * <em>not</em> close the output stream <code>pStream</code>!
+ * @param pStream Target output stream.
+ * @return An output stream, encoding its input in Base64 and writing
+ * the output to the writer <code>pWriter</code>.
+ */
+ public static Writer newDecoder(final OutputStream pStream) {
+ return new Writer(){
+ private final Decoder decoder = new Decoder(1024){
+ protected void writeBuffer(byte[] pBytes, int pOffset, int pLen) throws IOException {
+ pStream.write(pBytes, pOffset, pLen);
+ }
+ };
+ public void close() throws IOException {
+ flush();
+ }
+ public void flush() throws IOException {
+ decoder.flush();
+ pStream.flush();
+ }
+ public void write(char[] cbuf, int off, int len) throws IOException {
+ decoder.write(cbuf, off, len);
+ }
+ };
+ }
+
+ /** Converts the given base64 encoded character buffer into a byte array.
+ * @param pBuffer The character buffer being decoded.
+ * @param pOffset Offset of first character being decoded.
+ * @param pLength Number of characters being decoded.
+ * @return Converted byte array
+ * @throws DecodingException The input character stream contained invalid data.
+ */
+ public static byte[] decode(char[] pBuffer, int pOffset, int pLength) throws DecodingException {
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ Decoder d = new Decoder(1024){
+ protected void writeBuffer(byte[] pBuf, int pOff, int pLen) throws IOException {
+ baos.write(pBuf, pOff, pLen);
+ }
+ };
+ try {
+ d.write(pBuffer, pOffset, pLength);
+ d.flush();
+ } catch (DecodingException e) {
+ throw e;
+ } catch (IOException e) {
+ throw new UndeclaredThrowableException(e);
+ }
+ return baos.toByteArray();
+ }
+
+ /** Converts the given base64 encoded character buffer into a byte array.
+ * @param pBuffer The character buffer being decoded.
+ * @return Converted byte array
+ * @throws DecodingException The input character stream contained invalid data.
*/
- public static byte[] decode(String pValue) throws IOException {
- return (new BASE64Decoder()).decodeBuffer(pValue);
+ public static byte[] decode(char[] pBuffer) throws DecodingException {
+ return decode(pBuffer, 0, pBuffer.length);
}
- /** Converts the base64 encoded byte array <code>pValue</code>
- * into a string.
+ /** Converts the given base64 encoded String into a byte array.
+ * @param pBuffer The string being decoded.
+ * @return Converted byte array
+ * @throws DecodingException The input character stream contained invalid data.
*/
- public static String encode(byte[] pValue) {
- return (new BASE64Encoder()).encode(pValue);
+ public static byte[] decode(String pBuffer) throws DecodingException {
+ return decode(pBuffer.toCharArray());
}
}
Modified: webservices/jaxme/trunk/ws-jaxme/status.xml
URL: http://svn.apache.org/viewvc/webservices/jaxme/trunk/ws-jaxme/status.xml?view=diff&rev=501438&r1=501437&r2=501438
==============================================================================
--- webservices/jaxme/trunk/ws-jaxme/status.xml (original)
+++ webservices/jaxme/trunk/ws-jaxme/status.xml Tue Jan 30 08:11:21 2007
@@ -76,6 +76,10 @@
<action dev="JW" type="fix" context="generator">
The handling for primitive boolean default values was incorrect.
</action>
+ <action dev="JW" type="fix" context="runtime">
+ The base64 handling was dependent on the sun.* classes, which are
+ locked in late Java versions.
+ </action>
</release>
<release version="0.5.2" date="25-Oct-2006">
<action dev="JW" type="enhancement" context="generator">
---------------------------------------------------------------------
To unsubscribe, e-mail: jaxme-dev-unsubscribe@ws.apache.org
For additional commands, e-mail: jaxme-dev-help@ws.apache.org