1 /* Copyright 2004 The Apache Software Foundation 2 * 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 package org.apache.xmlbeans; 17 18 import javax.xml.namespace.QName; 19 20 /** 21 * A cache that can be used to pool QName instances. Each thread has one. 22 */ 23 public final class QNameCache 24 { 25 private static final float DEFAULT_LOAD = 0.70f; 26 private final float loadFactor; 27 private int numEntries = 0; 28 private int threshold; 29 private int hashmask; 30 private QName[] table; 31 32 /** 33 * Creates a QNameCache with the given initialCapacity and loadFactor. 34 * 35 * @param initialCapacity the number of entries to initially make space for 36 * @param loadFactor a number to control the density of the hashtable 37 */ 38 public QNameCache(int initialCapacity, float loadFactor) 39 { 40 assert initialCapacity > 0; 41 assert loadFactor > 0 && loadFactor < 1; 42 43 // Find a power of 2 >= initialCapacity 44 int capacity = 16; 45 while (capacity < initialCapacity) 46 capacity <<= 1; 47 48 this.loadFactor = loadFactor; 49 this.hashmask = capacity - 1; 50 threshold = (int)(capacity * loadFactor); 51 table = new QName[capacity]; 52 } 53 54 /** 55 * Creates a QNameCache with the given initialCapacity. 56 * 57 * @param initialCapacity the number of entries to initially make space for 58 */ 59 public QNameCache(int initialCapacity) 60 { 61 this(initialCapacity, DEFAULT_LOAD); 62 } 63 64 public QName getName(String uri, String localName) 65 { 66 return getName( uri, localName, "" ); 67 } 68 69 /** 70 * Fetches a QName with the given namespace and localname. 71 * Creates one if one is not found in the cache. 72 * 73 * @param uri the namespace 74 * @param localName the localname 75 * @param prefix the prefix 76 * @return the cached QName 77 */ 78 public QName getName(String uri, String localName, String prefix) 79 { 80 /* 81 return new QName(uri, localName, prefix); 82 */ 83 assert localName != null; 84 85 if (uri == null) uri = ""; 86 if (prefix == null) prefix = ""; 87 88 int index = hash(uri, localName, prefix) & hashmask; 89 while (true) { 90 QName q = table[index]; 91 if (q == null) 92 { 93 numEntries++; 94 if (numEntries >= threshold) 95 rehash(); 96 97 return table[index] = new QName(uri, localName, prefix); 98 } 99 else if (equals(q, uri, localName, prefix)) 100 return q; 101 else 102 index = (index-1) & hashmask; 103 } 104 } 105 106 private void rehash() 107 { 108 int newLength = table.length * 2; 109 QName[] newTable = new QName[newLength]; 110 int newHashmask = newLength - 1; 111 112 for (int i = 0 ; i < table.length ; i++) 113 { 114 QName q = table[i]; 115 if (q != null) 116 { 117 int newIndex = 118 hash( q.getNamespaceURI(), q.getLocalPart(), q.getPrefix() ) & newHashmask; 119 120 while (newTable[newIndex] != null) 121 newIndex = (newIndex - 1) & newHashmask; 122 123 newTable[newIndex] = q; 124 } 125 } 126 127 table = newTable; 128 hashmask = newHashmask; 129 threshold = (int) (newLength * loadFactor); 130 } 131 132 private static int hash(String uri, String localName, String prefix) 133 { 134 int h = 0; 135 136 h += prefix.hashCode() << 10; 137 h += uri.hashCode() << 5; 138 h += localName.hashCode(); 139 140 return h; 141 } 142 143 private static boolean equals(QName q, String uri, String localName, String prefix) 144 { 145 return 146 q.getLocalPart().equals(localName) && 147 q.getNamespaceURI().equals(uri) && 148 q.getPrefix().equals(prefix); 149 } 150 }