All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.glassfish.jaxb.runtime.v2.util.QNameMap Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 1997, 2022 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Distribution License v. 1.0, which is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

package org.glassfish.jaxb.runtime.v2.util;

import org.glassfish.jaxb.runtime.v2.runtime.Name;

import javax.xml.namespace.QName;
import java.util.*;

/**
 * Map keyed by {@link QName}.
 *
 * This specialized map allows a look up operation without constructing
 * a new QName instance, for a performance reason. This {@link Map} assumes
 * that both namespace URI and local name are {@link String#intern() intern}ed.
 *
 * @since JAXB 2.0
 */
public final class QNameMap {
    /**
     * The default initial capacity - MUST be a power of two.
     */
    private static final int DEFAULT_INITIAL_CAPACITY = 16;

    /**
     * The maximum capacity, used if a higher value is implicitly specified
     * by either of the constructors with arguments.
     * MUST be a power of two {@literal <= 1<<30}.
     */
    private static final int MAXIMUM_CAPACITY = 1 << 30;

    /**
     * The table, resized as necessary. Length MUST Always be a power of two.
     */
    transient Entry[] table = new Entry[DEFAULT_INITIAL_CAPACITY];

    /**
     * The number of key-value mappings contained in this identity hash map.
     */
    transient int size;

    /**
     * The next size value at which to resize . Taking it as
     * MAXIMUM_CAPACITY
     * @serial
     */
    private int threshold;

    /**
     * The load factor used when none specified in constructor.
     **/
    private static final float DEFAULT_LOAD_FACTOR = 0.75f;



    /**
     * Gives an entrySet view of this map
     */
    private Set> entrySet = null;

    public QNameMap() {
        threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
        table = new Entry[DEFAULT_INITIAL_CAPACITY];

    }

    /**
     * Associates the specified value with the specified keys in this map.
     * If the map previously contained a mapping for this key, the old
     * value is replaced.
     *
     * @param namespaceUri First key with which the specified value is to be associated.
     * @param localname Second key with which the specified value is to be associated.
     * @param value value to be associated with the specified key.
     *
     */
    public void put(String namespaceUri,String localname, V value ) {
        //keys cannot be null
        assert localname !=null;
        assert namespaceUri !=null;
        // keys must be interned
        assert localname == localname.intern();
        assert namespaceUri == namespaceUri.intern();

        int hash = hash(localname);
        int i = indexFor(hash, table.length);

        for (Entry e = table[i]; e != null; e = e.next) {
            if (e.hash == hash && localname == e.localName && namespaceUri==e.nsUri) {
                e.value = value;
                return;
            }
        }

        addEntry(hash, namespaceUri,localname, value, i);

    }

    public void put(QName name, V value ) {
        put(name.getNamespaceURI(),name.getLocalPart(),value);
    }

    public void put(Name name, V value ) {
        put(name.nsUri,name.localName,value);
    }

    /**
     * Returns the value to which the specified keys are mapped in this QNameMap,
     * or {@code null} if the map contains no mapping for this key.
     *
     * @param   nsUri the namespaceUri key whose associated value is to be returned.
     * @param   localPart the localPart key whose associated value is to be returned.
     * @return  the value to which this map maps the specified set of keya, or
     *          {@code null} if the map contains no mapping for this set of keys.
     * @see #put(String,String, Object)
     */
    public V get( String nsUri, String localPart ) {
        Entry e = getEntry(nsUri,localPart);
        if(e==null) return null;
        else        return e.value;
        }

    public V get( QName name ) {
        return get(name.getNamespaceURI(),name.getLocalPart());
    }

    /**
     * Returns the number of keys-value mappings in this map.
     *
     * @return the number of keys-value mappings in this map.
     */
    public int size() {
        return size;
    }

    /**
     * Copies all of the mappings from the specified map to this map
     * These mappings will replace any mappings that
     * this map had for any of the keys currently in the specified map.
     *
     * @param map mappings to be stored in this map.
     *
     */
    public QNameMap putAll(QNameMap map) {
        int numKeysToBeAdded = map.size();
        if (numKeysToBeAdded == 0)
            return this;


        if (numKeysToBeAdded > threshold) {
            int targetCapacity = numKeysToBeAdded;
            if (targetCapacity > MAXIMUM_CAPACITY)
                targetCapacity = MAXIMUM_CAPACITY;
            int newCapacity = table.length;
            while (newCapacity < targetCapacity)
                newCapacity <<= 1;
            if (newCapacity > table.length)
                resize(newCapacity);
        }

        for( Entry e : map.entrySet() )
            put(e.nsUri,e.localName,e.getValue());
        return this;
    }


    /**
     * Returns a hash value for the specified object.The hash value is computed
     * for the localName.
     */
    private static int hash(String x) {
        int h = x.hashCode();

        h += ~(h << 9);
        h ^=  (h >>> 14);
        h +=  (h << 4);
        h ^=  (h >>> 10);
        return h;
    }

    /**
     * Returns index for hash code h.
     */
    private static int indexFor(int h, int length) {
        return h & (length-1);
    }

