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