001: // ========================================================================
002: // Copyright 2004-2005 Mort Bay Consulting Pty. Ltd.
003: // ------------------------------------------------------------------------
004: // Licensed under the Apache License, Version 2.0 (the "License");
005: // you may not use this file except in compliance with the License.
006: // You may obtain a copy of the License at
007: // http://www.apache.org/licenses/LICENSE-2.0
008: // Unless required by applicable law or agreed to in writing, software
009: // distributed under the License is distributed on an "AS IS" BASIS,
010: // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
011: // See the License for the specific language governing permissions and
012: // limitations under the License.
013: // ========================================================================
014:
015: package org.mortbay.io;
016:
017: import java.io.IOException;
018: import java.io.InputStream;
019: import java.io.OutputStream;
020:
021: /**
022: * @author gregw
023: *
024: */
025: public abstract class AbstractBuffer implements Buffer {
026:
027: protected final static String __IMMUTABLE = "IMMUTABLE",
028: __READONLY = "READONLY", __READWRITE = "READWRITE",
029: __VOLATILE = "VOLATILE";
030:
031: protected int _access;
032: protected boolean _volatile;
033:
034: protected int _get;
035: protected int _put;
036: protected int _hash;
037: private int _hashGet;
038: private int _hashPut;
039: private int _mark;
040: protected String _string;
041: private View _view;
042:
043: /**
044: * Constructor for BufferView
045: *
046: * @param access 0==IMMUTABLE, 1==READONLY, 2==READWRITE
047: */
048: public AbstractBuffer(int access, boolean isVolatile) {
049: if (access == IMMUTABLE && isVolatile)
050: throw new IllegalArgumentException("IMMUTABLE && VOLATILE");
051: setMarkIndex(-1);
052: _access = access;
053: _volatile = isVolatile;
054: }
055:
056: /*
057: * @see org.mortbay.io.Buffer#toArray()
058: */
059: public byte[] asArray() {
060: byte[] bytes = new byte[length()];
061: byte[] array = array();
062: if (array != null)
063: Portable.arraycopy(array, getIndex(), bytes, 0,
064: bytes.length);
065: else
066: peek(getIndex(), bytes, 0, length());
067: return bytes;
068: }
069:
070: public ByteArrayBuffer duplicate(int access) {
071: Buffer b = this .buffer();
072: if (b instanceof Buffer.CaseInsensitve)
073: return new ByteArrayBuffer.CaseInsensitive(asArray(), 0,
074: length(), access);
075: else
076: return new ByteArrayBuffer(asArray(), 0, length(), access);
077: }
078:
079: /*
080: * @see org.mortbay.io.Buffer#asNonVolatile()
081: */
082: public Buffer asNonVolatileBuffer() {
083: if (!isVolatile())
084: return this ;
085: return duplicate(_access);
086: }
087:
088: public Buffer asImmutableBuffer() {
089: if (isImmutable())
090: return this ;
091: return duplicate(IMMUTABLE);
092: }
093:
094: /*
095: * @see org.mortbay.util.Buffer#asReadOnlyBuffer()
096: */
097: public Buffer asReadOnlyBuffer() {
098: if (isReadOnly())
099: return this ;
100: return new View(this , markIndex(), getIndex(), putIndex(),
101: READONLY);
102: }
103:
104: public Buffer asMutableBuffer() {
105: if (!isImmutable())
106: return this ;
107:
108: Buffer b = this .buffer();
109: if (b.isReadOnly()) {
110: return duplicate(READWRITE);
111: }
112: return new View(b, markIndex(), getIndex(), putIndex(), _access);
113: }
114:
115: public Buffer buffer() {
116: return this ;
117: }
118:
119: public void clear() {
120: setGetIndex(0);
121: setPutIndex(0);
122: }
123:
124: public void compact() {
125: if (isReadOnly())
126: throw new IllegalStateException(__READONLY);
127: int s = markIndex() >= 0 ? markIndex() : getIndex();
128: if (s > 0) {
129: byte array[] = array();
130: int length = putIndex() - s;
131: if (length > 0) {
132: if (array != null)
133: Portable.arraycopy(array(), s, array(), 0, length);
134: else
135: poke(0, peek(s, length));
136: }
137: if (markIndex() > 0)
138: setMarkIndex(markIndex() - s);
139: setGetIndex(getIndex() - s);
140: setPutIndex(putIndex() - s);
141: }
142: }
143:
144: public boolean equals(Object obj) {
145: if (obj == this )
146: return true;
147:
148: // reject non buffers;
149: if (obj == null || !(obj instanceof Buffer))
150: return false;
151: Buffer b = (Buffer) obj;
152:
153: if (this instanceof Buffer.CaseInsensitve
154: || b instanceof Buffer.CaseInsensitve)
155: return equalsIgnoreCase(b);
156:
157: // reject different lengths
158: if (b.length() != length())
159: return false;
160:
161: // reject AbstractBuffer with different hash value
162: if (_hash != 0 && obj instanceof AbstractBuffer) {
163: AbstractBuffer ab = (AbstractBuffer) obj;
164: if (ab._hash != 0 && _hash != ab._hash)
165: return false;
166: }
167:
168: // Nothing for it but to do the hard grind.
169: for (int i = length(); i-- > 0;) {
170: byte b1 = peek(getIndex() + i);
171: byte b2 = b.peek(b.getIndex() + i);
172: if (b1 != b2)
173: return false;
174: }
175: return true;
176: }
177:
178: public boolean equalsIgnoreCase(Buffer b) {
179: if (b == this )
180: return true;
181:
182: // reject different lengths
183: if (b.length() != length())
184: return false;
185:
186: // reject AbstractBuffer with different hash value
187: if (_hash != 0 && b instanceof AbstractBuffer) {
188: AbstractBuffer ab = (AbstractBuffer) b;
189: if (ab._hash != 0 && _hash != ab._hash)
190: return false;
191: }
192:
193: // Nothing for it but to do the hard grind.
194: for (int i = length(); i-- > 0;) {
195: byte b1 = peek(getIndex() + i);
196: byte b2 = b.peek(b.getIndex() + i);
197: if (b1 != b2) {
198: if ('a' <= b1 && b1 <= 'z')
199: b1 = (byte) (b1 - 'a' + 'A');
200: if ('a' <= b2 && b2 <= 'z')
201: b2 = (byte) (b2 - 'a' + 'A');
202: if (b1 != b2)
203: return false;
204: }
205: }
206: return true;
207: }
208:
209: public byte get() {
210: return peek(_get++);
211: }
212:
213: public int get(byte[] b, int offset, int length) {
214: int gi = getIndex();
215: int l = length();
216: if (length > l)
217: length = l;
218: length = peek(gi, b, offset, length);
219: setGetIndex(gi + length);
220: return length;
221: }
222:
223: public Buffer get(int length) {
224: int gi = getIndex();
225: Buffer view = peek(gi, length);
226: setGetIndex(gi + length);
227: return view;
228: }
229:
230: public final int getIndex() {
231: return _get;
232: }
233:
234: public boolean hasContent() {
235: return _put > _get;
236: }
237:
238: public int hashCode() {
239: if (_hash == 0 || _hashGet != _get || _hashPut != _put) {
240: for (int i = putIndex(); i-- > getIndex();) {
241: byte b = peek(i);
242: if ('a' <= b && b <= 'z')
243: b = (byte) (b - 'a' + 'A');
244: _hash = 31 * _hash + b;
245: }
246: if (_hash == 0)
247: _hash = -1;
248: _hashGet = _get;
249: _hashPut = _put;
250: }
251: return _hash;
252: }
253:
254: public boolean isImmutable() {
255: return _access <= IMMUTABLE;
256: }
257:
258: public boolean isReadOnly() {
259: return _access <= READONLY;
260: }
261:
262: public boolean isVolatile() {
263: return _volatile;
264: }
265:
266: public int length() {
267: return _put - _get;
268: }
269:
270: public void mark() {
271: setMarkIndex(_get - 1);
272: }
273:
274: public void mark(int offset) {
275: setMarkIndex(_get + offset);
276: }
277:
278: public int markIndex() {
279: return _mark;
280: }
281:
282: public byte peek() {
283: return peek(_get);
284: }
285:
286: public Buffer peek(int index, int length) {
287: if (_view == null) {
288: _view = new View(this , -1, index, index + length,
289: isReadOnly() ? READONLY : READWRITE);
290: } else {
291: _view.update(this .buffer());
292: _view.setMarkIndex(-1);
293: _view.setGetIndex(0);
294: _view.setPutIndex(index + length);
295: _view.setGetIndex(index);
296:
297: }
298: return _view;
299: }
300:
301: public int poke(int index, Buffer src) {
302: _hash = 0;
303: if (isReadOnly())
304: throw new IllegalStateException(__READONLY);
305: if (index < 0)
306: throw new IllegalArgumentException("index<0: " + index
307: + "<0");
308:
309: int length = src.length();
310: if (index + length > capacity()) {
311: length = capacity() - index;
312: if (length < 0)
313: throw new IllegalArgumentException("index>capacity(): "
314: + index + ">" + capacity());
315: }
316:
317: byte[] src_array = src.array();
318: byte[] dst_array = array();
319: if (src_array != null && dst_array != null)
320: Portable.arraycopy(src_array, src.getIndex(), dst_array,
321: index, length);
322: else if (src_array != null) {
323: int s = src.getIndex();
324: for (int i = 0; i < length; i++)
325: poke(index++, src_array[s++]);
326: } else if (dst_array != null) {
327: int s = src.getIndex();
328: for (int i = 0; i < length; i++)
329: dst_array[index++] = src.peek(s++);
330: } else {
331: int s = src.getIndex();
332: for (int i = 0; i < length; i++)
333: poke(index++, src.peek(s++));
334: }
335:
336: return length;
337: }
338:
339: public int poke(int index, byte[] b, int offset, int length) {
340: _hash = 0;
341: if (isReadOnly())
342: throw new IllegalStateException(__READONLY);
343: if (index < 0)
344: throw new IllegalArgumentException("index<0: " + index
345: + "<0");
346:
347: if (index + length > capacity()) {
348: length = capacity() - index;
349: if (length < 0)
350: throw new IllegalArgumentException("index>capacity(): "
351: + index + ">" + capacity());
352: }
353:
354: byte[] dst_array = array();
355: if (dst_array != null)
356: Portable.arraycopy(b, offset, dst_array, index, length);
357: else {
358: int s = offset;
359: for (int i = 0; i < length; i++)
360: poke(index++, b[s++]);
361: }
362: return length;
363: }
364:
365: public int put(Buffer src) {
366: int pi = putIndex();
367: int l = poke(pi, src);
368: setPutIndex(pi + l);
369: return l;
370: }
371:
372: public void put(byte b) {
373: int pi = putIndex();
374: poke(pi, b);
375: setPutIndex(pi + 1);
376: }
377:
378: public int put(byte[] b, int offset, int length) {
379: int pi = putIndex();
380: int l = poke(pi, b, offset, length);
381: setPutIndex(pi + l);
382: return l;
383: }
384:
385: public int put(byte[] b) {
386: int pi = putIndex();
387: int l = poke(pi, b, 0, b.length);
388: setPutIndex(pi + l);
389: return l;
390: }
391:
392: public final int putIndex() {
393: return _put;
394: }
395:
396: public void reset() {
397: if (markIndex() >= 0)
398: setGetIndex(markIndex());
399: }
400:
401: public void rewind() {
402: setGetIndex(0);
403: setMarkIndex(-1);
404: }
405:
406: public void setGetIndex(int getIndex) {
407: /* bounds checking
408: if (isImmutable()) new throw IllegalStateException(__IMMUTABLE);
409: if (getIndex < 0) throw new IllegalArgumentException("getIndex<0: " + getIndex + "<0");
410: if (getIndex > putIndex()) throw new IllegalArgumentException("getIndex>putIndex: " + getIndex + ">" + putIndex());
411: */
412: _get = getIndex;
413: _hash = 0;
414: }
415:
416: public void setMarkIndex(int index) {
417: if (index >= 0 && isImmutable())
418: throw new IllegalStateException(__IMMUTABLE);
419: _mark = index;
420: }
421:
422: public void setPutIndex(int putIndex) {
423: /* bounds checking
424: if (isImmutable()) new throw IllegalStateException(__IMMUTABLE);
425: if (putIndex > capacity())
426: throw new IllegalArgumentException("putIndex>capacity: " + putIndex + ">" + capacity());
427: if (getIndex() > putIndex)
428: throw new IllegalArgumentException("getIndex>putIndex: " + getIndex() + ">" + putIndex);
429: */
430: _put = putIndex;
431: _hash = 0;
432: }
433:
434: public int skip(int n) {
435: if (length() < n)
436: n = length();
437: setGetIndex(getIndex() + n);
438: return n;
439: }
440:
441: public Buffer slice() {
442: return peek(getIndex(), length());
443: }
444:
445: public Buffer sliceFromMark() {
446: return sliceFromMark(getIndex() - markIndex() - 1);
447: }
448:
449: public Buffer sliceFromMark(int length) {
450: if (markIndex() < 0)
451: return null;
452: Buffer view = peek(markIndex(), length);
453: setMarkIndex(-1);
454: return view;
455: }
456:
457: public int space() {
458: return capacity() - putIndex();
459: }
460:
461: public String toDetailString() {
462: StringBuffer buf = new StringBuffer();
463: buf.append("[");
464: buf.append(super .hashCode());
465: buf.append(",");
466: buf.append(this .array().hashCode());
467: buf.append(",m=");
468: buf.append(markIndex());
469: buf.append(",g=");
470: buf.append(getIndex());
471: buf.append(",p=");
472: buf.append(putIndex());
473: buf.append(",c=");
474: buf.append(capacity());
475: buf.append("]={");
476: if (markIndex() >= 0) {
477: for (int i = markIndex(); i < getIndex(); i++) {
478: char c = (char) peek(i);
479: if (Character.isISOControl(c)) {
480: buf.append(c < 16 ? "\\0" : "\\");
481: buf.append(Integer.toString(c, 16));
482: } else
483: buf.append(c);
484: }
485: buf.append("}{");
486: }
487: int count = 0;
488: for (int i = getIndex(); i < putIndex(); i++) {
489: char c = (char) peek(i);
490: if (Character.isISOControl(c)) {
491: buf.append(c < 16 ? "\\0" : "\\");
492: buf.append(Integer.toString(c, 16));
493: } else
494: buf.append(c);
495: if (count++ == 50) {
496: if (putIndex() - i > 20) {
497: buf.append(" ... ");
498: i = putIndex() - 20;
499: }
500: }
501: }
502: buf.append('}');
503: return buf.toString();
504: }
505:
506: /* ------------------------------------------------------------ */
507: public String toString() {
508: if (isImmutable()) {
509: if (_string == null)
510: _string = new String(asArray(), 0, length());
511: return _string;
512: }
513: return new String(asArray(), 0, length());
514: }
515:
516: /* ------------------------------------------------------------ */
517: public String toDebugString() {
518: return getClass() + "@" + super .hashCode();
519: }
520:
521: /* ------------------------------------------------------------ */
522: public void writeTo(OutputStream out) throws IOException {
523: byte array[] = array();
524:
525: if (array != null) {
526: out.write(array, getIndex(), length());
527: } else {
528: // System.err.println(this.getClass()+" OUCH!!!! Abstract writeTo: ? "+getIndex()+"-"+putIndex());
529:
530: // TODO perhaps in buffer?
531: for (int i = _get; i < _put; i++)
532: out.write(peek(i));
533: }
534: clear();
535: }
536:
537: /* ------------------------------------------------------------ */
538: public int readFrom(InputStream in, int max) throws IOException {
539: // System.err.println(this.getClass()+" OUCH!!!! Abstract readFrom "+max+" "+getIndex()+"-"+putIndex());
540: int len = 0;
541: while (space() > 0) {
542: // TODO perhaps buffer
543: int b = in.read();
544: if (b < 0)
545: return -1;
546: put((byte) b);
547: len++;
548: }
549: return len;
550: }
551: }
|