Home | History | Annotate | Download | only in impl
      1 // =================================================================================================
      2 // ADOBE SYSTEMS INCORPORATED
      3 // Copyright 2006 Adobe Systems Incorporated
      4 // All Rights Reserved
      5 //
      6 // NOTICE:  Adobe permits you to use, modify, and distribute this file in accordance with the terms
      7 // of the Adobe license agreement accompanying it.
      8 // =================================================================================================
      9 
     10 
     11 
     12 package com.adobe.xmp.impl;
     13 
     14 import java.io.ByteArrayInputStream;
     15 import java.io.IOException;
     16 import java.io.InputStream;
     17 
     18 
     19 /**
     20  * Byte buffer container including length of valid data.
     21  *
     22  * @since   11.10.2006
     23  */
     24 public class ByteBuffer
     25 {
     26 	/** */
     27 	private byte[] buffer;
     28 	/** */
     29 	private int length;
     30 	/** */
     31 	private String encoding = null;
     32 
     33 
     34 	/**
     35 	 * @param initialCapacity the initial capacity for this buffer
     36 	 */
     37 	public ByteBuffer(int initialCapacity)
     38 	{
     39 		this.buffer = new byte[initialCapacity];
     40 		this.length = 0;
     41 	}
     42 
     43 
     44 	/**
     45 	 * @param buffer a byte array that will be wrapped with <code>ByteBuffer</code>.
     46 	 */
     47 	public ByteBuffer(byte[] buffer)
     48 	{
     49 		this.buffer = buffer;
     50 		this.length = buffer.length;
     51 	}
     52 
     53 
     54 	/**
     55 	 * @param buffer a byte array that will be wrapped with <code>ByteBuffer</code>.
     56 	 * @param length the length of valid bytes in the array
     57 	 */
     58 	public ByteBuffer(byte[] buffer, int length)
     59 	{
     60 		if (length > buffer.length)
     61 		{
     62 			throw new ArrayIndexOutOfBoundsException("Valid length exceeds the buffer length.");
     63 		}
     64 		this.buffer = buffer;
     65 		this.length = length;
     66 	}
     67 
     68 
     69 	/**
     70 	 * Loads the stream into a buffer.
     71 	 *
     72 	 * @param in an InputStream
     73 	 * @throws IOException If the stream cannot be read.
     74 	 */
     75 	public ByteBuffer(InputStream in) throws IOException
     76 	{
     77 		// load stream into buffer
     78 		int chunk = 16384;
     79 		this.length = 0;
     80 		this.buffer = new byte[chunk];
     81 
     82 		int read;
     83 		while ((read = in.read(this.buffer, this.length, chunk)) > 0)
     84 		{
     85 			this.length += read;
     86 			if (read == chunk)
     87 			{
     88 				ensureCapacity(length + chunk);
     89 			}
     90 			else
     91 			{
     92 				break;
     93 			}
     94 		}
     95 	}
     96 
     97 
     98 	/**
     99 	 * @param buffer a byte array that will be wrapped with <code>ByteBuffer</code>.
    100 	 * @param offset the offset of the provided buffer.
    101 	 * @param length the length of valid bytes in the array
    102 	 */
    103 	public ByteBuffer(byte[] buffer, int offset, int length)
    104 	{
    105 		if (length > buffer.length - offset)
    106 		{
    107 			throw new ArrayIndexOutOfBoundsException("Valid length exceeds the buffer length.");
    108 		}
    109 		this.buffer = new byte[length];
    110 		System.arraycopy(buffer, offset, this.buffer, 0, length);
    111 		this.length = length;
    112 	}
    113 
    114 
    115 	/**
    116 	 * @return Returns a byte stream that is limited to the valid amount of bytes.
    117 	 */
    118 	public InputStream getByteStream()
    119 	{
    120 		return new ByteArrayInputStream(buffer, 0, length);
    121 	}
    122 
    123 
    124 	/**
    125 	 * @return Returns the length, that means the number of valid bytes, of the buffer;
    126 	 * the inner byte array might be bigger than that.
    127 	 */
    128 	public int length()
    129 	{
    130 		return length;
    131 	}
    132 
    133 
    134 //	/**
    135 //	 * <em>Note:</em> Only the byte up to length are valid!
    136 //	 * @return Returns the inner byte buffer.
    137 //	 */
    138 //	public byte[] getBuffer()
    139 //	{
    140 //		return buffer;
    141 //	}
    142 
    143 
    144 	/**
    145 	 * @param index the index to retrieve the byte from
    146 	 * @return Returns a byte from the buffer
    147 	 */
    148 	public byte byteAt(int index)
    149 	{
    150 		if (index < length)
    151 		{
    152 			return buffer[index];
    153 		}
    154 		else
    155 		{
    156 			throw new IndexOutOfBoundsException("The index exceeds the valid buffer area");
    157 		}
    158 	}
    159 
    160 
    161 	/**
    162 	 * @param index the index to retrieve a byte as int or char.
    163 	 * @return Returns a byte from the buffer
    164 	 */
    165 	public int charAt(int index)
    166 	{
    167 		if (index < length)
    168 		{
    169 			return buffer[index] & 0xFF;
    170 		}
    171 		else
    172 		{
    173 			throw new IndexOutOfBoundsException("The index exceeds the valid buffer area");
    174 		}
    175 	}
    176 
    177 
    178 	/**
    179 	 * Appends a byte to the buffer.
    180 	 * @param b a byte
    181 	 */
    182 	public void append(byte b)
    183 	{
    184 		ensureCapacity(length + 1);
    185 		buffer[length++] = b;
    186 	}
    187 
    188 
    189 	/**
    190 	 * Appends a byte array or part of to the buffer.
    191 	 *
    192 	 * @param bytes a byte array
    193 	 * @param offset an offset with
    194 	 * @param len
    195 	 */
    196 	public void append(byte[] bytes, int offset, int len)
    197 	{
    198 		ensureCapacity(length + len);
    199 		System.arraycopy(bytes, offset, buffer, length, len);
    200 		length += len;
    201 	}
    202 
    203 
    204 	/**
    205 	 * Append a byte array to the buffer
    206 	 * @param bytes a byte array
    207 	 */
    208 	public void append(byte[] bytes)
    209 	{
    210 		append(bytes, 0, bytes.length);
    211 	}
    212 
    213 
    214 	/**
    215 	 * Append another buffer to this buffer.
    216 	 * @param anotherBuffer another <code>ByteBuffer</code>
    217 	 */
    218 	public void append(ByteBuffer anotherBuffer)
    219 	{
    220 		append(anotherBuffer.buffer, 0, anotherBuffer.length);
    221 	}
    222 
    223 
    224 	/**
    225 	 * Detects the encoding of the byte buffer, stores and returns it.
    226 	 * Only UTF-8, UTF-16LE/BE and UTF-32LE/BE are recognized.
    227 	 * <em>Note:</em> UTF-32 flavors are not supported by Java, the XML-parser will complain.
    228 	 *
    229 	 * @return Returns the encoding string.
    230 	 */
    231 	public String getEncoding()
    232 	{
    233 		if (encoding == null)
    234 		{
    235 			// needs four byte at maximum to determine encoding
    236 			if (length < 2)
    237 			{
    238 				// only one byte length must be UTF-8
    239 				encoding = "UTF-8";
    240 			}
    241 			else if (buffer[0] == 0)
    242 			{
    243 				// These cases are:
    244 				//   00 nn -- -- - Big endian UTF-16
    245 				//   00 00 00 nn - Big endian UTF-32
    246 				//   00 00 FE FF - Big endian UTF 32
    247 
    248 				if (length < 4  ||  buffer[1] != 0)
    249 				{
    250 					encoding =  "UTF-16BE";
    251 				}
    252 				else if ((buffer[2] & 0xFF) == 0xFE  &&  (buffer[3] & 0xFF) == 0xFF)
    253 				{
    254 					encoding = "UTF-32BE";
    255 				}
    256 				else
    257 				{
    258 					encoding = "UTF-32";
    259 				}
    260 			}
    261 			else if ((buffer[0] & 0xFF) < 0x80)
    262 			{
    263 				// These cases are:
    264 				//   nn mm -- -- - UTF-8, includes EF BB BF case
    265 				//   nn 00 -- -- - Little endian UTF-16
    266 
    267 				if (buffer[1] != 0)
    268 				{
    269 					encoding = "UTF-8";
    270 				}
    271 				else if (length < 4  ||  buffer[2] != 0)
    272 				{
    273 					encoding = "UTF-16LE";
    274 				}
    275 				else
    276 				{
    277 					encoding = "UTF-32LE";
    278 				}
    279 			}
    280 			else
    281 			{
    282 				// These cases are:
    283 				//   EF BB BF -- - UTF-8
    284 				//   FE FF -- -- - Big endian UTF-16
    285 				//   FF FE 00 00 - Little endian UTF-32
    286 				//   FF FE -- -- - Little endian UTF-16
    287 
    288 				if ((buffer[0] & 0xFF) == 0xEF)
    289 				{
    290 					encoding = "UTF-8";
    291 				}
    292 				else if ((buffer[0] & 0xFF) == 0xFE)
    293 				{
    294 					encoding = "UTF-16"; // in fact BE
    295 				}
    296 				else if (length < 4  ||  buffer[2] != 0)
    297 				{
    298 					encoding = "UTF-16"; // in fact LE
    299 				}
    300 				else
    301 				{
    302 					encoding = "UTF-32"; // in fact LE
    303 				}
    304 			}
    305 		}
    306 
    307 		return encoding;
    308 	}
    309 
    310 
    311 	/**
    312 	 * Ensures the requested capacity by increasing the buffer size when the
    313 	 * current length is exceeded.
    314 	 *
    315 	 * @param requestedLength requested new buffer length
    316 	 */
    317 	private void ensureCapacity(int requestedLength)
    318 	{
    319 		if (requestedLength > buffer.length)
    320 		{
    321 			byte[] oldBuf = buffer;
    322 			buffer = new byte[oldBuf.length * 2];
    323 			System.arraycopy(oldBuf, 0, buffer, 0, oldBuf.length);
    324 		}
    325 	}
    326 }