001: /*
002: Copyright © 2006,2007 Stefano Chizzolini. http://clown.stefanochizzolini.it
003:
004: Contributors:
005: * Stefano Chizzolini (original code developer, http://www.stefanochizzolini.it):
006: contributed code is Copyright © 2006,2007 by Stefano Chizzolini.
007:
008: This file should be part of the source code distribution of "PDF Clown library"
009: (the Program): see the accompanying README files for more info.
010:
011: This Program is free software; you can redistribute it and/or modify it under
012: the terms of the GNU General Public License as published by the Free Software
013: Foundation; either version 2 of the License, or (at your option) any later version.
014:
015: This Program is distributed in the hope that it will be useful, but WITHOUT ANY
016: WARRANTY, either expressed or implied; without even the implied warranty of
017: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the License for more details.
018:
019: You should have received a copy of the GNU General Public License along with this
020: Program (see README files); if not, go to the GNU website (http://www.gnu.org/).
021:
022: Redistribution and use, with or without modification, are permitted provided that such
023: redistributions retain the above copyright notice, license and disclaimer, along with
024: this list of conditions.
025: */
026:
027: package it.stefanochizzolini.clown.bytes;
028:
029: import it.stefanochizzolini.clown.bytes.filters.Filter;
030: import it.stefanochizzolini.clown.util.NotImplementedException;
031:
032: import java.io.EOFException;
033: import java.io.IOException;
034: import java.nio.ByteOrder;
035: import java.util.Date;
036:
037: /**
038: Byte buffer.
039: @version 0.0.4
040: */
041: public final class Buffer implements IBuffer {
042: // <class>
043: // <static>
044: // <fields>
045: /**
046: Default buffer capacity.
047: */
048: private static final int DefaultCapacity = 1 << 8;
049:
050: private static final String DefaultCharset = "ISO-8859-1";
051: // </fields>
052: // </static>
053:
054: // <dynamic>
055: // <fields>
056: /**
057: Inner buffer where data are stored.
058: */
059: private byte[] data;
060: /**
061: Number of bytes actually used in the buffer.
062: */
063: private int length;
064: /**
065: Pointer position within the buffer.
066: */
067: private int position = 0;
068:
069: private ByteOrder byteOrder = ByteOrder.BIG_ENDIAN;
070:
071: // </fields>
072:
073: // <constructors>
074: public Buffer() {
075: this (0);
076: }
077:
078: public Buffer(int capacity) {
079: if (capacity < 1)
080: capacity = DefaultCapacity;
081:
082: this .data = new byte[capacity];
083: this .length = 0;
084: }
085:
086: public Buffer(byte[] data) {
087: this .data = data;
088: this .length = data.length;
089: }
090:
091: public Buffer(byte[] data, ByteOrder byteOrder) {
092: this .data = data;
093: this .length = data.length;
094: this .byteOrder = byteOrder;
095: }
096:
097: // </constructors>
098:
099: // <interface>
100: // <public>
101: // <IBuffer>
102: public void append(byte data) {
103: while (true) {
104: try {
105: this .data[this .length] = data;
106: break; // Escape the loop.
107: } catch (Exception e) {
108: // Do NOT additional data exceed buffer capacity?
109: if (!ensureCapacity(1)) // Unhandled exception.
110: {
111: // Propagate the exception!
112: throw new RuntimeException(e);
113: }
114: }
115: }
116:
117: // Update buffer size!
118: this .length++;
119: }
120:
121: public void append(byte[] data) {
122: append(data, 0, data.length);
123: }
124:
125: public void append(byte[] data, int offset, int length) {
126: while (true) {
127: try {
128: System.arraycopy(data, offset, this .data, this .length,
129: length);
130: break; // Escape the loop.
131: } catch (Exception e) {
132: // Do NOT additional data exceed buffer capacity?
133: if (!ensureCapacity(length)) // Unhandled exception.
134: {
135: // Propagate the exception!
136: throw new RuntimeException(e);
137: }
138: }
139: }
140:
141: // Update buffer size!
142: this .length += length;
143: }
144:
145: public void append(String data) {
146: try {
147: append(data.getBytes(DefaultCharset));
148: } catch (Exception e) {
149: throw new RuntimeException(e);
150: }
151: }
152:
153: public void append(IInputStream data) {
154: append(data.toByteArray(), 0, (int) data.getLength());
155: }
156:
157: public IBuffer clone() {
158: IBuffer clone = new Buffer(getCapacity());
159: clone.append(data);
160:
161: return clone;
162: }
163:
164: public void decode(Filter filter) {
165: data = filter.decode(data, 0, length);
166: length = data.length;
167: }
168:
169: public void delete(int index, int length) {
170: try {
171: // Shift left the trailing data block to override the deleted data!
172: System.arraycopy(this .data, index + length, this .data,
173: index, this .length - (index + length));
174: } catch (Exception e) {
175: throw new RuntimeException(e);
176: }
177:
178: // Update the buffer size!
179: this .length -= length;
180: }
181:
182: public byte[] encode(Filter filter) {
183: return filter.encode(data, 0, length);
184: }
185:
186: public int getByte(int index) {
187: return data[index];
188: }
189:
190: public byte[] getByteArray(int index, int length) {
191: byte[] data = new byte[length];
192: System.arraycopy(this .data, index, data, 0, length);
193:
194: return data;
195: }
196:
197: public String getString(int index, int length) {
198: try {
199: return new String(data, index, length, DefaultCharset);
200: } catch (Exception e) {
201: throw new RuntimeException(e);
202: }
203: }
204:
205: public int getCapacity() {
206: return data.length;
207: }
208:
209: public void insert(int index, byte[] data) {
210: insert(index, data, 0, data.length);
211: }
212:
213: public void insert(int index, byte[] data, int offset, int length) {
214: while (true) {
215: try {
216: // Shift right the existing data block to make room for new data!
217: System.arraycopy(this .data, index, this .data, index
218: + length, this .length - index);
219: break; // Escape the loop.
220: } catch (Exception e) {
221: // Do NOT additional data exceed buffer capacity?
222: if (!ensureCapacity(length)) // Unhandled exception.
223: {
224: // Propagate the exception!
225: throw new RuntimeException(e);
226: }
227: }
228: }
229:
230: // Insert additional data!
231: System.arraycopy(data, offset, this .data, index, length);
232:
233: // Update the buffer size!
234: this .length += length;
235: }
236:
237: public void insert(int index, String data) {
238: try {
239: insert(index, data.getBytes(DefaultCharset));
240: } catch (Exception e) {
241: // Propagate the exception!
242: throw new RuntimeException(e);
243: }
244: }
245:
246: public void insert(int index, IInputStream data) {
247: insert(index, data.toByteArray());
248: }
249:
250: public void replace(int index, byte[] data) {
251: // Replace data!
252: System.arraycopy(data, 0, this .data, index, data.length);
253: }
254:
255: public void replace(int index, byte[] data, int offset, int length) {
256: // Replace data!
257: System.arraycopy(data, offset, this .data, index, data.length);
258: }
259:
260: public void replace(int index, String data) {
261: try {
262: // Replace data!
263: replace(index, data.getBytes(DefaultCharset));
264: } catch (Exception e) {
265: throw new RuntimeException(e);
266: }
267: }
268:
269: public void replace(int index, IInputStream data) {
270: // Replace data!
271: replace(index, data.toByteArray());
272: }
273:
274: public void setLength(int value) {
275: length = value;
276: }
277:
278: public void writeTo(IOutputStream stream) {
279: stream.write(data, 0, length);
280: }
281:
282: // <IInputStream>
283: public ByteOrder getByteOrder() {
284: return byteOrder;
285: }
286:
287: public long getLength() {
288: return length;
289: }
290:
291: public long getPosition() {
292: return position;
293: }
294:
295: /* int hashCode() uses inherited implementation. */
296:
297: public void read(byte[] data) {
298: read(data, 0, data.length);
299: }
300:
301: public void read(byte[] data, int offset, int length) {
302: try {
303: System.arraycopy(this .data, position, data, offset, length);
304: position += length;
305: } catch (Exception e) {
306: throw new RuntimeException(e);
307: }
308: }
309:
310: public byte readByte() throws EOFException {
311: try {
312: return data[position++];
313: } catch (ArrayIndexOutOfBoundsException e) {
314: throw new EOFException();
315: }
316: }
317:
318: public int readInt() throws EOFException {
319: throw new NotImplementedException();
320: }
321:
322: public String readLine() throws EOFException {
323: StringBuilder buffer = new StringBuilder();
324: try {
325: while (true) {
326: int c = data[position++];
327: if (c == '\r' || c == '\n')
328: break;
329:
330: buffer.append((char) c);
331: }
332: } catch (ArrayIndexOutOfBoundsException e) {
333: throw new EOFException();
334: }
335:
336: return buffer.toString();
337: }
338:
339: public short readShort() throws EOFException {
340: throw new NotImplementedException();
341: }
342:
343: public String readString(int length) {
344: try {
345: String data = new String(this .data, position, length,
346: DefaultCharset);
347: position += length;
348:
349: return data;
350: } catch (Exception e) {
351: throw new RuntimeException(e);
352: }
353: }
354:
355: public int readUnsignedByte() throws EOFException {
356: try {
357: return (data[position++] & 0xFF);
358: } catch (ArrayIndexOutOfBoundsException e) {
359: throw new EOFException();
360: }
361: }
362:
363: public int readUnsignedShort() throws EOFException {
364: //TODO: harmonize byteorder semantics with C# version!!!
365: try {
366: if (byteOrder == ByteOrder.LITTLE_ENDIAN)
367: return (data[position++] & 0xFF)
368: | (data[position++] & 0xFF) << 8;
369: else
370: // ByteOrder.BIG_ENDIAN
371: return (data[position++] & 0xFF) << 8
372: | (data[position++] & 0xFF);
373: } catch (ArrayIndexOutOfBoundsException e) {
374: throw new EOFException();
375: }
376: }
377:
378: public void seek(long position) {
379: this .position = (int) position;
380: }
381:
382: public void setByteOrder(ByteOrder value) {
383: byteOrder = value;
384: }
385:
386: public void setPosition(long value) {
387: position = (int) value;
388: }
389:
390: public void skip(long offset) {
391: position += (int) offset;
392: }
393:
394: public byte[] toByteArray() {
395: byte[] data = new byte[this .length];
396: System.arraycopy(this .data, 0, data, 0, this .length);
397:
398: return data;
399: }
400:
401: // </IInputStream>
402: // </IBuffer>
403: // </public>
404:
405: // <protected>
406: /**
407: Check whether the buffer capacity has sufficient room for adding data.
408: */
409: protected boolean ensureCapacity(int additionalLength) {
410: int minCapacity = this .length + additionalLength;
411: // Is additional data within the buffer capacity?
412: if (minCapacity <= this .data.length)
413: return false; // OK -- No change.
414:
415: // Additional data exceed buffer capacity.
416: // Reallocate the buffer!
417: byte[] data = new byte[Math.max(this .data.length << 1, // 1 order of magnitude greater than current capacity.
418: minCapacity // Minimum capacity required.
419: )];
420: System.arraycopy(this .data, 0, data, 0, this .length);
421: this .data = data;
422:
423: return true; // Reallocation happened.
424: }
425: }
|