/* eXist Native XML Database * Copyright (C) 2000-03, Wolfgang M. Meier (wolfgang@exist-db.org) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * $Id$ */ package org.exist.util; import org.exist.xquery.Constants; import org.xml.sax.ContentHandler; import org.xml.sax.SAXException; /** * Faster string implementation which uses a CharArrayPool to * pool the backing char arrays. */ public final class XMLString implements CharSequence, Comparable { public final static int SUPPRESS_NONE = 0; public final static int SUPPRESS_LEADING_WS = 0x01; public final static int SUPPRESS_TRAILING_WS = 0x02; public final static int SUPPRESS_BOTH = SUPPRESS_LEADING_WS | SUPPRESS_TRAILING_WS; public final static int DEFAULT_CAPACITY = 16; private char[] value_ = null; private int start_ = 0; private int length_ = 0; public XMLString() { value_ = CharArrayPool.getCharArray(DEFAULT_CAPACITY); } public XMLString(int capacity) { value_ = CharArrayPool.getCharArray(capacity); } public XMLString(char[] ch) { value_ = CharArrayPool.getCharArray(ch.length); System.arraycopy(ch, 0, value_, 0, ch.length); length_ = ch.length; } public XMLString(char[] ch, int start, int length) { value_ = CharArrayPool.getCharArray(length); System.arraycopy(ch, start, value_, 0, length); length_ = length; } public XMLString(XMLString other) { value_ = CharArrayPool.getCharArray(other.length_); System.arraycopy(other.value_, other.start_, value_, 0, other.length_); length_ = other.length_; } public final XMLString append(String str) { append(str.toCharArray()); return this; } public final XMLString append(char[] ch) { append(ch, 0, ch.length); return this; } public final XMLString append(char[] ch, int offset, int len) { ensureCapacity(length_ + len); System.arraycopy(ch, offset, value_, length_, len); length_ += len; return this; } public final XMLString append(XMLString other) { ensureCapacity(length_ + other.length_); System.arraycopy(other.value_, other.start_, value_, length_, other.length_); length_ += other.length_; return this; } public final XMLString append(char ch) { if(value_.length < length_ + 2) ensureCapacity(length_ + 1); value_[length_++] = ch; return this; } public final void setData(char[] ch, int offset, int len) { length_ = 0; start_ = 0; append(ch, offset, len); } public final XMLString normalize(int mode) { if(length_ == 0) return this; if ((mode & SUPPRESS_LEADING_WS) != 0) { while (length_ > 0 && isWhiteSpace(value_[start_])) { --length_; if(length_ > 0) ++start_; } } if ((mode & SUPPRESS_TRAILING_WS) != 0) { while (length_ > 0 && isWhiteSpace(value_[start_ + length_ - 1])) { --length_; } } return this; } public final boolean isWhitespaceOnly() { if(length_ == 0) return true; int i = 0; while(i < length_ && isWhiteSpace(value_[start_ + i])) i++; return i == length_; } public final String toString() { if (value_ == null) return "null"; return new String(value_, start_, length_); } public final int length() { return length_; } public final int startOffset() { return start_; } public final String substring(int start, int count) { if (start < 0 || count < 0 || start >= length_ || start + count > length_) throw new StringIndexOutOfBoundsException(); return new String(value_, start_ + start, count); } public final XMLString delete(int start, int count) { System.arraycopy(value_, start + count + start_, value_, start, length_ - (start + count)); start_ = 0; length_ = length_ - count; return this; } public final XMLString insert(int offset, String data) { ensureCapacity(length_ + data.length()); System.arraycopy(value_, offset, value_, offset + data.length(), length_ - offset); System.arraycopy(data.toCharArray(), 0, value_, offset, data.length()); length_ += data.length(); return this; } public final XMLString replace(int offset, int count, String data) { if (offset < 0 || count < 0 || offset >= length_ || offset + count > length_) throw new StringIndexOutOfBoundsException(); System.arraycopy(data.toCharArray(), 0, value_, start_ + offset, count); return this; } public final char charAt(int pos) { return value_[start_ + pos]; } public final void reset() { CharArrayPool.releaseCharArray(value_); value_ = null; start_ = 0; length_ = 0; } public final void reuse() { start_ = 0; length_ = 0; } private void ensureCapacity(int capacity) { if (value_ == null) //value_ = new char[capacity]; value_ = CharArrayPool.getCharArray(capacity); else if (value_.length - start_ < capacity) { int newCapacity = (length_ + 1) * 2; if (newCapacity < capacity) newCapacity = capacity; char[] temp = CharArrayPool.getCharArray(newCapacity); System.arraycopy(value_, start_, temp, 0, length_); CharArrayPool.releaseCharArray(value_); value_ = temp; start_ = 0; } } private static boolean isWhiteSpace(char ch) { return (ch == 0x20) || (ch == 0x09) || (ch == 0xD) || (ch == 0xA); } /** * Release all resources hold by this XMLString. */ public final void release() { CharArrayPool.releaseCharArray(value_); value_ = null; } /* (non-Javadoc) * @see java.lang.CharSequence#subSequence(int, int) */ public final CharSequence subSequence(int start, int end) { return new XMLString(value_, start_ + start, end - start); } public final XMLString transformToLower() { final int end = start_ + length_; for (int i = start_; i < end; i++) { value_[i] = Character.toLowerCase(value_[i]); } return this; } public final int UTF8Size() { return UTF8.encoded(value_, start_, length_); } public final byte[] UTF8Encode(byte[] b, int offset) { return UTF8.encode(value_, start_, length_, b, offset); } public final void toSAX(ContentHandler ch) throws SAXException { ch.characters(value_, start_, length_); } /* (non-Javadoc) * @see java.lang.Comparable#compareTo(java.lang.Object) */ public final int compareTo(Object o) { CharSequence cs = (CharSequence) o; for (int i = 0; i < length_ && i < cs.length(); i++) { if (value_[start_ + i] < cs.charAt(i)) return Constants.INFERIOR; else if (value_[start_ + i] > cs.charAt(i)) return Constants.SUPERIOR; } if (length_ < cs.length()) return Constants.INFERIOR; else if (length_ > cs.length()) return Constants.SUPERIOR; else return Constants.EQUAL; } /* * @see java.lang.Object#equals(java.lang.Object) */ public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof XMLString) { XMLString anotherString = (XMLString) anObject; if (length_ == anotherString.length_) { int n = length_; char v1[] = value_; char v2[] = anotherString.value_; int i = start_; int j = anotherString.start_; while (n-- != 0) { if (v1[i++] != v2[j++]) return false; } return true; } } else { String anotherString = anObject.toString(); if (length_ == anotherString.length()) { int j = start_; for (int i = 0; i < length_; i++) { if (value_[j++] != anotherString.charAt(i)) return false; } return true; } } return false; } /* * @see java.lang.Object#hashCode() */ public int hashCode() { int off = start_; int h = 0; for (int i = 0; i < length_; i++) { h = 31*h + value_[off++]; } return h; } }