    /**
     * Add a new entry with the specified keys, value and hash code to
     * the specified bucket.  It is the responsibility of this
     * method to resize the table if appropriate.
     *
     */
    private void addEntry(int hash, String nsUri, String localName, V value, int bucketIndex) {
        Entry e = table[bucketIndex];
        table[bucketIndex] = new Entry<>(hash, nsUri, localName, value, e);
        if (size++ >= threshold)
            resize(2 * table.length);
    }


    /**
     * Rehashes the contents of this map into a new array with a
     * larger capacity.  This method is called automatically when the
     * number of keys in this map reaches its threshold.
     */
    private void resize(int newCapacity) {
        Entry[] oldTable = table;
        int oldCapacity = oldTable.length;
        if (oldCapacity == MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return;
        }

        @SuppressWarnings({"unchecked"})
        Entry[] newTable = (Entry[]) new Entry[newCapacity];
        transfer(newTable);
        table = newTable;
        threshold = newCapacity;
    }

    /**
     * Transfer all entries from current table to newTable.
     */
    private void transfer(Entry[] newTable) {
        Entry[] src = table;
        int newCapacity = newTable.length;
        for (int j = 0; j < src.length; j++) {
            Entry e = src[j];
            if (e != null) {
                src[j] = null;
                do {
                    Entry next = e.next;
                    int i = indexFor(e.hash, newCapacity);
                    e.next = newTable[i];
                    newTable[i] = e;
                    e = next;
                } while (e != null);
            }
        }
    }

    /**
     * Returns one random item in the map.
     * If this map is empty, return null.
     *
     * 

* This method is useful to obtain the value from a map that only contains one element. */ public Entry getOne() { for( Entry e : table ) { if(e!=null) return e; } return null; } public Collection keySet() { Set r = new HashSet<>(); for (Entry e : entrySet()) { r.add(e.createQName()); } return r; } private abstract class HashIterator implements Iterator { Entry next; // next entry to return int index; // current slot HashIterator() { Entry[] t = table; int i = t.length; Entry n = null; if (size != 0) { // advance to first entry while (i > 0 && (n = t[--i]) == null) {} } next = n; index = i; } @Override public boolean hasNext() { return next != null; } Entry nextEntry() { Entry e = next; if (e == null) throw new NoSuchElementException(); Entry n = e.next; Entry[] t = table; int i = index; while (n == null && i > 0) n = t[--i]; index = i; next = n; return e; } @Override public void remove() { throw new UnsupportedOperationException(); } } public boolean containsKey(String nsUri,String localName) { return getEntry(nsUri,localName)!=null; } /** * Returns true if this map is empty. */ public boolean isEmpty() { return size == 0; } public static final class Entry { /** The namespace URI. */ public final String nsUri; /** The localPart. */ public final String localName; V value; final int hash; Entry next; /** * Create new entry. */ Entry(int h, String nsUri, String localName, V v, Entry n) { value = v; next = n; this.nsUri = nsUri; this.localName = localName; hash = h; } /** * Creates a new QName object from {@link #nsUri} and {@link #localName}. */ public QName createQName() { return new QName(nsUri,localName); } public V getValue() { return value; } public V setValue(V newValue) { V oldValue = value; value = newValue; return oldValue; } @Override public boolean equals(Object o) { if (!(o instanceof Entry)) return false; Entry e = (Entry)o; String k1 = nsUri; String k2 = e.nsUri; String k3 = localName; String k4 = e.localName; if (k1 == k2 || (k1 != null && k1.equals(k2)) && (Objects.equals(k3, k4))) { Object v1 = getValue(); Object v2 = e.getValue(); return Objects.equals(v1, v2); } return false; } @Override public int hashCode() { return ( localName.hashCode()) ^ (value==null ? 0 : value.hashCode()); } @Override public String toString() { return '"'+nsUri +"\",\"" +localName + "\"=" + getValue(); } } public Set> entrySet() { Set> es = entrySet; return es != null ? es : (entrySet = new EntrySet()); } private Iterator> newEntryIterator() { return new EntryIterator(); } private class EntryIterator extends HashIterator> { @Override public Entry next() { return nextEntry(); } } private class EntrySet extends AbstractSet> { @Override public Iterator> iterator() { return newEntryIterator(); } @Override public boolean contains(Object o) { if (!(o instanceof Entry)) return false; Entry e = (Entry) o; Entry candidate = getEntry(e.nsUri,e.localName); return candidate != null && candidate.equals(e); } @Override public boolean remove(Object o) { throw new UnsupportedOperationException(); } @Override public int size() { return size; } } private Entry getEntry(String nsUri,String localName) { // strings must be interned assert nsUri==nsUri.intern(); assert localName==localName.intern(); int hash = hash(localName); int i = indexFor(hash, table.length); Entry e = table[i]; while (e != null && !(localName == e.localName && nsUri == e.nsUri)) e = e.next; return e; } @Override public String toString() { StringBuilder buf = new StringBuilder(); buf.append('{'); for( Entry e : entrySet() ) { if(buf.length()>1) buf.append(','); buf.append('['); buf.append(e); buf.append(']'); } buf.append('}'); return buf.toString(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy