1/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the  "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 *     http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18/*
19 * $Id: ExpandedNameTable.java 468653 2006-10-28 07:07:05Z minchau $
20 */
21package org.apache.xml.dtm.ref;
22
23import org.apache.xml.dtm.DTM;
24
25/**
26 * This is a default implementation of a table that manages mappings from
27 * expanded names to expandedNameIDs.
28 *
29 * %OPT% The performance of the getExpandedTypeID() method is very important
30 * to DTM building. To get the best performance out of this class, we implement
31 * a simple hash algorithm directly into this class, instead of using the
32 * inefficient java.util.Hashtable. The code for the get and put operations
33 * are combined in getExpandedTypeID() method to share the same hash calculation
34 * code. We only need to implement the rehash() interface which is used to
35 * expand the hash table.
36 */
37public class ExpandedNameTable
38{
39
40  /** Array of extended types for this document   */
41  private ExtendedType[] m_extendedTypes;
42
43  /** The initial size of the m_extendedTypes array */
44  private static int m_initialSize = 128;
45
46  /** Next available extended type   */
47  // %REVIEW% Since this is (should be) always equal
48  // to the length of m_extendedTypes, do we need this?
49  private int m_nextType;
50
51  // These are all the types prerotated, for caller convenience.
52  public static final int ELEMENT = ((int)DTM.ELEMENT_NODE) ;
53  public static final int ATTRIBUTE = ((int)DTM.ATTRIBUTE_NODE) ;
54  public static final int TEXT = ((int)DTM.TEXT_NODE) ;
55  public static final int CDATA_SECTION = ((int)DTM.CDATA_SECTION_NODE) ;
56  public static final int ENTITY_REFERENCE = ((int)DTM.ENTITY_REFERENCE_NODE) ;
57  public static final int ENTITY = ((int)DTM.ENTITY_NODE) ;
58  public static final int PROCESSING_INSTRUCTION = ((int)DTM.PROCESSING_INSTRUCTION_NODE) ;
59  public static final int COMMENT = ((int)DTM.COMMENT_NODE) ;
60  public static final int DOCUMENT = ((int)DTM.DOCUMENT_NODE) ;
61  public static final int DOCUMENT_TYPE = ((int)DTM.DOCUMENT_TYPE_NODE) ;
62  public static final int DOCUMENT_FRAGMENT =((int)DTM.DOCUMENT_FRAGMENT_NODE) ;
63  public static final int NOTATION = ((int)DTM.NOTATION_NODE) ;
64  public static final int NAMESPACE = ((int)DTM.NAMESPACE_NODE) ;
65
66  /** Workspace for lookup. NOT THREAD SAFE!
67   * */
68  ExtendedType hashET = new ExtendedType(-1, "", "");
69
70  /** The array to store the default extended types. */
71  private static ExtendedType[] m_defaultExtendedTypes;
72
73  /**
74   * The default load factor of the Hashtable.
75   * This is used to calcualte the threshold.
76   */
77  private static float m_loadFactor = 0.75f;
78
79  /**
80   * The initial capacity of the hash table. Use a bigger number
81   * to avoid the cost of expanding the table.
82   */
83  private static int m_initialCapacity = 203;
84
85  /**
86   * The capacity of the hash table, i.e. the size of the
87   * internal HashEntry array.
88   */
89  private int m_capacity;
90
91  /**
92   * The threshold of the hash table, which is equal to capacity * loadFactor.
93   * If the number of entries in the hash table is bigger than the threshold,
94   * the hash table needs to be expanded.
95   */
96  private int m_threshold;
97
98  /**
99   * 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().equals(""))
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.equals("") ? 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().equals(""))
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}
392