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