001/*
002 *  Copyright 2020 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.runtime.model;
017
018import java.util.Collection;
019
020import org.apache.commons.lang3.StringUtils;
021
022import org.ametys.runtime.model.exception.UndefinedItemPathException;
023
024/**
025 * Interface for objects that can access to some model items
026 */
027public interface ModelItemAccessor
028{
029    /**
030     * Retrieves the child model item with the given name
031     * @param childName name of the model item child to retrieve
032     * @return the child with the given name, or <code>null</code> if no child is found
033     */
034    public default ModelItem getChild(String childName)
035    {
036        for (ModelItem child : getModelItems())
037        {
038            if (StringUtils.equals(child.getName(), childName))
039            {
040                return child;
041            }
042            
043            // If the accessor has no name, check its children
044            if (child.getName() == null && child instanceof ModelItemAccessor)
045            {
046                ModelItem item = ((ModelItemAccessor) child).getChild(childName);
047                if (item != null)
048                {
049                    return item;
050                }
051            }
052        }
053
054        return null;
055    }
056    
057    /**
058     * Retrieves the model item at given path
059     * @param itemPath the item path
060     * @return the model item.
061     * @throws UndefinedItemPathException if there is no item defined at the given path 
062     */
063    public default ModelItem getModelItem(String itemPath) throws UndefinedItemPathException
064    {
065        String[] pathSegments = StringUtils.split(itemPath, ModelItem.ITEM_PATH_SEPARATOR);
066        
067        if (pathSegments.length > 0)
068        {
069            ModelItemAccessor currentAccessor = this;
070            ModelItem child = null;
071            for (int i = 0; i < pathSegments.length; i++)
072            {
073                child = currentAccessor.getChild(pathSegments[i]);
074
075                if (child == null)
076                {
077                    throw new UndefinedItemPathException("The item at path '" + itemPath + "' is not defined in the model item accessor '" + toString() + "'.");
078                }
079                else if (i < pathSegments.length - 1 && !(child instanceof ModelItemAccessor))
080                {
081                    String currentAccessorPath = StringUtils.join(pathSegments, ModelItem.ITEM_PATH_SEPARATOR, 0, i + 1);
082                    String subItemPath = StringUtils.join(pathSegments, ModelItem.ITEM_PATH_SEPARATOR, i + 1, pathSegments.length);
083                    throw new UndefinedItemPathException("In the model item accessor '" + toString() + "', the item at path '" + currentAccessorPath + "' is not a model item accessor. So it can not access to a sub item with path '" + subItemPath + "'.");
084                }
085                else if (child instanceof ModelItemAccessor)
086                {
087                    currentAccessor = (ModelItemAccessor) child;
088                }
089            }
090            
091            return child;
092        }
093        else
094        {
095            throw new UndefinedItemPathException("The item at path '" + itemPath + "' is not defined in the model item accessor '" + toString() + "'.");
096        }
097    }
098    
099    /**
100     * Checks if there is an item defined with the given path
101     * @param itemPath path of the item
102     * @return <code>true</code> if there is an item, <code>false</code> otherwise
103     */
104    public default boolean hasModelItem(String itemPath)
105    {
106        try
107        {
108            getModelItem(itemPath);
109            return true;
110        }
111        catch (UndefinedItemPathException e)
112        {
113            // The getModelItem method throws an UndefinedItemPathException if there is no model item defined with the given path 
114            return false;
115        }
116    }
117    
118    /**
119     * Retrieves all the model items of this accessor
120     * @return the model items
121     */
122    public Collection<? extends ModelItem> getModelItems();
123}