/* Jackson JSON-processor.
 *
 * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi
 */

package org.gridkit.jvmtool.jackson;

/**
 * Object that encapsulates Location information used for reporting
 * parsing (or potentially generation) errors, as well as current location
 * within input streams.
 */
public class JsonLocation
    implements java.io.Serializable // as per [JACKSON-168]
{
    private static final long serialVersionUID = 1L;

    /**
     * Shared immutable "N/A location" that can be returned to indicate
     * that no location information is available
     */
    public final static JsonLocation NA = new JsonLocation("N/A", -1L, -1L, -1, -1);

    final long _totalBytes;
    final long _totalChars;

    final int _lineNr;
    final int _columnNr;

    /**
     * Displayable description for input source: file path, URL.
     *<p>
     * NOTE: <code>transient</code> since 2.2 so that Location itself is Serializable.
     */
    final transient Object _sourceRef;

    public JsonLocation(Object srcRef, long totalChars, int lineNr, int colNr)
    {
        /* Unfortunately, none of legal encodings are straight single-byte
         * encodings. Could determine offset for UTF-16/UTF-32, but the
         * most important one is UTF-8...
         * so for now, we'll just not report any real byte count
         */
        this(srcRef, -1L, totalChars, lineNr, colNr);
    }

    public JsonLocation(Object sourceRef, long totalBytes, long totalChars,
            int lineNr, int columnNr)
    {
        _sourceRef = sourceRef;
        _totalBytes = totalBytes;
        _totalChars = totalChars;
        _lineNr = lineNr;
        _columnNr = columnNr;
    }

    /**
     * Reference to the original resource being read, if one available.
     * For example, when a parser has been constructed by passing
     * a {@link java.io.File} instance, this method would return
     * that File. Will return null if no such reference is available,
     * for example when {@link java.io.InputStream} was used to
     * construct the parser instance.
     */
    public Object getSourceRef() { return _sourceRef; }

    /**
     * @return Line number of the location (1-based)
     */
    public int getLineNr() { return _lineNr; }

    /**
     * @return Column number of the location (1-based)
     */
    public int getColumnNr() { return _columnNr; }

    /**
     * @return Character offset within underlying stream, reader or writer,
     *   if available; -1 if not.
     */
    public long getCharOffset() { return _totalChars; }

    /**
     * @return Byte offset within underlying stream, reader or writer,
     *   if available; -1 if not.
     */
    public long getByteOffset()
    {
        return _totalBytes;
    }

    @Override
    public String toString()
    {
        StringBuilder sb = new StringBuilder(80);
        sb.append("[Source: ");
        if (_sourceRef == null) {
            sb.append("UNKNOWN");
        } else {
            sb.append(_sourceRef.toString());
        }
        sb.append("; line: ");
        sb.append(_lineNr);
        sb.append(", column: ");
        sb.append(_columnNr);
        sb.append(']');
        return sb.toString();
    }

    @Override
    public int hashCode()
    {
        int hash = (_sourceRef == null) ? 1 : _sourceRef.hashCode();
        hash ^= _lineNr;
        hash += _columnNr;
        hash ^= (int) _totalChars;
        hash += (int) _totalBytes;
        return hash;
    }

    @Override
    public boolean equals(Object other)
    {
        if (other == this) return true;
        if (other == null) return false;
        if (!(other instanceof JsonLocation)) return false;
        JsonLocation otherLoc = (JsonLocation) other;

        if (_sourceRef == null) {
            if (otherLoc._sourceRef != null) return false;
        } else if (!_sourceRef.equals(otherLoc._sourceRef)) return false;

        return (_lineNr == otherLoc._lineNr)
            && (_columnNr == otherLoc._columnNr)
            && (_totalChars == otherLoc._totalChars)
            && (getByteOffset() == otherLoc.getByteOffset())
            ;
    }
}
