package org.apache.fop.datatypes;

import java.lang.reflect.InvocationTargetException;

import java.util.LinkedList;
import java.util.Collection;
import java.util.Iterator;

import org.apache.fop.fo.properties.*;
import org.apache.fop.fo.PropertyConsts;
import org.apache.fop.fo.PropNames;
import org.apache.fop.fo.FONode;
import org.apache.fop.datatypes.PropertyValue;
import org.apache.fop.fo.expr.PropertyException;

/*
 * PropertyValueList.java
 * $Id$
 *
 * Created: Tue Dec 11 22:37:16 2001
 * Copyright (C) 2001 The Apache Software Foundation. All rights reserved.
 * For details on use and redistribution please refer to the
 * LICENSE file included with these sources.
 * @author <a href="mailto:pbwest@powerup.com.au">Peter B. West</a>
 * @version $Revision$ $Name$
 */
/**
 * A list of <tt>PropertyValue</tt> elements.
 */

public class PropertyValueList extends LinkedList implements PropertyValue {

    /**
     * An integer index to the type of property of which this is a value.
     */
    protected int property;

    /**
     * An integer property type.
     */
    public final int type;

    /**
     * The PropertyConsts singleton.
     */
    public final PropertyConsts propertyConsts;

    /**
     * @param property <tt>int</tt> index of the property.
     */
    public PropertyValueList(int property) throws PropertyException {
        super();
        if (property < 1 || property > PropNames.LAST_PROPERTY_INDEX)
            throw new PropertyException("Invalid property index: " + property);
        this.property = property;
        type = PropertyValue.LIST;
        propertyConsts = PropertyConsts.getPropertyConsts();
    }

    /**
     * @param propertyName a <tt>String</tt> containing the property name.
     */
    public PropertyValueList(String propertyName)
        throws PropertyException
    {
        super();
        property = PropNames.getPropertyIndex(propertyName);
        if (property < 1 || property > PropNames.LAST_PROPERTY_INDEX)
            throw new PropertyException("Invalid property index: " + property);
        type = PropertyValue.LIST;
        propertyConsts = PropertyConsts.getPropertyConsts();
    }

    /**
     * Constructor with a <tt>Collection</tt>.  Pass through to superclass
     * only if the collection is another instance of a PropertyValueList.
     * @param property <tt>int</tt> index of the property.
     * @param c a <tt>Collection</tt>, which must be another
     * <i>PropertyValueList</i>.
     * @exception IllegalArgumentException if the <tt>Collection</tt> is
     * not a <i>PropertyValueList</i>.
     */
    public PropertyValueList(int property, Collection c)
        throws PropertyException
    {
        super(c);
        // This test only follows the super() call because that call must
        // be the first in a constructor.
        if (! (c instanceof PropertyValueList))
            throw new IllegalArgumentException
                    ("Collection is not a PropertyValueList.");
        if (property < 1 || property > PropNames.LAST_PROPERTY_INDEX)
            throw new PropertyException("Invalid property index: " + property);
        this.property = property;
        type = PropertyValue.LIST;
        propertyConsts = PropertyConsts.getPropertyConsts();
    }

    /**
     * Constructor with a <tt>Collection</tt>.  Pass through to superclass
     * only if the collection is another instance of a PropertyValueList.
     * @param propertyName a <tt>String</tt> containing the property name.
     * @param c a <tt>Collection</tt>, which must be another
     * <i>PropertyValueList</i>.
     * @exception IllegalArgumentException if the <tt>Collection</tt> is
     * not a <i>PropertyValueList</i>.
     */
    public PropertyValueList(String propertyName, Collection c)
        throws PropertyException
    {
        super(c);
        // This test only follows the super() call because that call must
        // be the first in a constructor.
        if (! (c instanceof PropertyValueList))
            throw new IllegalArgumentException
                    ("Collection is not a PropertyValueList.");
        property = PropNames.getPropertyIndex(propertyName);
        if (property < 1 || property > PropNames.LAST_PROPERTY_INDEX)
            throw new PropertyException("Invalid property index: " + property);
        type = PropertyValue.LIST;
        propertyConsts = PropertyConsts.getPropertyConsts();
    }

    /**
     * Append a PropertyValue or PropertyValueList to the end of the list.
     * @param o a <tt>PropertyValue</tt> or a <PropertyValueList</tt>;
     * the element to add.  Defined as an
     * <tt>Object</tt> to override the definition in <tt>LinkedList</tt>.
     * @return a <tt>boolean</tt> success or failure(?).
     * @exception IllegalArgumentException if the object is not a
     * <tt>PropertyValue</tt> or <tt>PropertyValueList</tt>.
     */
    public boolean add(Object o) {
        if (! (o instanceof PropertyValue || o instanceof PropertyValueList))
            throw new IllegalArgumentException
                    ("Object is not a PropertyValue or a PropertyValueList.");
        return super.add(o);
    }

