1/* 2 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. 3 */ 4/* 5 * Licensed to the Apache Software Foundation (ASF) under one or more 6 * contributor license agreements. See the NOTICE file distributed with 7 * this work for additional information regarding copyright ownership. 8 * The ASF licenses this file to You under the Apache License, Version 2.0 9 * (the "License"); you may not use this file except in compliance with 10 * the License. You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 */ 20/* 21 * $Id: AttributesImplSerializer.java,v 1.2.4.1 2005/09/15 08:15:14 suresh_emailid Exp $ 22 */ 23 24package com.sun.org.apache.xml.internal.serializer; 25 26import java.util.HashMap; 27import java.util.Map; 28import org.xml.sax.Attributes; 29import org.xml.sax.helpers.AttributesImpl; 30 31/** 32 * This class extends org.xml.sax.helpers.AttributesImpl which implements org. 33 * xml.sax.Attributes. But for optimization this class adds a Map for 34 * faster lookup of an index by qName, which is commonly done in the stream 35 * serializer. 36 * 37 * @see org.xml.sax.Attributes 38 * 39 * @xsl.usage internal 40 */ 41public final class AttributesImplSerializer extends AttributesImpl 42{ 43 /** 44 * Hash table of qName/index values to quickly lookup the index 45 * of an attributes qName. qNames are in uppercase in the hash table 46 * to make the search case insensitive. 47 * 48 * The keys to the hashtable to find the index are either 49 * "prefix:localName" or "{uri}localName". 50 */ 51 private final Map<String, Integer> m_indexFromQName = new HashMap<>(); 52 53 private final StringBuffer m_buff = new StringBuffer(); 54 55 /** 56 * This is the number of attributes before switching to the hash table, 57 * and can be tuned, but 12 seems good for now - Brian M. 58 */ 59 private static final int MAX = 12; 60 61 /** 62 * One less than the number of attributes before switching to 63 * the Map. 64 */ 65 private static final int MAXMinus1 = MAX - 1; 66 67 /** 68 * This method gets the index of an attribute given its qName. 69 * @param qname the qualified name of the attribute, e.g. "prefix1:locName1" 70 * @return the integer index of the attribute. 71 * @see org.xml.sax.Attributes#getIndex(String) 72 */ 73 public final int getIndex(String qname) 74 { 75 int index; 76 77 if (super.getLength() < MAX) 78 { 79 // if we haven't got too many attributes let the 80 // super class look it up 81 index = super.getIndex(qname); 82 return index; 83 } 84 // we have too many attributes and the super class is slow 85 // so find it quickly using our Map. 86 Integer i = m_indexFromQName.get(qname); 87 if (i == null) 88 index = -1; 89 else 90 index = i.intValue(); 91 return index; 92 } 93 /** 94 * This method adds the attribute, but also records its qName/index pair in 95 * the hashtable for fast lookup by getIndex(qName). 96 * @param uri the URI of the attribute 97 * @param local the local name of the attribute 98 * @param qname the qualified name of the attribute 99 * @param type the type of the attribute 100 * @param val the value of the attribute 101 * 102 * @see org.xml.sax.helpers.AttributesImpl#addAttribute(String, String, String, String, String) 103 * @see #getIndex(String) 104 */ 105 public final void addAttribute( 106 String uri, 107 String local, 108 String qname, 109 String type, 110 String val) 111 { 112 int index = super.getLength(); 113 super.addAttribute(uri, local, qname, type, val); 114 // (index + 1) is now the number of attributes 115 // so either compare (index+1) to MAX, or compare index to (MAX-1) 116 117 if (index < MAXMinus1) 118 { 119 return; 120 } 121 else if (index == MAXMinus1) 122 { 123 switchOverToHash(MAX); 124 } 125 else 126 { 127 /* add the key with the format of "prefix:localName" */ 128 /* we have just added the attibute, its index is the old length */ 129 Integer i = index; 130 m_indexFromQName.put(qname, i); 131 132 /* now add with key of the format "{uri}localName" */ 133 m_buff.setLength(0); 134 m_buff.append('{').append(uri).append('}').append(local); 135 String key = m_buff.toString(); 136 m_indexFromQName.put(key, i); 137 } 138 } 139 140 /** 141 * We are switching over to having a hash table for quick look 142 * up of attributes, but up until now we haven't kept any 143 * information in the Map, so we now update the Map. 144 * Future additional attributes will update the Map as 145 * they are added. 146 * @param numAtts 147 */ 148 private void switchOverToHash(int numAtts) 149 { 150 for (int index = 0; index < numAtts; index++) 151 { 152 String qName = super.getQName(index); 153 Integer i = index; 154 m_indexFromQName.put(qName, i); 155 156 // Add quick look-up to find with uri/local name pair 157 String uri = super.getURI(index); 158 String local = super.getLocalName(index); 159 m_buff.setLength(0); 160 m_buff.append('{').append(uri).append('}').append(local); 161 String key = m_buff.toString(); 162 m_indexFromQName.put(key, i); 163 } 164 } 165 166 /** 167 * This method clears the accumulated attributes. 168 * 169 * @see org.xml.sax.helpers.AttributesImpl#clear() 170 */ 171 public final void clear() 172 { 173 174 int len = super.getLength(); 175 super.clear(); 176 if (MAX <= len) 177 { 178 // if we have had enough attributes and are 179 // using the Map, then clear the Map too. 180 m_indexFromQName.clear(); 181 } 182 183 } 184 185 /** 186 * This method sets the attributes, previous attributes are cleared, 187 * it also keeps the hashtable up to date for quick lookup via 188 * getIndex(qName). 189 * @param atts the attributes to copy into these attributes. 190 * @see org.xml.sax.helpers.AttributesImpl#setAttributes(Attributes) 191 * @see #getIndex(String) 192 */ 193 public final void setAttributes(Attributes atts) 194 { 195 196 super.setAttributes(atts); 197 198 // we've let the super class add the attributes, but 199 // we need to keep the hash table up to date ourselves for the 200 // potentially new qName/index pairs for quick lookup. 201 int numAtts = atts.getLength(); 202 if (MAX <= numAtts) 203 switchOverToHash(numAtts); 204 205 } 206 207 /** 208 * This method gets the index of an attribute given its uri and locanName. 209 * @param uri the URI of the attribute name. 210 * @param localName the local namer (after the ':' ) of the attribute name. 211 * @return the integer index of the attribute. 212 * @see org.xml.sax.Attributes#getIndex(String) 213 */ 214 public final int getIndex(String uri, String localName) 215 { 216 int index; 217 218 if (super.getLength() < MAX) 219 { 220 // if we haven't got too many attributes let the 221 // super class look it up 222 index = super.getIndex(uri,localName); 223 return index; 224 } 225 // we have too many attributes and the super class is slow 226 // so find it quickly using our Map. 227 // Form the key of format "{uri}localName" 228 m_buff.setLength(0); 229 m_buff.append('{').append(uri).append('}').append(localName); 230 String key = m_buff.toString(); 231 Integer i = m_indexFromQName.get(key); 232 if (i == null) 233 index = -1; 234 else 235 index = i; 236 return index; 237 } 238} 239