/*
 * eXist-db Open Source Native XML Database
 * Copyright (C) 2001 The eXist-db Authors
 *
 * info@exist-db.org
 * http://www.exist-db.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
package org.exist.util.hashtable;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import net.jcip.annotations.ThreadSafe;
import org.exist.dom.QName;
import org.exist.xquery.Constants;

/**
 * @author Pieter Deelen
 */
@ThreadSafe
public class NamePool {

    private final ConcurrentMap<WrappedQName, QName> pool;

    public NamePool() {
        pool = new ConcurrentHashMap<>();
    }

    public QName getSharedName(final QName name) {
        final WrappedQName wrapped = new WrappedQName(name);
        final QName sharedName = pool.putIfAbsent(wrapped, name);
        if (sharedName == null) {
            // The name was not in the pool, return the name just added.
            return name;
        } else {
            // The name was in the pool, return the shared name.
            return sharedName;
        }
    }

    /**
     * QName ignores nameType and prefix when testing for equality.
     * Wrap it to overwrite those methods.
     */
    private static class WrappedQName implements Comparable<WrappedQName> {
        private final QName qname;

        public WrappedQName(final QName qname) {
            this.qname = qname;
        }

        @Override
        public int compareTo(final WrappedQName other) {
            if (qname.getNameType() != other.qname.getNameType()) {
                return qname.getNameType() < other.qname.getNameType() ? Constants.INFERIOR : Constants.SUPERIOR;
            }
            final int c;
            if (qname.getNamespaceURI() == null) {
                c = other.qname.getNamespaceURI() == null ? Constants.EQUAL : Constants.INFERIOR;
            } else if (other.qname.getNamespaceURI() == null) {
                c = Constants.SUPERIOR;
            } else {
                c = other.qname.getNamespaceURI().compareTo(other.qname.getNamespaceURI());
            }
            return c == Constants.EQUAL ? qname.getLocalPart().compareTo(other.qname.getLocalPart()) : c;
        }

        @Override
        public int hashCode() {
            int h = qname.getNameType() + 31 + qname.getLocalPart().hashCode();
            h += 31 * h + (qname.getNamespaceURI() == null ? 1 : qname.getNamespaceURI().hashCode());
            h += 31 * h + (qname.getPrefix() == null ? 1 : qname.getPrefix().hashCode());
            return h;
        }

        @Override
        public boolean equals(final Object obj) {
            if (obj == null || !(obj instanceof WrappedQName)) {
                return false;
            }

            final WrappedQName other = (WrappedQName) obj;
            final int cmp = compareTo(other);
            if (cmp != 0) {
                return false;
            }

            if (qname.getPrefix() == null) {
                return other.qname.getPrefix() == null;
            } else if (other.qname.getPrefix() == null) {
                return false;
            } else {
                return qname.getPrefix().equals(other.qname.getPrefix());
            }
        }
    }
}

@@ -2648,32 +2648,95 @@
        %7D else %7B%0A
+                // DW: which one needs to be changed to qname?%0A
                 

Add remark/question