001: /*
002: * Copyright 2003-2004 The Apache Software Foundation.
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: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: /*
017: * $Id: AttributesImplSerializer.java,v 1.6 2005/02/11 06:18:13 minchau Exp $
018: */
019:
020: package org.apache.xml.serializer;
021:
022: import java.util.Hashtable;
023:
024: import org.xml.sax.Attributes;
025: import org.xml.sax.helpers.AttributesImpl;
026:
027: /**
028: * This class extends org.xml.sax.helpers.AttributesImpl which implements org.
029: * xml.sax.Attributes. But for optimization this class adds a Hashtable for
030: * faster lookup of an index by qName, which is commonly done in the stream
031: * serializer.
032: *
033: * @see org.xml.sax.Attributes
034: *
035: * @xsl.usage internal
036: */
037: public final class AttributesImplSerializer extends AttributesImpl {
038: /**
039: * Hash table of qName/index values to quickly lookup the index
040: * of an attributes qName. qNames are in uppercase in the hash table
041: * to make the search case insensitive.
042: *
043: * The keys to the hashtable to find the index are either
044: * "prefix:localName" or "{uri}localName".
045: */
046: private final Hashtable m_indexFromQName = new Hashtable();
047:
048: private final StringBuffer m_buff = new StringBuffer();
049:
050: /**
051: * This is the number of attributes before switching to the hash table,
052: * and can be tuned, but 12 seems good for now - Brian M.
053: */
054: private static final int MAX = 12;
055:
056: /**
057: * One less than the number of attributes before switching to
058: * the Hashtable.
059: */
060: private static final int MAXMinus1 = MAX - 1;
061:
062: /**
063: * This method gets the index of an attribute given its qName.
064: * @param qname the qualified name of the attribute, e.g. "prefix1:locName1"
065: * @return the integer index of the attribute.
066: * @see org.xml.sax.Attributes#getIndex(String)
067: */
068: public final int getIndex(String qname) {
069: int index;
070:
071: if (super .getLength() < MAX) {
072: // if we haven't got too many attributes let the
073: // super class look it up
074: index = super .getIndex(qname);
075: return index;
076: }
077: // we have too many attributes and the super class is slow
078: // so find it quickly using our Hashtable.
079: Integer i = (Integer) m_indexFromQName.get(qname);
080: if (i == null)
081: index = -1;
082: else
083: index = i.intValue();
084: return index;
085: }
086:
087: /**
088: * This method adds the attribute, but also records its qName/index pair in
089: * the hashtable for fast lookup by getIndex(qName).
090: * @param uri the URI of the attribute
091: * @param local the local name of the attribute
092: * @param qname the qualified name of the attribute
093: * @param type the type of the attribute
094: * @param val the value of the attribute
095: *
096: * @see org.xml.sax.helpers.AttributesImpl#addAttribute(String, String, String, String, String)
097: * @see #getIndex(String)
098: */
099: public final void addAttribute(String uri, String local,
100: String qname, String type, String val) {
101: int index = super .getLength();
102: super .addAttribute(uri, local, qname, type, val);
103: // (index + 1) is now the number of attributes
104: // so either compare (index+1) to MAX, or compare index to (MAX-1)
105:
106: if (index < MAXMinus1) {
107: return;
108: } else if (index == MAXMinus1) {
109: switchOverToHash(MAX);
110: } else {
111: /* add the key with the format of "prefix:localName" */
112: /* we have just added the attibute, its index is the old length */
113: Integer i = new Integer(index);
114: m_indexFromQName.put(qname, i);
115:
116: /* now add with key of the format "{uri}localName" */
117: m_buff.setLength(0);
118: m_buff.append('{').append(uri).append('}').append(local);
119: String key = m_buff.toString();
120: m_indexFromQName.put(key, i);
121: }
122: return;
123: }
124:
125: /**
126: * We are switching over to having a hash table for quick look
127: * up of attributes, but up until now we haven't kept any
128: * information in the Hashtable, so we now update the Hashtable.
129: * Future additional attributes will update the Hashtable as
130: * they are added.
131: * @param numAtts
132: */
133: private void switchOverToHash(int numAtts) {
134: for (int index = 0; index < numAtts; index++) {
135: String qName = super .getQName(index);
136: Integer i = new Integer(index);
137: m_indexFromQName.put(qName, i);
138:
139: // Add quick look-up to find with uri/local name pair
140: String uri = super .getURI(index);
141: String local = super .getLocalName(index);
142: m_buff.setLength(0);
143: m_buff.append('{').append(uri).append('}').append(local);
144: String key = m_buff.toString();
145: m_indexFromQName.put(key, i);
146: }
147: }
148:
149: /**
150: * This method clears the accumulated attributes.
151: *
152: * @see org.xml.sax.helpers.AttributesImpl#clear()
153: */
154: public final void clear() {
155:
156: int len = super .getLength();
157: super .clear();
158: if (MAX <= len) {
159: // if we have had enough attributes and are
160: // using the Hashtable, then clear the Hashtable too.
161: m_indexFromQName.clear();
162: }
163:
164: }
165:
166: /**
167: * This method sets the attributes, previous attributes are cleared,
168: * it also keeps the hashtable up to date for quick lookup via
169: * getIndex(qName).
170: * @param atts the attributes to copy into these attributes.
171: * @see org.xml.sax.helpers.AttributesImpl#setAttributes(Attributes)
172: * @see #getIndex(String)
173: */
174: public final void setAttributes(Attributes atts) {
175:
176: super .setAttributes(atts);
177:
178: // we've let the super class add the attributes, but
179: // we need to keep the hash table up to date ourselves for the
180: // potentially new qName/index pairs for quick lookup.
181: int numAtts = atts.getLength();
182: if (MAX <= numAtts)
183: switchOverToHash(numAtts);
184:
185: }
186:
187: /**
188: * This method gets the index of an attribute given its uri and locanName.
189: * @param uri the URI of the attribute name.
190: * @param localName the local namer (after the ':' ) of the attribute name.
191: * @return the integer index of the attribute.
192: * @see org.xml.sax.Attributes#getIndex(String)
193: */
194: public final int getIndex(String uri, String localName) {
195: int index;
196:
197: if (super .getLength() < MAX) {
198: // if we haven't got too many attributes let the
199: // super class look it up
200: index = super .getIndex(uri, localName);
201: return index;
202: }
203: // we have too many attributes and the super class is slow
204: // so find it quickly using our Hashtable.
205: // Form the key of format "{uri}localName"
206: m_buff.setLength(0);
207: m_buff.append('{').append(uri).append('}').append(localName);
208: String key = m_buff.toString();
209: Integer i = (Integer) m_indexFromQName.get(key);
210: if (i == null)
211: index = -1;
212: else
213: index = i.intValue();
214: return index;
215: }
216: }
|