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 }