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.Arrays;
020import java.util.Collection;
021import java.util.Collections;
022import java.util.HashMap;
023import java.util.LinkedHashMap;
024import java.util.List;
025import java.util.Map;
026
027import org.apache.cocoon.ProcessingException;
028
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 ModelItemType _type;
044
045    /**
046     * Default constructor.
047     */
048    public ModelItemGroup()
049    {
050        super();
051    }
052    
053    /**
054     * Constructor used to create simple models and items 
055     * @param name the name of the definition
056     * @param children the group's children
057     */
058    public ModelItemGroup(String name, ModelItem... children)
059    {
060        super(name);
061        Arrays.stream(children)
062            .forEach(item -> addChild(item));
063    }
064    
065    public Collection<ModelItem> getModelItems()
066    {
067        return getChildren();
068    }
069    
070    /**
071     * Retrieves the list of children model items
072     * @return the children
073     */
074    public List<ModelItem> getChildren()
075    {
076        return getChildren(true);
077    }
078    
079    /**
080     * Retrieves the list of children model items, with or without the switcher
081     * @param withSwitch true to retrieve the switcher with the other children
082     * @return the children with or without the switcher
083     */
084    public List<ModelItem> getChildren(boolean withSwitch)
085    {
086        if (withSwitch || _switcher == null)
087        {
088            return Collections.unmodifiableList(_children);
089        }
090        
091        List<ModelItem> childrenWithoutSwitcher = new ArrayList<>(_children);
092        childrenWithoutSwitcher.remove(_switcher);
093        return childrenWithoutSwitcher;
094    }
095    
096    /**
097     * Add a child in the group
098     * @param child the item to add
099     */
100    public void addChild(ModelItem child)
101    {
102        addChild(child, false);
103    }
104    
105    /**
106     * Add a child in the group
107     * @param child the child to add
108     * @param isGroupSwitch true if the child to add is the group switch, false otherwise
109     */
110    @SuppressWarnings("unchecked")
111    public void addChild(ModelItem child, boolean isGroupSwitch)
112    {
113        _children.add(child);
114        child.setParent(this);
115        
116        if (isGroupSwitch)
117        {
118            if (_switcher == null)
119            {
120                if (child instanceof ElementDefinition)
121                {
122                    ElementType type = ((ElementDefinition) child).getType();
123                    if (ModelItemTypeConstants.BOOLEAN_TYPE_ID.equals(type.getId()))
124                    {
125                        _switcher = (ElementDefinition<Boolean>) child;
126                    }
127                }
128                
129                if (_switcher == null)
130                {
131                    // If switcher is still null, the type of child is not compatible
132                    throw new RuntimeException("The group '" + getLabel() + "' has a switch '" + child + "' that is not valid because it is not a boolean.");
133                }
134            }
135            else
136            {
137                throw new RuntimeException("At least two group-switches have been defined for the configuration group '" + getLabel() + "'. These parameters are '" + _switcher + "' and '" + child + "'.");
138            }
139        }
140    }
141    
142    /**
143     * Retrieves the switcher element definition
144     * @return the switcher
145     */
146    public ElementDefinition<Boolean> getSwitcher()
147    {
148        return _switcher;
149    }
150    
151    @Override
152    public void setModel(Model model)
153    {
154        super.setModel(model);
155        for (ModelItem modelItem : _children)
156        {
157            modelItem.setModel(model);
158        }
159    }
160    
161    @Override
162    public ModelItemType getType()
163    {
164        return _type;
165    }
166
167    public void setType(ModelItemType type)
168    {
169        _type = type;
170    }
171    
172    @Override
173    public Map<String, Object> toJSON() throws ProcessingException
174    {
175        return toJSON(true);
176    }
177    
178    /**
179     * Converts the model item group in a JSON map
180     * @param includeChildren true to iterate and add children as elements in the returned JSON map, false otherwise
181     * @return The model item as a JSON map
182     * @throws ProcessingException If an error occurs when converting the model item group
183     */
184    public Map<String, Object> toJSON(boolean includeChildren) throws ProcessingException
185    {
186        Map<String, Object> result = super.toJSON();
187        
188        ModelItemType type = getType();
189        result.put("type", type != null ? type.getId() : __DEFAULT_TYPE_ID);
190        
191        ElementDefinition<Boolean> switcher = getSwitcher();
192        if (switcher != null)
193        {
194            Map<String, Object> switcherToJSON = new HashMap<>();
195            
196            switcherToJSON.put("id", switcher.getName());
197            switcherToJSON.put("label", switcher.getLabel());
198            
199            if (switcher.getType() != null)
200            {
201                switcherToJSON.put("default-value", switcher.getType().valueToJSONForClient(switcher.getDefaultValue()));
202            }
203            
204            result.put("switcher", switcherToJSON);
205        }
206        
207        // Do not include switcher in children because it is already included above
208        List<ModelItem> children = getChildren(false);
209        if (includeChildren && !children.isEmpty())
210        {
211            Map<String, Object> elements = new LinkedHashMap<>();
212            for (ModelItem child : children)
213            {
214                String name = child.getName();
215                if (name != null)
216                {
217                    elements.put(name, child.toJSON());
218                }
219            }
220            
221            result.put("elements", elements);
222        }
223        
224        return result;
225    }
226    
227    /**
228     * Creates a {@link ModelItemGroup}
229     * @param name the group's name
230     * @param children the group's children
231     * @return the created {@link ModelItemGroup}
232     */
233    public static ModelItemGroup of(String name, ModelItem... children)
234    {
235        return new ModelItemGroup(name, children);
236    }
237}