/*
 *    GeoTools - OpenSource mapping toolkit
 *    http://geotools.org
 *    (C) 2007, GeoTools Project Managment Committee (PMC)
 *    (C) 2007, Geomatys
 *
 *    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;
 *    version 2.1 of the License.
 *
 *    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.
 */
package org.geotools.image.io.metadata;

// J2SE dependencies
import java.util.ArrayList;
import java.util.List;
import org.w3c.dom.Node;

// Geotools dependencies
import org.geotools.resources.i18n.Errors;
import org.geotools.resources.i18n.ErrorKeys;


/**
 * A list of child elements, for example {@code <SampleDimensions>} or {@code <Axis>}.
 *
 * @since 2.4
 * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/unsupported/coverageio/src/main/java/org/geotools/image/io/metadata/ChildList.java $
 * @version $Id: ChildList.java 26151 2007-07-04 18:54:48Z desruisseaux $
 * @author Martin Desruisseaux
 */
abstract class ChildList/*<T extends MetadataAccessor>*/ extends MetadataAccessor {
    /**
     * The list of childs.
     */
    private final List/*<T>*/ childs;

    /**
     * Creates a parser for childs. The arguments are given unchanged to the
     * {@linkplain MetadataAccessor#MetadataAccessor super-class constructor}.
     *
     * @param  metadata   The metadata node.
     * @param  parentPath The path to the {@linkplain Node node} of interest, or {@code null}
     *                    if {@code metadata} is directly the node of interest.
     * @param  childPath  The path (relative to {@code parentPath}) to the child
     *                    {@linkplain Element elements}, or {@code null} if none.
     */
    protected ChildList(final GeographicMetadata metadata, final String parentPath, final String childPath) {
        super(metadata, parentPath, childPath);
        final int count = childCount();
        childs = new ArrayList(count != 0 ? count : 4);
    }

    /**
     * Returns the child at the specified index.
     *
     * @param  index the child index.
     * @throws IndexOutOfBoundsException if the index is out of bounds.
     */
    public /*T*/MetadataAccessor getChild(final int index) throws IndexOutOfBoundsException {
        if (index < 0 || index >= childCount()) {
            throw new IndexOutOfBoundsException(Errors.format(outOfBounds(), new Integer(index)));
        }
        while (childs.size() <= index) {
            childs.add(null);
        }
        MetadataAccessor candidate = (MetadataAccessor) childs.get(index);
        if (candidate == null) {
            candidate = newChild(index);
            childs.set(index, candidate);
        }
        return candidate;
    }

    /**
     * Creates a new child, append to the list and returns it.
     */
    public /*T*/MetadataAccessor addChild() {
        final int index = appendChild();
        final MetadataAccessor candidate = newChild(index);
        assert index == childs.size();
        childs.add(candidate);
        return candidate;
    }

    /**
     * Creates a new child at the specified index.
     */
    protected abstract /*T*/MetadataAccessor newChild(int index);

    /**
     * Returns the key for "out of range" error localization.
     */
    int outOfBounds() {
        return ErrorKeys.INDEX_OUT_OF_BOUNDS_$1;
    }

    /**
     * A list of {@linkplain Band bands}.
     */
    static final class Bands extends ChildList/*<Band>*/ {
        /** Creates a parser for bands. */
        public Bands(final GeographicMetadata metadata) {
            super(metadata, "SampleDimensions", "SampleDimension");
        }

        /** Create a new band. */
        protected /*<Band>*/ MetadataAccessor newChild(final int index) {
            return new Band(this, index);
        }

        /** Returns the key for "out of range" error localization. */
        int outOfBounds() {
            return ErrorKeys.BAD_BAND_NUMBER_$1;
        }
    }

    /**
     * A list of {@linkplain Axis axis}.
     */
    static final class Axes extends ChildList/*<Axis>*/ {
        /** Creates a parser for axis. */
        public Axes(final GeographicMetadata metadata) {
            super(metadata, "CoordinateReferenceSystem/CoordinateSystem", "Axis");
        }

        /** Create a new band. */
        protected /*<Axis>*/ MetadataAccessor newChild(final int index) {
            return new Axis(this, index);
        }
    }
}
