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