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