001/*
002 *  Copyright 2018 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.ArrayList;
019import java.util.Collection;
020import java.util.Collections;
021import java.util.HashMap;
022import java.util.LinkedHashMap;
023import java.util.List;
024import java.util.Map;
025
026import org.apache.cocoon.ProcessingException;
027
028import org.ametys.core.model.type.AbstractModelItemType;
029import org.ametys.runtime.model.type.ElementType;
030import org.ametys.runtime.model.type.ModelItemType;
031import org.ametys.runtime.model.type.ModelItemTypeConstants;
032
033/**
034 * Class for group of model items
035 */
036public class ModelItemGroup extends AbstractModelItem implements ModelItemContainer
037{
038    /** Id for model item group types */
039    private static final String __DEFAULT_TYPE_ID = "composite";
040    
041    private List<ModelItem> _children = new ArrayList<>();
042    private ElementDefinition<Boolean> _switcher;
043    private AbstractModelItemType _type;
044    
045    public Collection<ModelItem> getModelItems()
046    {
047        return getChildren();
048    }
049    
050    /**
051     * Retrieves the list of children model items
052     * @return the children
053     */
054    public List<ModelItem> getChildren()
055    {
056        return getChildren(true);
057    }
058    
059    /**
060     * Retrieves the list of children model items, with or without the switcher
061     * @param withSwitch true to retrieve the switcher with the other children
062     * @return the children with or without the switcher
063     */
064    public List<ModelItem> getChildren(boolean withSwitch)
065    {
066        if (withSwitch || _switcher == null)
067        {
068            return Collections.unmodifiableList(_children);
069        }
070        
071        List<ModelItem> childrenWithoutSwitcher = new ArrayList<>(_children);
072        childrenWithoutSwitcher.remove(_switcher);
073        return childrenWithoutSwitcher;
074    }
075    
076    /**
077     * Add a child in the group
078     * @param child the item to add
079     */
080    public void addChild(ModelItem child)
081    {
082        addChild(child, false);
083    }
084    
085    /**
086     * Add a child in the group
087     * @param child the child to add
088     * @param isGroupSwitch true if the child to add is the group switch, false otherwise
089     */
090    @SuppressWarnings("unchecked")
091    public void addChild(ModelItem child, boolean isGroupSwitch)
092    {
093        _children.add(child);
094        child.setParent(this);
095        
096        if (isGroupSwitch)
097        {
098            if (_switcher == null)
099            {
100                if (child instanceof ElementDefinition)
101                {
102                    ElementType type = ((ElementDefinition) child).getType();
103                    if (ModelItemTypeConstants.BOOLEAN_TYPE_ID.equals(type.getId()))
104                    {
105                        _switcher = (ElementDefinition<Boolean>) child;
106                    }
107                }
108                
109                if (_switcher == null)
110                {
111                    // If switcher is still null, the type of child is not compatible
112                    throw new RuntimeException("The group '" + getLabel() + "' has a switch '" + child + "' that is not valid because it is not a boolean.");
113                }
114            }
115            else
116            {
117                throw new RuntimeException("At least two group-switches have been defined for the configuration group '" + getLabel() + "'. These parameters are '" + _switcher + "' and '" + child + "'.");
118            }
119        }
120    }
121    
122    /**
123     * Retrieves the switcher element definition
124     * @return the switcher
125     */
126    public ElementDefinition<Boolean> getSwitcher()
127    {
128        return _switcher;
129    }
130    
131    @Override
132    public AbstractModelItemType getType()
133    {
134        return _type;
135    }
136
137    public void setType(ModelItemType type)
138    {
139        if (type instanceof AbstractModelItemType)
140        {
141            _type = (AbstractModelItemType) type;
142        }
143        else
144        {
145            throw new IllegalArgumentException("Unable to set the type '" + type.getClass() + "' on the group '" + getName() + "'");
146        }
147    }
148    
149    @Override
150    public Map<String, Object> toJSON() throws ProcessingException
151    {
152        return toJSON(true);
153    }
154    
155    /**
156     * Converts the model item group in a JSON map
157     * @param includeChildren true to iterate and add children as elements in the returned JSON map, false otherwise
158     * @return The model item as a JSON map
159     * @throws ProcessingException If an error occurs when converting the model item group
160     */
161    public Map<String, Object> toJSON(boolean includeChildren) throws ProcessingException
162    {
163        Map<String, Object> result = super.toJSON();
164        
165        ModelItemType type = getType();
166        result.put("type", type != null ? type.getId() : __DEFAULT_TYPE_ID);
167        
168        ElementDefinition<Boolean> switcher = getSwitcher();
169        if (switcher != null)
170        {
171            Map<String, Object> switcherToJSON = new HashMap<>();
172            
173            switcherToJSON.put("id", switcher.getName());
174            switcherToJSON.put("label", switcher.getLabel());
175            
176            if (switcher.getType() != null)
177            {
178                switcherToJSON.put("default-value", switcher.getType().valueToJSONForClient(switcher.getDefaultValue()));
179            }
180            
181            result.put("switcher", switcherToJSON);
182        }
183        
184        // Do not include switcher in children because it is already included above
185        List<ModelItem> children = getChildren(false);
186        if (includeChildren && !children.isEmpty())
187        {
188            Map<String, Object> elements = new LinkedHashMap<>();
189            for (ModelItem child : children)
190            {
191                String name = child.getName();
192                if (name != null)
193                {
194                    elements.put(name, child.toJSON());
195                }
196            }
197            
198            result.put("elements", elements);
199        }
200        
201        return result;
202    }
203}