    /**
     * Insert a <tt>PropertyValue</tt> or <tt>PropertyValueList</tt>
     * at the beginning of the list.
     * @param o a <tt>PropertyValue</tt> or a <PropertyValueList</tt>;
     * the element to add.  Defined as an
     * <tt>Object</tt> to override the definition in <tt>LinkedList</tt>.
     * @exception IllegalArgumentException if the object is not a
     * <tt>PropertyValue</tt> or <tt>PropertyValueList</tt>.
     */
    public void addFirst(Object o) {
        if (! (o instanceof PropertyValue || o instanceof PropertyValueList))
            throw new IllegalArgumentException
                    ("Object is not a PropertyValue or a PropertyValueList.");
        super.addFirst(o);
    }

    /**
     * Append a PropertyValue to the end of the list.
     * @param o a <tt>PropertyValue</tt>; the element to add.  Defined as an
     * <tt>Object</tt> to override the definition in <tt>LinkedList</tt>.
     * @return a <tt>boolean</tt> success or failure(?).
     * @exception IllegalArgumentException if the object is not a
     * <tt>PropertyValue</tt>.
     */
    public void addLast(Object o) {
        add(o);
    }

    /*
     * Following fields and methods implement the PropertyValue interface
     */

    /**
     * @return <tt>int</tt> property index.
     */
    public int getProperty() {
        return property;
    }

    public void setProperty(int index) throws PropertyException {
        if (index < 0 || index > PropNames.LAST_PROPERTY_INDEX)
            throw new PropertyException("Invalid property index: " + index);
        property = index;
    }

    /**
     * @return type field of the <tt>PropertyValue</tt>.
     */
    public int getType() {
        return type;
    }

    /**
     * In some circumstances, the property against which a type is to be
     * validated may not be the same as the property against which this
     * <i>AbstractPropertyValue</i> is defined.
     * A specific property argument is then required.
     * @param testProperty <tt>int</tt> property index of the property
     * for which the type is to be validated.
     * @param type <tt>int</tt> bitmap of data types to check for
     * validity against this property.
     */
    public void validate(int testProperty, int type)
        throws PropertyException
    {
        // N.B. PROPERTY_SPECIFIC inheritance may require more specialized
        // checks.  Only line-height comes into this category.

        // N.B. The first commented-out condition means that I cannot validate
        // unless the property is NOT inherited.
        // I can't remember why I put this
        // condition in here.  Removing it.  pbw 2002/02/18
        //if (propertyConsts.inherited.get(testProperty) == Property.NO
        //&& (propertyConsts.getDataTypes(testProperty) & type) == 0) {

            if ((propertyConsts.getDataTypes(testProperty) & type) == 0) {
            String pname = PropNames.getPropertyName(testProperty);
            throw new PropertyException
                    ("Datatype(s) " +
                     Property.listDataTypes(type) +
                     " not defined on " + pname);
        }
    }

    /**
     * @param type <tt>int</tt> bitmap of data types to check for
     * validity against this property.
     */
    public void validate(int type) throws PropertyException {
        // N.B. PROPERTY_SPECIFIC inheritance may require more specialized
        // checks.  Only line-height comes into this category.
        validate(property, type);
    }

    public String toString() {
        String str, cstr;
        try {
            str = "Property: " + PropNames.getPropertyName(property)
                    + " Index: " + property + " List contents:\n";
            Iterator contents = iterator();
            while (contents.hasNext()) {
                int i = 0, j = 0;
                Object obj = contents.next();
                try {
                    cstr = (String)(obj.getClass()
                                .getMethod("toString", null)
                                .invoke(obj, null));
                } catch (IllegalAccessException e) {
                    throw new PropertyException (e);
                } catch (NoSuchMethodException e) {
                    throw new PropertyException (e);
                } catch (InvocationTargetException e) {
                    throw new PropertyException (e);
                }
                while (i < cstr.length() && j >= 0) {
                    j = cstr.indexOf('\n', j);
                    if (j >= 0) {
                        str = str + ">" + cstr.substring(i, ++j);
                        i = j;
                    } else {
                        str = str + ">" + cstr.substring(i);
                        i = cstr.length();
                    }
                }
            }
            return str;

        } catch (PropertyException e) {
            throw new RuntimeException(e.getMessage());
        }
    }

    public Object clone() {
        return super.clone();
    }

}// PropertyValueList