/*
* Geotoolkit.org - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2009-2012, Open Source Geospatial Foundation (OSGeo)
* (C) 2009-2012, 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.geotoolkit.image.io.metadata;
import java.util.Arrays;
import java.util.AbstractList;
import java.util.RandomAccess;
import java.util.logging.Level;
import org.apache.sis.util.collection.CheckedContainer;
import static org.apache.sis.util.ArgumentChecks.ensureValidIndex;
import static org.geotoolkit.internal.image.io.GridDomainAccessor.ARRAY_ATTRIBUTE_NAME;
/**
* A list of {@link MetadataProxy} instances. This list is <cite>live</cite>: changes to the
* backing {@link javax.imageio.metadata.IIOMetadata} are immediately reflected in this list.
*
* {@note Current implementation has a limitation, in that changes in existing elements are
* reflected by this view as expected, but <em>addition</em> or <em>removal</em> of
* elements are not visible if they are not performed by the <code>MetadataNodeParser</code>
* instance wrapped by this class.}
*
* @param <T> The type of elements in this list.
*
* @author Martin Desruisseaux (Geomatys)
* @version 3.06
*
* @since 3.06
* @module
*/
final class MetadataProxyList<T> extends AbstractList<T> implements
org.apache.sis.util.collection.CheckedContainer<T>, RandomAccess
{
/**
* The proxy which is the parent of all elements in this list.
*/
private final MetadataProxy<T> parent;
/**
* The proxies which have been created up to date. Elements in this array
* may be null if the proxy at a given index has not yet been created.
*/
private T[] elements;
/**
* Creates a new list.
*/
static <T> MetadataProxyList<T> create(final Class<T> elementType, final MetadataNodeParser accessor) {
return new MetadataProxyList<>(elementType, accessor);
}
/**
* Creates a new list.
*/
private MetadataProxyList(final Class<T> elementType, final MetadataNodeParser accessor) {
parent = new MetadataProxy<>(elementType, accessor);
}
/**
* Sets the logging level of all proxies created up to date. This will be executed
* only if the level is different than the one used up to date, as a safety against
* infinite recursivity.
*/
final void setWarningLevel(final Level level) {
if (!level.equals(parent.accessor.setWarningLevel(level))) {
MetadataProxy.setWarningLevel(Arrays.asList(elements), level);
}
}
/**
* Returns the type of elements in this list.
*/
@Override
public Class<T> getElementType() {
return parent.interfaceType;
}
/**
* Returns the size of this list.
*/
@Override
public int size() {
return parent.accessor.childCount();
}
/**
* Returns the element at the given index. This method creates the proxies when first needed.
*/
@Override
public T get(final int index) {
ensureValidIndex(size(), index);
T[] elements = this.elements;
if (elements == null) {
@SuppressWarnings("unchecked")
final T[] unsafe = (T[]) new Object[Math.max(4, index+1)];
this.elements = elements = unsafe;
} else {
final int length = elements.length;
if (index >= length) {
this.elements = elements = Arrays.copyOf(elements, Math.max(index, length*2));
}
}
T element = elements[index];
if (element == null) {
/*
* If the object has not already been created, create it now. In usual situations,
* we just create a new proxy element. However in a few cases, the element type is
* not an other interface for which we can create a proxy. The main case we want to
* support is the OffsetVector node which are elements having a single attribute
* named "values":
*
* RectifiedGridDomain : RectifiedGrid
* └───OffsetVectors : List<double[]>
* └───OffsetVector : double[]
* └───values
*
* Note that this practice (element with a single attribute called "value") is used
* in the standard Image I/O metadata format. For now are interested only in arrays
* of primitive ints, doubles or Strings, which is why "values" is plural.
*/
final Class<T> type = parent.interfaceType;
final Class<?> componentType = type.getComponentType();
if (componentType == null) {
element = parent.newProxyInstance(index);
} else {
final MetadataNodeParser accessor = parent.accessor;
accessor.selectChild(index);
final Object array;
if (componentType.equals(Double.TYPE)) {
array = accessor.getAttributeAsDoubles(ARRAY_ATTRIBUTE_NAME, false);
} else if (componentType.equals(Integer.TYPE)) {
array = accessor.getAttributeAsIntegers(ARRAY_ATTRIBUTE_NAME, false);
} else {
array = accessor.getAttributeAsStrings(ARRAY_ATTRIBUTE_NAME, false);
}
element = type.cast(array);
}
elements[index] = element;
}
return element;
}
}