/* |
|
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. |
|
*/ |
|
/* |
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
|
* contributor license agreements. See the NOTICE file distributed with |
|
* this work for additional information regarding copyright ownership. |
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|
* (the "License"); you may not use this file except in compliance with |
|
* the License. You may obtain a copy of the License at |
|
* |
|
* http://www.apache.org/licenses/LICENSE-2.0 |
|
* |
|
* Unless required by applicable law or agreed to in writing, software |
|
* distributed under the License is distributed on an "AS IS" BASIS, |
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
* See the License for the specific language governing permissions and |
|
* limitations under the License. |
|
*/ |
|
/* |
|
* $Id: AttributesImplSerializer.java,v 1.2.4.1 2005/09/15 08:15:14 suresh_emailid Exp $ |
|
*/ |
|
package com.sun.org.apache.xml.internal.serializer; |
|
import java.util.HashMap; |
|
import java.util.Map; |
|
import org.xml.sax.Attributes; |
|
import org.xml.sax.helpers.AttributesImpl; |
|
/** |
|
* This class extends org.xml.sax.helpers.AttributesImpl which implements org. |
|
* xml.sax.Attributes. But for optimization this class adds a Map for |
|
* faster lookup of an index by qName, which is commonly done in the stream |
|
* serializer. |
|
* |
|
* @see org.xml.sax.Attributes |
|
* |
|
* @xsl.usage internal |
|
*/ |
|
public final class AttributesImplSerializer extends AttributesImpl |
|
{ |
|
/** |
|
* Hash table of qName/index values to quickly lookup the index |
|
* of an attributes qName. qNames are in uppercase in the hash table |
|
* to make the search case insensitive. |
|
* |
|
* The keys to the hashtable to find the index are either |
|
* "prefix:localName" or "{uri}localName". |
|
*/ |
|
private final Map<String, Integer> m_indexFromQName = new HashMap<>(); |
|
private final StringBuffer m_buff = new StringBuffer(); |
|
/** |
|
* This is the number of attributes before switching to the hash table, |
|
* and can be tuned, but 12 seems good for now - Brian M. |
|
*/ |
|
private static final int MAX = 12; |
|
/** |
|
* One less than the number of attributes before switching to |
|
* the Map. |
|
*/ |
|
private static final int MAXMinus1 = MAX - 1; |
|
/** |
|
* This method gets the index of an attribute given its qName. |
|
* @param qname the qualified name of the attribute, e.g. "prefix1:locName1" |
|
* @return the integer index of the attribute. |
|
* @see org.xml.sax.Attributes#getIndex(String) |
|
*/ |
|
public final int getIndex(String qname) |
|
{ |
|
int index; |
|
if (super.getLength() < MAX) |
|
{ |
|
// if we haven't got too many attributes let the |
|
// super class look it up |
|
index = super.getIndex(qname); |
|
return index; |
|
} |
|
// we have too many attributes and the super class is slow |
|
// so find it quickly using our Map. |
|
Integer i = m_indexFromQName.get(qname); |
|
if (i == null) |
|
index = -1; |
|
else |
|
index = i.intValue(); |
|
return index; |
|
} |
|
/** |
|
* This method adds the attribute, but also records its qName/index pair in |
|
* the hashtable for fast lookup by getIndex(qName). |
|
* @param uri the URI of the attribute |
|
* @param local the local name of the attribute |
|
* @param qname the qualified name of the attribute |
|
* @param type the type of the attribute |
|
* @param val the value of the attribute |
|
* |
|
* @see org.xml.sax.helpers.AttributesImpl#addAttribute(String, String, String, String, String) |
|
* @see #getIndex(String) |
|
*/ |
|
public final void addAttribute( |
|
String uri, |
|
String local, |
|
String qname, |
|
String type, |
|
String val) |
|
{ |
|
int index = super.getLength(); |
|
super.addAttribute(uri, local, qname, type, val); |
|
// (index + 1) is now the number of attributes |
|
// so either compare (index+1) to MAX, or compare index to (MAX-1) |
|
if (index < MAXMinus1) |
|
{ |
|
return; |
|
} |
|
else if (index == MAXMinus1) |
|
{ |
|
switchOverToHash(MAX); |
|
} |
|
else |
|
{ |
|
/* add the key with the format of "prefix:localName" */ |
|
/* we have just added the attibute, its index is the old length */ |
|
Integer i = index; |
|
m_indexFromQName.put(qname, i); |
|
/* now add with key of the format "{uri}localName" */ |
|
m_buff.setLength(0); |
|
m_buff.append('{').append(uri).append('}').append(local); |
|
String key = m_buff.toString(); |
|
m_indexFromQName.put(key, i); |
|
} |
|
} |
|
/** |
|
* We are switching over to having a hash table for quick look |
|
* up of attributes, but up until now we haven't kept any |
|
* information in the Map, so we now update the Map. |
|
* Future additional attributes will update the Map as |
|
* they are added. |
|
* @param numAtts |
|
*/ |
|
private void switchOverToHash(int numAtts) |
|
{ |
|
for (int index = 0; index < numAtts; index++) |
|
{ |
|
String qName = super.getQName(index); |
|
Integer i = index; |
|
m_indexFromQName.put(qName, i); |
|
// Add quick look-up to find with uri/local name pair |
|
String uri = super.getURI(index); |
|
String local = super.getLocalName(index); |
|
m_buff.setLength(0); |
|
m_buff.append('{').append(uri).append('}').append(local); |
|
String key = m_buff.toString(); |
|
m_indexFromQName.put(key, i); |
|
} |
|
} |
|
/** |
|
* This method clears the accumulated attributes. |
|
* |
|
* @see org.xml.sax.helpers.AttributesImpl#clear() |
|
*/ |
|
public final void clear() |
|
{ |
|
int len = super.getLength(); |
|
super.clear(); |
|
if (MAX <= len) |
|
{ |
|
// if we have had enough attributes and are |
|
// using the Map, then clear the Map too. |
|
m_indexFromQName.clear(); |
|
} |
|
} |
|
/** |
|
* This method sets the attributes, previous attributes are cleared, |
|
* it also keeps the hashtable up to date for quick lookup via |
|
* getIndex(qName). |
|
* @param atts the attributes to copy into these attributes. |
|
* @see org.xml.sax.helpers.AttributesImpl#setAttributes(Attributes) |
|
* @see #getIndex(String) |
|
*/ |
|
public final void setAttributes(Attributes atts) |
|
{ |
|
super.setAttributes(atts); |
|
// we've let the super class add the attributes, but |
|
// we need to keep the hash table up to date ourselves for the |
|
// potentially new qName/index pairs for quick lookup. |
|
int numAtts = atts.getLength(); |
|
if (MAX <= numAtts) |
|
switchOverToHash(numAtts); |
|
} |
|
/** |
|
* This method gets the index of an attribute given its uri and locanName. |
|
* @param uri the URI of the attribute name. |
|
* @param localName the local namer (after the ':' ) of the attribute name. |
|
* @return the integer index of the attribute. |
|
* @see org.xml.sax.Attributes#getIndex(String) |
|
*/ |
|
public final int getIndex(String uri, String localName) |
|
{ |
|
int index; |
|
if (super.getLength() < MAX) |
|
{ |
|
// if we haven't got too many attributes let the |
|
// super class look it up |
|
index = super.getIndex(uri,localName); |
|
return index; |
|
} |
|
// we have too many attributes and the super class is slow |
|
// so find it quickly using our Map. |
|
// Form the key of format "{uri}localName" |
|
m_buff.setLength(0); |
|
m_buff.append('{').append(uri).append('}').append(localName); |
|
String key = m_buff.toString(); |
|
Integer i = m_indexFromQName.get(key); |
|
if (i == null) |
|
index = -1; |
|
else |
|
index = i; |
|
return index; |
|
} |
|
} |