/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// Package
///////////////
package org.apache.jena.rdf.model;


// Imports
///////////////
import java.util.*;
import java.util.function.Function;

import org.apache.jena.util.iterator.* ;


/**
 * <p>
 * Provides a convenience encapsulation for lists formed from chains of RDF
 * statements arranged to form a head/tail cons-cell structure.  The properties
 * that form the links between cells, and from cells to values, are specified by
 * a vocabulary interface, so this abstraction is designed to cope equally well
 * with DAML lists, RDF lists, and user-defined lists.
 * </p>
 * <p>
 * A well-formed list has cells that are made up of three statements: one
 * denoting the <code>rdf:type</code> of the list cell, one denoting the link
 * to the value of the list at that point, and one pointing to the list tail. If
 * a list cell is not well-formed, list operations may fail in unpredictable
 * ways. However, to explicitly check that the list is well-formed at all times
 * is expensive.  Therefore the list operates in two modes: in <i>strict</i>
 * mode, the well-formedness of the list is checked at the start of each list
 * operation, and an {@link InvalidListException} is thrown if the list is not
 * well- formed. This ensures that list operations are safe, but will slow down
 * processing. In <i>non-strict</i> mode, this checking is switched off, but can
 * be invoked explicitly by clients by calling {@link #isValid}. By default, RDF
 * lists are processed in non-strict mode.
 * </p>
 */
