001/*
002 *  Copyright 2010 Anyware Services
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.ametys.plugins.repository;
017
018import java.util.NoSuchElementException;
019
020/**
021 * {@link AmetysObject} which is traversable and allow to create children.
022 */
023public interface TraversableAmetysObject extends AmetysObject
024{
025    /**
026     * Retrieves children of the current object.
027     * @param <A> the actual type of {@link AmetysObject}s.
028     * @return the children or an empty {@link AmetysObjectIterable}.
029     * @throws AmetysRepositoryException if an error occurs.
030     */
031    <A extends AmetysObject> AmetysObjectIterable<A> getChildren() throws AmetysRepositoryException;
032
033    /**
034     * Tests if this Object has at least one child with the given name.<br>
035     * @param name the name to test. Cannot be <code>null</code> nor empty nor
036     * begin with a <code>'/'</code> and it cannot contain <code>'/'</code>.
037     * @return <code>true</code> if the named child exists,
038     * <code>false</code> otherwise.
039     * @throws AmetysRepositoryException if an error occurs.
040     */
041    boolean hasChild(String name) throws AmetysRepositoryException;
042
043    /**
044     * Retrieves a given child from its relative path.
045     * The path cannot be <code>null</code>, empty nor begin with a <code>'/'</code>.
046     * @param <A> the actual type of {@link AmetysObject}.
047     * @param path the path of the child which can contains a position with.
048     * <code>[n]</code> if same name sibling is allowed.
049     * @return the child found.
050     * @throws AmetysRepositoryException if an error occurs.
051     * @throws UnknownAmetysObjectException if the object does not exist.
052     */
053    <A extends AmetysObject> A getChild(String path) throws AmetysRepositoryException, UnknownAmetysObjectException;
054    
055    /**
056     * Gets the child {@link AmetysObject} at the given position
057     * @param <A> The type of children objects
058     * @param index the position of the {@link AmetysObject} within the ordered set of its sibling objects.
059     * @return the {@link AmetysObject} if found
060     * @throws AmetysRepositoryException if an error occurs
061     * @throws UnknownAmetysObjectException if no object was found at this position
062     */
063    @SuppressWarnings("unchecked")
064    default <A extends AmetysObject> A getChildAt(long index) throws AmetysRepositoryException, UnknownAmetysObjectException
065    {
066        if (index < 0)
067        {
068            throw new AmetysRepositoryException("Child index cannot be negative");
069        }
070        
071        AmetysObjectIterable<AmetysObject> children = getChildren();
072        AmetysObjectIterator<AmetysObject> it = children.iterator();
073        
074        try
075        {
076            it.skip(index);
077            return (A) it.next();
078        }
079        catch (NoSuchElementException e)
080        {
081            throw new UnknownAmetysObjectException("There's no child at index " + index + " for object " + this.getId());
082        }
083    }
084    
085    /**
086     * Returns the position of this {@link AmetysObject} within the ordered set of its sibling objects.
087     * @param ao The Ametys object
088     * @return the position of this  {@link AmetysObject} within the ordered set of its sibling objects.
089     * @throws AmetysRepositoryException if an error occurs.
090     */
091    default long getChildPosition(AmetysObject ao) throws AmetysRepositoryException
092    {
093        AmetysObjectIterable<AmetysObject> children = getChildren();
094        
095        long count = 0;
096        for (AmetysObject child : children)
097        {
098            if (child.getId().equals(ao.getId()))
099            {
100                return count;
101            }
102            count++;
103        }
104        
105        // Not found
106        return -1;
107    }
108}