001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the "License"); 007 * you may not use this file except in compliance with the License. 008 * You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 /* 019 * $Id: ExpandedNameTable.java 1225372 2011-12-28 22:58:27Z mrglavas $ 020 */ 021 package org.apache.xml.dtm.ref; 022 023 import org.apache.xml.dtm.DTM; 024 025 /** 026 * This is a default implementation of a table that manages mappings from 027 * expanded names to expandedNameIDs. 028 * 029 * %OPT% The performance of the getExpandedTypeID() method is very important 030 * to DTM building. To get the best performance out of this class, we implement 031 * a simple hash algorithm directly into this class, instead of using the 032 * inefficient java.util.Hashtable. The code for the get and put operations 033 * are combined in getExpandedTypeID() method to share the same hash calculation 034 * code. We only need to implement the rehash() interface which is used to 035 * expand the hash table. 036 */ 037 public class ExpandedNameTable 038 { 039 040 /** Array of extended types for this document */ 041 private ExtendedType[] m_extendedTypes; 042 043 /** The initial size of the m_extendedTypes array */ 044 private static int m_initialSize = 128; 045 046 /** Next available extended type */ 047 // %REVIEW% Since this is (should be) always equal 048 // to the length of m_extendedTypes, do we need this? 049 private int m_nextType; 050 051 // These are all the types prerotated, for caller convenience. 052 public static final int ELEMENT = ((int)DTM.ELEMENT_NODE) ; 053 public static final int ATTRIBUTE = ((int)DTM.ATTRIBUTE_NODE) ; 054 public static final int TEXT = ((int)DTM.TEXT_NODE) ; 055 public static final int CDATA_SECTION = ((int)DTM.CDATA_SECTION_NODE) ; 056 public static final int ENTITY_REFERENCE = ((int)DTM.ENTITY_REFERENCE_NODE) ; 057 public static final int ENTITY = ((int)DTM.ENTITY_NODE) ; 058 public static final int PROCESSING_INSTRUCTION = ((int)DTM.PROCESSING_INSTRUCTION_NODE) ; 059 public static final int COMMENT = ((int)DTM.COMMENT_NODE) ; 060 public static final int DOCUMENT = ((int)DTM.DOCUMENT_NODE) ; 061 public static final int DOCUMENT_TYPE = ((int)DTM.DOCUMENT_TYPE_NODE) ; 062 public static final int DOCUMENT_FRAGMENT =((int)DTM.DOCUMENT_FRAGMENT_NODE) ; 063 public static final int NOTATION = ((int)DTM.NOTATION_NODE) ; 064 public static final int NAMESPACE = ((int)DTM.NAMESPACE_NODE) ; 065 066 /** Workspace for lookup. NOT THREAD SAFE! 067 * */ 068 ExtendedType hashET = new ExtendedType(-1, "", ""); 069 070 /** The array to store the default extended types. */ 071 private static ExtendedType[] m_defaultExtendedTypes; 072 073 /** 074 * The default load factor of the Hashtable. 075 * This is used to calcualte the threshold. 076 */ 077 private static float m_loadFactor = 0.75f; 078 079 /** 080 * The initial capacity of the hash table. Use a bigger number 081 * to avoid the cost of expanding the table. 082 */ 083 private static int m_initialCapacity = 203; 084 085 /** 086 * The capacity of the hash table, i.e. the size of the 087 * internal HashEntry array. 088 */ 089 private int m_capacity; 090 091 /** 092 * The threshold of the hash table, which is equal to capacity * loadFactor. 093 * If the number of entries in the hash table is bigger than the threshold, 094 * the hash table needs to be expanded. 095 */ 096 private int m_threshold; 097 098 /** 099 * The internal array to store the hash entries. 100 * Each array member is a slot for a hash bucket. 101 */ 102 private HashEntry[] m_table; 103 104 /** 105 * Init default values 106 */ 107 static { 108 m_defaultExtendedTypes = new ExtendedType[DTM.NTYPES]; 109 110 for (int i = 0; i < DTM.NTYPES; i++) 111 { 112 m_defaultExtendedTypes[i] = new ExtendedType(i, "", ""); 113 } 114 } 115 116 /** 117 * Create an expanded name table. 118 */ 119 public ExpandedNameTable() 120 { 121 m_capacity = m_initialCapacity; 122 m_threshold = (int)(m_capacity * m_loadFactor); 123 m_table = new HashEntry[m_capacity]; 124 125 initExtendedTypes(); 126 } 127 128 129 /** 130 * Initialize the vector of extended types with the 131 * basic DOM node types. 132 */ 133 private void initExtendedTypes() 134 { 135 m_extendedTypes = new ExtendedType[m_initialSize]; 136 for (int i = 0; i < DTM.NTYPES; i++) { 137 m_extendedTypes[i] = m_defaultExtendedTypes[i]; 138 m_table[i] = new HashEntry(m_defaultExtendedTypes[i], i, i, null); 139 } 140 141 m_nextType = DTM.NTYPES; 142 } 143 144 /** 145 * Given an expanded name represented by namespace, local name and node type, 146 * return an ID. If the expanded-name does not exist in the internal tables, 147 * the entry will be created, and the ID will be returned. Any additional 148 * nodes that are created that have this expanded name will use this ID. 149 * 150 * @param namespace The namespace 151 * @param localName The local name 152 * @param type The node type 153 * 154 * @return the expanded-name id of the node. 155 */ 156 public int getExpandedTypeID(String namespace, String localName, int type) 157 { 158 return getExpandedTypeID(namespace, localName, type, false); 159 } 160 161 /** 162 * Given an expanded name represented by namespace, local name and node type, 163 * return an ID. If the expanded-name does not exist in the internal tables, 164 * the entry will be created, and the ID will be returned. Any additional 165 * nodes that are created that have this expanded name will use this ID. 166 * <p> 167 * If searchOnly is true, we will return -1 if the name is not found in the 168 * table, otherwise the name is added to the table and the expanded name id 169 * of the new entry is returned. 170 * 171 * @param namespace The namespace 172 * @param localName The local name 173 * @param type The node type 174 * @param searchOnly If it is true, we will only search for the expanded name. 175 * -1 is return is the name is not found. 176 * 177 * @return the expanded-name id of the node. 178 */ 179 public int getExpandedTypeID(String namespace, String localName, int type, boolean searchOnly) 180 { 181 if (null == namespace) 182 namespace = ""; 183 if (null == localName) 184 localName = ""; 185 186 // Calculate the hash code 187 int hash = type + namespace.hashCode() + localName.hashCode(); 188 189 // Redefine the hashET object to represent the new expanded name. 190 hashET.redefine(type, namespace, localName, hash); 191 192 // Calculate the index into the HashEntry table. 193 int index = hash % m_capacity; 194 if (index < 0) 195 index = -index; 196 197 // Look up the expanded name in the hash table. Return the id if 198 // the expanded name is already in the hash table. 199 for (HashEntry e = m_table[index]; e != null; e = e.next) 200 { 201 if (e.hash == hash && e.key.equals(hashET)) 202 return e.value; 203 } 204 205 if (searchOnly) 206 { 207 return DTM.NULL; 208 } 209 210 // Expand the internal HashEntry array if necessary. 211 if (m_nextType > m_threshold) { 212 rehash(); 213 index = hash % m_capacity; 214 if (index < 0) 215 index = -index; 216 } 217 218 // Create a new ExtendedType object 219 ExtendedType newET = new ExtendedType(type, namespace, localName, hash); 220 221 // Expand the m_extendedTypes array if necessary. 222 if (m_extendedTypes.length == m_nextType) { 223 ExtendedType[] newArray = new ExtendedType[m_extendedTypes.length * 2]; 224 System.arraycopy(m_extendedTypes, 0, newArray, 0, 225 m_extendedTypes.length); 226 m_extendedTypes = newArray; 227 } 228 229 m_extendedTypes[m_nextType] = newET; 230 231 // Create a new hash entry for the new ExtendedType and put it into 232 // the table. 233 HashEntry entry = new HashEntry(newET, m_nextType, hash, m_table[index]); 234 m_table[index] = entry; 235 236 return m_nextType++; 237 } 238 239 /** 240 * Increases the capacity of and internally reorganizes the hashtable, 241 * in order to accommodate and access its entries more efficiently. 242 * This method is called when the number of keys in the hashtable exceeds 243 * this hashtable's capacity and load factor. 244 */ 245 private void rehash() 246 { 247 int oldCapacity = m_capacity; 248 HashEntry[] oldTable = m_table; 249 250 int newCapacity = 2 * oldCapacity + 1; 251 m_capacity = newCapacity; 252 m_threshold = (int)(newCapacity * m_loadFactor); 253 254 m_table = new HashEntry[newCapacity]; 255 for (int i = oldCapacity-1; i >=0 ; i--) 256 { 257 for (HashEntry old = oldTable[i]; old != null; ) 258 { 259 HashEntry e = old; 260 old = old.next; 261 262 int newIndex = e.hash % newCapacity; 263 if (newIndex < 0) 264 newIndex = -newIndex; 265 266 e.next = m_table[newIndex]; 267 m_table[newIndex] = e; 268 } 269 } 270 } 271 272 /** 273 * Given a type, return an expanded name ID.Any additional nodes that are 274 * created that have this expanded name will use this ID. 275 * 276 * @return the expanded-name id of the node. 277 */ 278 public int getExpandedTypeID(int type) 279 { 280 return type; 281 } 282 283 /** 284 * Given an expanded-name ID, return the local name part. 285 * 286 * @param ExpandedNameID an ID that represents an expanded-name. 287 * @return String Local name of this node, or null if the node has no name. 288 */ 289 public String getLocalName(int ExpandedNameID) 290 { 291 return m_extendedTypes[ExpandedNameID].getLocalName(); 292 } 293 294 /** 295 * Given an expanded-name ID, return the local name ID. 296 * 297 * @param ExpandedNameID an ID that represents an expanded-name. 298 * @return The id of this local name. 299 */ 300 public final int getLocalNameID(int ExpandedNameID) 301 { 302 // ExtendedType etype = m_extendedTypes[ExpandedNameID]; 303 if (m_extendedTypes[ExpandedNameID].getLocalName().length() == 0) 304 return 0; 305 else 306 return ExpandedNameID; 307 } 308 309 310 /** 311 * Given an expanded-name ID, return the namespace URI part. 312 * 313 * @param ExpandedNameID an ID that represents an expanded-name. 314 * @return String URI value of this node's namespace, or null if no 315 * namespace was resolved. 316 */ 317 public String getNamespace(int ExpandedNameID) 318 { 319 String namespace = m_extendedTypes[ExpandedNameID].getNamespace(); 320 return (namespace.length() == 0 ? null : namespace); 321 } 322 323 /** 324 * Given an expanded-name ID, return the namespace URI ID. 325 * 326 * @param ExpandedNameID an ID that represents an expanded-name. 327 * @return The id of this namespace. 328 */ 329 public final int getNamespaceID(int ExpandedNameID) 330 { 331 //ExtendedType etype = m_extendedTypes[ExpandedNameID]; 332 if (m_extendedTypes[ExpandedNameID].getNamespace().length() == 0) 333 return 0; 334 else 335 return ExpandedNameID; 336 } 337 338 /** 339 * Given an expanded-name ID, return the local name ID. 340 * 341 * @param ExpandedNameID an ID that represents an expanded-name. 342 * @return The id of this local name. 343 */ 344 public final short getType(int ExpandedNameID) 345 { 346 //ExtendedType etype = m_extendedTypes[ExpandedNameID]; 347 return (short)m_extendedTypes[ExpandedNameID].getNodeType(); 348 } 349 350 /** 351 * Return the size of the ExpandedNameTable 352 * 353 * @return The size of the ExpandedNameTable 354 */ 355 public int getSize() 356 { 357 return m_nextType; 358 } 359 360 /** 361 * Return the array of extended types 362 * 363 * @return The array of extended types 364 */ 365 public ExtendedType[] getExtendedTypes() 366 { 367 return m_extendedTypes; 368 } 369 370 /** 371 * Inner class which represents a hash table entry. 372 * The field next points to the next entry which is hashed into 373 * the same bucket in the case of "hash collision". 374 */ 375 private static final class HashEntry 376 { 377 ExtendedType key; 378 int value; 379 int hash; 380 HashEntry next; 381 382 protected HashEntry(ExtendedType key, int value, int hash, HashEntry next) 383 { 384 this.key = key; 385 this.value = value; 386 this.hash = hash; 387 this.next = next; 388 } 389 } 390 391 }