/* Woodstox Lite ("wool") XML processor
 *
 * Copyright (c) 2006- Tatu Saloranta, tatu.saloranta@iki.fi
 *
 * Licensed under the License specified in the file LICENSE which is
 * included with the source code.
 * You may not use this file except in compliance with the License.
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.fasterxml.aalto.in;

import com.fasterxml.aalto.impl.ErrorConsts;

/**
 * An alternate implementation of PName: instead of coming straight from
 * byte contents, it is actually just built from a character array.
 *<p>
 * Note: one unfortunate result of this being a somewhat different PName
 * is that equality comparison between this and other implementations will not
 * work as expected. As such, these should only be used as temporary names.
 */
public final class PNameC
    extends PName
{
    /**
     * Since the hash may be calculated different from the way eventual
     * String's hash will be (right now it is not), we better store
     * "our" hash here.
     */
    protected final int mHash;

    public PNameC(String pname, String prefix, String ln, int hash)
    {
        super(pname, prefix, ln);
        mHash = hash;
    }

    @Override
    public PName createBoundName(NsBinding nsb)
    {
        PNameC newName = new PNameC(_prefixedName, _prefix, _localName, mHash);
        newName._namespaceBinding = nsb;
        return newName;
    }

    public static PNameC construct(String pname)
    {
        return construct(pname, calcHash(pname));
    }

    public static PNameC construct(String pname, int hash)
    {
        int colonIx = pname.indexOf(':');
        if (colonIx < 0) {
            return new PNameC(pname, null, pname, hash);
        }
        /* !!! TODO: cache prefix intern() calls, since they are bound
         * to cluster nicely (and quite often within same thread too)
         */
        return new PNameC(pname, pname.substring(0, colonIx).intern(),
                          pname.substring(colonIx+1).intern(), hash);
    }

    /*
    //////////////////////////////////////////////////////////
    // Sub-class API
    //////////////////////////////////////////////////////////
     */

    public boolean equalsPName(char[] buffer, int start, int len, int hash)
    {
        if (hash != mHash) {
            return false;
        }
        String pname = _prefixedName;
        int plen = pname.length();
        if (len != plen) {
            return false;
        }
        for (int i = 0; i < len; ++i) {
            if (buffer[start+i] != pname.charAt(i)) {
                return false;
            }
        }
        return true;
    }

    public int getCustomHash() { return mHash; }

    /**
     * Implementation of a hashing method for variable length
     * Strings. Most of the time intention is that this calculation
     * is done by caller during parsing, not here; however, sometimes
     * it needs to be done for parsed "String" too.
     *
     * @param len Length of String; has to be at least 1 (caller guarantees
     *   this pre-condition)
     */
    public static int calcHash(char[] buffer, int start, int len) {
        int hash = (int) buffer[0];
        for (int i = 1; i < len; ++i) {
            hash = (hash * 31) + (int) buffer[i];
        }
        return hash;
    }

    public static int calcHash(String key) {
        int hash = (int) key.charAt(0);
        for (int i = 1, len = key.length(); i < len; ++i) {
            hash = (hash * 31) + (int) key.charAt(i);

        }
        return hash;
    }

    /*
    //////////////////////////////////////////////////////////
    // Methods for package/core parser
    //////////////////////////////////////////////////////////
     */

    /**
     * This method should never get called on instances of this class,
     * so let's throw an exception if that does happen.
     */
    @Override
    public int sizeInQuads()
    {
        ErrorConsts.throwInternalError();
        return 0; // never gets here
    }

    @Override
    public int getFirstQuad()
    {
        ErrorConsts.throwInternalError();
        return 0; // never gets here
    }

    @Override
    public final int getLastQuad()
    {
        ErrorConsts.throwInternalError();
        return 0; // never gets here
    }

    @Override
    public int getQuad(int index)
    {
        ErrorConsts.throwInternalError();
        return 0; // never gets here
    }

    /*
    //////////////////////////////////////////////////////////
    // Redefined standard methods
    //////////////////////////////////////////////////////////
     */

    /**
     * Whether we should use internal hash, or the hash of prefixed
     * name string itself is an open question. For now, let's use
     * former.
     */
    @Override
    public int hashCode() { return mHash; }
}
