package javolution.xml.stream;

import java.lang.CharSequence;
import javax.realtime.MemoryArea;
import javolution.lang.Reusable;
import javolution.text.CharArray;
import javolution.text.Text;
import javolution.xml.sax.Attributes;

/**
 * This class provides the implementation of the {@link Attributes}
 * interface for the StAX parser. 
 * 
 * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
 */
final class AttributesImpl implements Attributes, Reusable {

    /**
     * Holds the local names.
     */
    private CharArray[] _localNames = new CharArray[16];

    /**
     * Holds the prefixes.
     */
    private CharArray[] _prefixes = new CharArray[16];

    /**
     * Holds the qualified names.
     */
    private CharArray[] _qNames = new CharArray[16];

    /**
     * Holds the values.
     */
    private CharArray[] _values = new CharArray[16];

    /**
     * Holds the namespace stack.
     */
    private final NamespacesImpl _namespaces;

    /**
     * Holds the current number of attributes.
     */
    private int _length;

    ////////////////////////////////////////////////////////////////////
    // Constructors.
    ////////////////////////////////////////////////////////////////////

    /**
     * Creates a list of attribute using the specified namespace stack.
     */
    public AttributesImpl(NamespacesImpl namespaces) {
        _namespaces = namespaces;
    }

    // Implements Attributes.
    public int getLength() {
        return _length;
    }

    // Implements Attributes.
    public CharArray getURI(int index) {
        return (index >= 0 && index < _length) ? _namespaces
                .getNamespaceURINullAllowed(_prefixes[index]) : null;
    }

    // Implements Attributes.
    public CharArray getLocalName(int index) {
        return (index >= 0 && index < _length) ? _localNames[index] : null;
    }

    // Implements Attributes.
    public CharArray getPrefix(int index) {
        return (index >= 0 && index < _length) ? _prefixes[index] : null;
    }

    // Implements Attributes.
    public CharArray getQName(int index) {
        return (index >= 0 && index < _length) ? _qNames[index] : null;
    }

    // Implements Attributes.
    public CharArray getType(int index) {
        return (index >= 0 && index < _length) ? CDATA : null;
    }

    private static final CharArray CDATA = new CharArray("CDATA");

    // Implements Attributes.
    public CharArray getValue(int index) {
        return (index >= 0 && index < _length) ? _values[index] : null;
    }

    // Implements Attributes.
    public int getIndex(CharSequence uri, CharSequence localName) {
        if (uri == null) 
            throw new IllegalArgumentException(
                    "null namespace URI is not allowed");
        for (int i = 0; i < _length; i++) {
            if (_localNames[i].equals(localName)) {
                CharArray ns = _namespaces.getNamespaceURINullAllowed(_prefixes[i]);
                if ((ns != null) && ns.equals(uri))
                    return i;
            }
        }
        return -1;
    }

    // Implements Attributes.
    public int getIndex(CharSequence qName) {
        for (int i = 0; i < _length; i++) {
            if (_qNames[i].equals(qName))
                return i;
        }
        return -1;
    }

    // Implements Attributes.
    public CharArray getType(CharSequence uri, CharSequence localName) {
        final int index = getIndex(uri, localName);
        return (index >= 0) ? CDATA : null;
    }

    // Implements Attributes.
    public CharArray getType(CharSequence qName) {
        final int index = getIndex(qName);
        return (index >= 0) ? CDATA : null;
    }

    // Implements Attributes.
    public CharArray getValue(CharSequence uri, CharSequence localName) {
        final int index = getIndex(uri, localName);
        return (index >= 0) ? _values[index] : null;
    }

    // Implements Attributes.
    public CharArray getValue(CharSequence qName) {
        final int index = getIndex(qName);
        return (index >= 0) ? _values[index] : null;
    }

    /**
     * Clear the attribute list for reuse.
     */
    public void reset() {
        _length = 0;
    }

    /**
     * Adds an attribute to the end of the attribute list.
     * 
     * @param localName the local name.
     * @param prefix the prefix or <code>null</code> if none.
     * @param qName the qualified (prefixed) name.
     * @param value the attribute value.
     */
    public void addAttribute(CharArray localName, CharArray prefix,
            CharArray qName, CharArray value) {
        if (_length >= _localNames.length) {
            increaseCapacity();
        }
        _localNames[_length] = localName;
        _prefixes[_length] = prefix;
        _qNames[_length] = qName;
        _values[_length++] = value;
    }

    /**
     * Returns the string representation of these attributes.
     * 
     * @return this attributes textual representation.
     */
    public String toString() {
        Text text = Text.valueOf('[');
        final Text equ = Text.valueOf('=');
        final Text sep = Text.valueOf(", ");
        for (int i = 0; i < _length;) {
            text = text.concat(Text.valueOf(_qNames[i]).concat(equ).concat(
                    Text.valueOf(_values[i])));
            if (++i != _length) {
                text = text.concat(sep);
            }
        }
        return text.concat(Text.valueOf(']')).toString();
    }

    private void increaseCapacity() {
        MemoryArea.getMemoryArea(this).executeInArea(new Runnable() {
            public void run() {
                final int newCapacity = _length * 2;

                CharArray[] tmp = new CharArray[newCapacity];
                System.arraycopy(_localNames, 0, tmp, 0, _length);
                _localNames = tmp;

                tmp = new CharArray[newCapacity];
                System.arraycopy(_prefixes, 0, tmp, 0, _length);
                _prefixes = tmp;

                tmp = new CharArray[newCapacity];
                System.arraycopy(_qNames, 0, tmp, 0, _length);
                _qNames = tmp;

                tmp = new CharArray[newCapacity];
                System.arraycopy(_values, 0, tmp, 0, _length);
                _values = tmp;
            }
        });
    }

}