public interface RDFList
    extends Resource
{
    // Constants
    //////////////////////////////////


    // External signature methods
    //////////////////////////////////

    /**
     * <p>
     * Answer the number of elements in the list.
     * </p>
     *
     * @return The size of the list as an integer
     */
    public int size();


    /**
     * <p>
     * Answer the value that is at the head of the list.
     * </p>
     *
     * @return The value that is associated with the head of the list.
     * @exception EmptyListException if this list is the empty list
     */
    public RDFNode getHead();


    /**
     * <p>
     * Update the head of the list to have the given value, and return the
     * previous value.
     * </p>
     *
     * @param value The value that will become the value of the list head
     * @exception EmptyListException if this list is the empty list
     */
    public RDFNode setHead( RDFNode value );


    /**
     * <p>
     * Answer the list that is the tail of this list.
     * </p>
     *
     * @return The tail of the list, as a list
     * @exception EmptyListException if this list is the empty list
     */
    public RDFList getTail();


    /**
     * <p>
     * Update the list cell at the front of the list to have the given list as
     * tail. The old tail is returned, and remains in the model.
     * </p>
     *
     * @param tail The new tail for this list.
     * @return The old tail.
     */
    public RDFList setTail( RDFList tail );


    /**
     * Answer true if this list is the empty list.
     *
     * @return True if this is the empty (nil) list, otherwise false.
     */
    public boolean isEmpty();


    /**
     * <p>
     * Return a reference to a new list cell whose head is <code>value</code>
     * and whose tail is this list.
     * </p>
     *
     * @param value A new value to add to the head of the list
     * @return The new list, whose head is <code>value</code>
     */
    public RDFList cons( RDFNode value );


    /**
     * <p>
     * Add the given value to the end of the list. This is a side-effecting
     * operation on the underlying model that is only defined if this is not the
     * empty list.  If this list is the empty (nil) list, we cannot perform a
     * side-effecting update without changing the URI of this node (from <code>rdf:nil</code>)
     * to a blank-node for the new list cell) without violating a Jena invariant.
     * Therefore, this update operation will throw an exception if an attempt is
     * made to add to the nil list.  Safe ways to add to an empty list include
     * {@link #with} and {@link #cons}.
     * </p>
     *
     * @param value A value to add to the end of the list
     * @exception EmptyListUpdateException if an attempt is made to
     * <code>add</code> to the empty list.
     */
    public void add( RDFNode value );


    /**
     * <p>
     * Answer the list that is this list with the given value added to the end
     * of the list.  This operation differs from {@link #add} in that it will
     * always work, even on an empty list, but the return value is the updated
     * list.  Specifically, in the case of adding a value to the empty list, the
     * returned list will not be the same as this list. <strong>Client code should
     * not assume that this is an in-place update, but should ensure that the resulting
     * list is asserted back into the graph into the appropriate relationships.</strong>
     * </p>
     *
     * @param value A value to add to the end of the list
     * @return The list that results from adding a value to the end of this list
     */
    public RDFList with( RDFNode value );


    /**
     * <p>
     * Answer the node that is the i'th element of the list, assuming that the
     * head is item zero.  If the list is too short to have an i'th element,
     * throws a {@link ListIndexException}.
     * </p>
     *
     * @param i The index into the list, from 0
     * @return The list value at index i, or null
     * @exception ListIndexException if the list has fewer than (i + 1)
     * elements.
     */
    public RDFNode get( int i );


    /**
     * <p>
     * Replace the value at the i'th position in the list with the given value.
     * If the list is too short to have an i'th element, throws
     * a {@link ListIndexException}.
     * </p>
     *
     * @param i The index into the list, from 0
     * @param value The new value to associate with the i'th list element
     * @return The value that was previously at position i in the list
     * @exception ListIndexException if the list has fewer than (i + 1)
     * elements.
     */
    public RDFNode replace( int i, RDFNode value );


    /**
     * <p>
     * Answer true if the given node appears as the value of a value of any
     * of the cells of this list.
     * </p>
     *
     * @param value A value to test for
     * @return True if the list contains value.
     */
    public boolean contains( RDFNode value );


    /**
     * <p>
     * Answer the index of the first occurrence of the given value in the list,
     * or -1 if the value is not in the list.
     * </p>
     *
     * @param value The value to search for
     * @return The index of the first occurrence of value in the list, or
     * <code>-1</code> if not found.
     */
    public int indexOf( RDFNode value );


    /**
     * <p>
     * Answer the index of the first occurrence of the given value in the list
     * after index <code>start</code>, or -1 if the value is not in the list
     * after the given start point.
     * </p>
     *
     * @param value The value to search for
     * @param start The index into the list to start searching from
     * @return The index of the first occurrence of value in the list not less
     * than <code>start</code>, or <code>-1</code> if not found.
     * @exception ListIndexException if <code>start</code> is greater than the
     * length of the list.
     */
    public int indexOf( RDFNode value, int start );


    /**
     * <p>
     * Answer a new list that is formed by adding each element of this list to
     * the head of the given <code>list</code>. This is a non side-effecting
     * operation on either this list or the given list, but generates a copy
     * of this list.  For a more storage efficient alternative, see
     * {@link #concatenate concatenate}.
     * </p>
     *
     * @param list The argument list
     * @return A new RDFList that contains all of this elements of this list,
     * followed by all of the elements of the given list.
     */
    public RDFList append( RDFList list );


    /**
     * <p>
     * Answer a new list that is formed by adding each element of this list to
     * the head of the the list formed from the
     * given <code>nodes</code>. This is a non side-effecting
     * operation on either this list or the given list, but generates a copy
     * of this list.  For a more storage efficient alternative,
     * see {@link #concatenate concatenate}.
     * </p>
     *
     * @param nodes An iterator whose range is RDFNode
     * @return A new RDFList that contains all of this elements of this list,
     * followed by all of the elements of the given iterator.
     */
    public RDFList append( Iterator<? extends RDFNode> nodes );


    /**
     * <p>
     * Change the tail of this list to point to the given list, so that this
     * list becomes the list of the concatenation of the elements of both lists.
     * This is a side-effecting operation on this list; for a non side-effecting
     * alternative, see {@link #append}.  Due to the problem of maintaining
     * the URI invariant on a node, this operation will throw an exception if an
     * attempt is made to concatenate onto an empty list.  To avoid this, test for
     * an empty list: if true replace the empty list with the argument list, otherwise
     * proceed with the concatenate as usual.  An alternative solution is to use
     * {@link #append} and replace the original list with the return value.
     * </p>
     *
     * @param list The argument list to concatenate to this list
     * @exception EmptyListUpdateException if this list is the nil list
     */
    public void concatenate( RDFList list );


    /**
     * <p>
     * Add the nodes returned by the given iterator to the end of this list.
     * </p>
     *
     * @param nodes An iterator whose range is RDFNode
     * @exception EmptyListUpdateException if this list is the nil list
     * @see #concatenate(RDFList) for details on avoiding the empty list update exception.
     */
    public void concatenate( Iterator<? extends RDFNode> nodes );


    /**
     * <p>
     * Answer a list that contains all of the elements of this list in the same
     * order, but is a duplicate copy in the underlying model.
     * </p>
     *
     * @return A copy of the current list
     */
    public RDFList copy();


    /**
     * <p>
     * Apply a function to each value in the list in turn.
     * </p>
     *
     * @param fn The function to apply to each list node.
     */
    public void apply( ApplyFn fn );


    /**
     * <p>
     * Apply a function to each value in the list in turn, accumulating the
     * results in an accumulator. The final value of the accumulator is returned
     * as the value of <code>reduce()</code>.
     * </p>
     *
     * @param fn The reduction function to apply
     * @param initial The initial value for the accumulator
     * @return The final value of the accumulator.
     */
    public Object reduce( ReduceFn fn, Object initial );


    /**
     * <p>Answer an iterator of the elements of this list, to each of which
     * the given map function has been applied.</p>
     * @param fn A mapping function
     * @return The iterator of the elements of this list mapped with the given map function.
     */
    public <T> ExtendedIterator<T> mapWith( Function<RDFNode, T> fn );


    /**
     * <p>
     * Remove the value from the head of the list.  The tail of the list remains
     * in the model.  Note that no changes are made to list cells that point to
     * this list cell as their tail.  Immediately following a
     * <code>removeHead</code> operation, such lists will be in a non-valid
     * state.
     * </p>
     *
     * @return The remainder of the list after the head is removed (i&#046;e&#046; the
     * pre-removal list tail)
     */
    public RDFList removeHead();

    /**
     * <p>Remove all of the components of this list from the model. Once this operation
     * has completed, the {@link RDFList} resource on which it was called will no
     * longer be a resource in the model, so further methods calls on the list object
     * (for example, {@link #size} will fail.  Due to restrictions on the encoding
     * of lists in RDF, it is not possible to perform an operation which empties a list
     * and then adds further values to that list. Client code wishing to perform
     * such an operation should do so in two steps: first remove the old list, then
     * create a new list with the new contents. It is important that RDF statements
     * that reference the old list (in the object position) be updated to point
     * to the newly created list.
     * Note that this
     * is operation is only removing the list cells themselves, not the resources
     * referenced by the list - unless being the object of an <code>rdf:first</code>
     * statement is the only mention of that resource in the model.</p>
     */
    public void removeList();


    /**
     * <p>Remove the given value from this list. If <code>val</code> does not occur in
     * the list, no action is taken.  Since removing the head of the list will invalidate
     * the list head cell, in general the list must return the list that results from this
     * operation. However, in many cases the return value will be the same as the object
     * that this method is invoked on</p>
     *
     * @param val The value to be removed from the list
     * @return The resulting list, which will be the same as the current list in most
     * cases, except when <code>val</code> occurs at the head of the list.
     */
    public RDFList remove( RDFNode val );


    /**
     * <p>
     * Answer an iterator over the elements of the list. Note that this iterator
     * does not take a snapshot of the list, so changes to the list statements
     * in the model while iterating will affect the behaviour of the iterator.
     * To get an iterator that is not affected by model changes,
     * use {@link #asJavaList}.
     * </p>
     *
     * @return A closable iterator over the elements of the list.
     */
    public ExtendedIterator<RDFNode> iterator();


    /**
     * <p>
     * Answer the contents of this RDF list as a Java list of RDFNode values.
     * </p>
     *
     * @return The contents of this list as a Java List.
     */
    public List<RDFNode> asJavaList();


    /**
     * <p>
     * Answer true if this list has the same elements in the same order as the
     * given list.  Note that the standard <code>equals</code> test just tests
     * for equality of two given list cells.  While such a test is sufficient
     * for many purposes, this test provides a broader equality definition, but
     * is correspondingly more expensive to test.
     * </p>
     *
     * @param list The list to test against
     * @return True if the given list and this list are the same length, and
     * contain equal elements in the same order.
     */
    public boolean sameListAs( RDFList list );


    /**
     * <p>
     * Answer true lists are operating in strict mode, in which the
     * well- formedness of the list is checked at every operation.
     * </p>
     *
     * @return True lists are being strictly checked.
     */
    public boolean getStrict();


    /**
     * <p>
     * Set a flag to indicate whether to strictly check the well-formedness of
     * lists at each operation. Default false.  <strong>Note</strong> that the flag that is
     * manipulated is actually a static: it applies to all lists. However, RDFList
     * is a Java interface, and Java does not permit static methods in interfaces.
     * </p>
     *
     * @param strict The <b>static</b> flag for whether lists will be checked strictly.
     */
    public void setStrict( boolean strict );


    /**
     * <p>
     * Answer true if the list is well-formed, by checking that each node is
     * correctly typed, and has a head and tail pointer from the correct
     * vocabulary. If the list is invalid, the reason is available
     * via {@link #getValidityErrorMessage}.
     * </p>
     *
     * @return True if the list is well-formed.
     * @see #getValidityErrorMessage
     */
    public boolean isValid();


    /**
     * <p>
     * Answer the error message returned by the last failed validity check,
     * if any.
     * </p>
     *
     * @return The most recent error message, or null.
     * @see #isValid
     */
    public String getValidityErrorMessage();


    //==============================================================================
    // Inner class definitions
    //==============================================================================

    /**
     * Interface that encapsulates a function to apply to every element in a
     * list.
     */
    public static interface ApplyFn {
        /**
         * <p>
         * Apply a function to the given RDF node.
         * </p>
         *
         * @param node A node from the list.
         */
        public void apply( RDFNode node );
    }


    /**
     * Interface that encapsulates a function to apply to each element of a list
     * in turn, and passing the result to an accumulator.
     */
    public static interface ReduceFn {
        /**
         * <p>
         * Apply a function to the given RDF node.
         * </p>
         *
         * @param node A node from the list.
         * @param accumulator The accumulator for the reduction, which will
         * either be an initial value passed to {@link RDFList#reduce}, or the
         * output from <code>reduce</code> applied to the previous node in the
         * list.
         * @return The result of applying the reduction function to the current
         * node and the accumulator.
         */
        public Object reduce( RDFNode node, Object accumulator );
    }
}
