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            AttributesImpl attributes = new AttributesImpl();
199            attributes.addCDATAAttribute("name", definition.getName());
200            attributes.addCDATAAttribute("plugin", definition.getPluginName());
201            attributes.addCDATAAttribute("path", definition.getPath());
202            
203            String typeId = definition.getType() != null ? definition.getType().getId() : ModelItemGroup.DEFAULT_TYPE_ID;
204            attributes.addCDATAAttribute("type", typeId);
205            
206            XMLUtils.startElement(contentHandler, "metadata", attributes);
207            
208            XMLUtils.createElementIfNotNull(contentHandler, "role", getRole());
209            XMLUtils.createI18nElementIfNotNull(contentHandler, "label", getLabel());
210            XMLUtils.createI18nElementIfNotNull(contentHandler, "description", getDescription());
211            
212            definition.toSAX(contentHandler, context);
213            
214            for (ViewItem viewItem : _getChildrenWithoutSwitcher())
215            {
216                viewItem.toSAX(contentHandler, context);
217            }
218            
219            XMLUtils.endElement(contentHandler, "metadata");
220        }
221    }
222    
223    private List<ViewItem> _getChildrenWithoutSwitcher()
224    {
225        ElementDefinition switcher = getDefinition().getSwitcher();
226        if (switcher == null)
227        {
228            return getViewItems();
229        }
230        
231        List<ViewItem> childrenWithoutSwitcher = new ArrayList<>();
232        for (ViewItem child : getViewItems())
233        {
234            if (child instanceof ViewElement)
235            {
236                ElementDefinition childDefinitonReference = ((ViewElement) child).getDefinition();
237                if (!switcher.equals(childDefinitonReference))
238                {
239                    childrenWithoutSwitcher.add(child);
240                }
241            }
242        }
243        
244        return childrenWithoutSwitcher;
245    }
246    
247    @SuppressWarnings("unchecked")
248    @Override
249    public void copyTo(ViewItem item)
250    {
251        super.copyTo(item);
252        
253        assert item instanceof ModelViewItemGroup;
254        ((ModelViewItemGroup) item).setDefinition(getDefinition());
255    }
256    
257    public ModelViewItemGroup createInstance()
258    {
259        return new ModelViewItemGroup();
260    }
261
262    @Override
263    public int hashCode()
264    {
265        final int prime = 31;
266        int result = super.hashCode();
267        result = prime * result + Objects.hash(_definition);
268        return result;
269    }
270
271    @Override
272    public boolean equals(Object obj)
273    {
274        if (this == obj)
275        {
276            return true;
277        }
278        if (!super.equals(obj))
279        {
280            return false;
281        }
282        if (getClass() != obj.getClass())
283        {
284            return false;
285        }
286        ModelViewItemGroup other = (ModelViewItemGroup) obj;
287        return Objects.equals(_definition, other._definition);
288    }
289    
290    @Override
291    public String toString()
292    {
293        return _definition.toString() + ": " + _children.toString();
294    }
295}