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.HashMap;
020import java.util.List;
021import java.util.Map;
022import java.util.Objects;
023
024import org.apache.cocoon.ProcessingException;
025import org.apache.cocoon.xml.AttributesImpl;
026import org.apache.commons.lang3.StringUtils;
027import org.xml.sax.ContentHandler;
028import org.xml.sax.SAXException;
029
030import org.ametys.core.util.SizeUtils.ExcludeFromSizeCalculation;
031import org.ametys.core.util.XMLUtils;
032import org.ametys.runtime.i18n.I18nizableText;
033import org.ametys.runtime.model.exception.BadItemTypeException;
034
035/**
036 * View reference to a group of model items
037 * @param <T> type of the referenced model item group
038 */
039public class ModelViewItemGroup<T extends ModelItemGroup> extends AbstractViewItemGroup implements ModelViewItem<T>
040{
041    @ExcludeFromSizeCalculation
042    private T _definition;
043    
044    @ExcludeFromSizeCalculation
045    private ViewItemAccessor _parent;
046    
047    /**
048     * Creates a {@link ModelViewItemGroup} with the items of the given {@link ModelItemGroup}
049     * @param modelItem the model item group
050     * @return the created {@link ModelViewItemGroup}
051     * @throws IllegalArgumentException if the model item is <code>null</code>
052     */
053    public static ModelViewItemGroup of(ModelItemGroup modelItem) throws IllegalArgumentException
054    {
055        if (modelItem == null)
056        {
057            throw new IllegalArgumentException("Unable to create the view from a null model");
058        }
059        else
060        {
061            return ViewHelper.createViewItemAccessor(List.of(modelItem));
062        }
063    }
064    
065    /**
066     * Creates a {@link ModelViewItemGroup} with the given items
067     * @param modelItem the model item containing items definitions
068     * @param itemPaths the paths of the items to put in the view item
069     * @return the created {@link ModelViewItemGroup}
070     * @throws IllegalArgumentException if the model item is <code>null</code> or if an item path is <code>null</code>, empty, or is not defined in the given model items
071     * @throws BadItemTypeException if a segment in a path (but not the last) does not represent a group item
072     */
073    public static ModelViewItemGroup of(ModelItemGroup modelItem, String... itemPaths) throws IllegalArgumentException, BadItemTypeException
074    {
075        if (modelItem == null)
076        {
077            throw new IllegalArgumentException("Unable to create the view from a null model");
078        }
079        else
080        {
081            return ViewHelper.createViewItemAccessor(List.of(modelItem), itemPaths);
082        }
083    }
084    
085    public T getDefinition()
086    {
087        return _definition;
088    }
089    
090    public void setDefinition(T definition)
091    {
092        if (definition == null)
093        {
094            throw new IllegalArgumentException("Try to set a null definition to the model view item group");
095        }
096        
097        _definition = definition;
098    }
099    
100    public ViewItemAccessor getParent()
101    {
102        return _parent;
103    }
104    
105    public void setParent(ViewItemAccessor parent)
106    {
107        _parent = parent;
108    }
109    
110    public String getName()
111    {
112        if (_definition != null)
113        {
114            return _definition.getName();
115        }
116        else
117        {
118            return null;
119        }
120    }
121    
122    @Override
123    public I18nizableText getLabel()
124    {
125        I18nizableText label = super.getLabel();
126        if (label != null)
127        {
128            return label;
129        }
130        
131        return getDefinition().getLabel();
132    }
133    
134    @Override
135    public I18nizableText getDescription()
136    {
137        I18nizableText desc = super.getDescription();
138        if (desc != null)
139        {
140            return desc;
141        }
142        
143        return getDefinition().getDescription();
144    }
145    
146    public Map<String, Object> toJSON(DefinitionContext context) throws ProcessingException
147    {
148        ModelItemGroup definition = getDefinition();
149        if (definition != null)
150        {
151            Map<String, Object> result = definition.toJSON(context, false);
152            if (!result.isEmpty())
153            {
154                // use overridden label and description if present  
155                result.put("label", getLabel());
156                result.put("description", getDescription());
157                result.put("role", getRole());
158                
159                if (StringUtils.isEmpty(getName()))
160                {
161                    result.put("unnamed-group", true);
162                }
163                
164                result.putAll(_childrenToJSON(context));
165            }
166            return result;
167        }
168        
169        return Map.of();
170    }
171    
172    /**
173     * 
174     * Converts the group's children in a JSON map
175     * @param context the context of the definitions referenced in this group and/or the children
176     * @return The children as a JSON map
177     * @throws ProcessingException If an error occurs when converting the children
178     */
179    protected Map<String, Object> _childrenToJSON(DefinitionContext context) throws ProcessingException
180    {
181        Map<String, Object> result = new HashMap<>();
182        
183        List<ViewItem> viewItems = _getChildrenWithoutSwitcher();
184        if (!viewItems.isEmpty())
185        {
186            result.put("elements", ViewHelper.viewItemsToJSON(viewItems, context));
187        }
188        
189        return result;
190    }
191    
192    @SuppressWarnings("static-access")
193    public void toSAX(ContentHandler contentHandler, DefinitionContext context) throws SAXException
194    {
195        ModelItemGroup definition = getDefinition();
196        if (definition != null)
197        {
198            String tagName = context.getItemTagName().orElse(DEFAULT_ITEM_TAG_NAME);
199            
200            AttributesImpl attributes = new AttributesImpl();
201            attributes.addCDATAAttribute("name", definition.getName());
202            attributes.addCDATAAttribute("plugin", definition.getPluginName());
203            attributes.addCDATAAttribute("path", definition.getPath());
204            
205            String typeId = definition.getType() != null ? definition.getType().getId() : ModelItemGroup.DEFAULT_TYPE_ID;
206            attributes.addCDATAAttribute("type", typeId);
207            
208            XMLUtils.startElement(contentHandler, tagName, attributes);
209            
210            XMLUtils.createElementIfNotNull(contentHandler, "role", getRole());
211            XMLUtils.createI18nElementIfNotNull(contentHandler, "label", getLabel());
212            XMLUtils.createI18nElementIfNotNull(contentHandler, "description", getDescription());
213            
214            definition.toSAX(contentHandler, context);
215            
216            for (ViewItem viewItem : _getChildrenWithoutSwitcher())
217            {
218                viewItem.toSAX(contentHandler, context);
219            }
220            
221            XMLUtils.endElement(contentHandler, tagName);
222        }
223    }
224    
225    private List<ViewItem> _getChildrenWithoutSwitcher()
226    {
227        ElementDefinition switcher = getDefinition().getSwitcher();
228        if (switcher == null)
229        {
230            return getViewItems();
231        }
232        
233        List<ViewItem> childrenWithoutSwitcher = new ArrayList<>();
234        for (ViewItem child : getViewItems())
235        {
236            if (child instanceof ViewElement)
237            {
238                ElementDefinition childDefinitonReference = ((ViewElement) child).getDefinition();
239                if (!switcher.equals(childDefinitonReference))
240                {
241                    childrenWithoutSwitcher.add(child);
242                }
243            }
244        }
245        
246        return childrenWithoutSwitcher;
247    }
248    
249    @SuppressWarnings("unchecked")
250    @Override
251    public void copyTo(ViewItem item)
252    {
253        super.copyTo(item);
254        
255        assert item instanceof ModelViewItemGroup;
256        ((ModelViewItemGroup) item).setDefinition(getDefinition());
257    }
258    
259    public ModelViewItemGroup createInstance()
260    {
261        return new ModelViewItemGroup();
262    }
263
264    @Override
265    public int hashCode()
266    {
267        final int prime = 31;
268        int result = super.hashCode();
269        result = prime * result + Objects.hash(_definition);
270        return result;
271    }
272
273    @Override
274    public boolean equals(Object obj)
275    {
276        if (this == obj)
277        {
278            return true;
279        }
280        if (!super.equals(obj))
281        {
282            return false;
283        }
284        if (getClass() != obj.getClass())
285        {
286            return false;
287        }
288        ModelViewItemGroup other = (ModelViewItemGroup) obj;
289        return Objects.equals(_definition, other._definition);
290    }
291    
292    @Override
293    public String toString()
294    {
295        return _definition.toString() + ": " + _children.toString();
296    }
297}