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.Collection;
020import java.util.Collections;
021import java.util.LinkedHashMap;
022import java.util.List;
023import java.util.Map;
024import java.util.Objects;
025
026import org.apache.cocoon.ProcessingException;
027
028import org.ametys.runtime.i18n.I18nizableText;
029import org.ametys.runtime.model.exception.BadItemTypeException;
030import org.ametys.runtime.util.ModifiableLabelable;
031
032/**
033 * View of items
034 */
035public class View implements ViewItemContainer, ModifiableLabelable
036{
037    private String _name;
038    private I18nizableText _label;
039    private I18nizableText _description;
040    private String _iconGlyph;
041    private String _iconDecorator;
042    private String _smallIcon;
043    private String _mediumIcon;
044    private String _largeIcon;
045    private boolean _isInternal;
046    
047    private List<ViewItem> _items = new ArrayList<>();
048    
049    /**
050     * Creates a {@link View} with the items of the given {@link Model}
051     * @param model the model
052     * @return the created {@link View}
053     * @throws IllegalArgumentException if the model is <code>null</code>
054     */
055    public static View of(Model model) throws IllegalArgumentException
056    {
057        if (model == null)
058        {
059            throw new IllegalArgumentException("Unable to create the view from a null model");
060        }
061        else
062        {
063            return ViewHelper.createViewItemAccessor(List.of(model));
064        }
065    }
066    
067    /**
068     * Creates a {@link View} with the items of the given {@link Model}s
069     * @param models the models
070     * @return the created {@link View}
071     * @throws IllegalArgumentException if the models collection is empty
072     */
073    public static View of(Collection<? extends Model> models) throws IllegalArgumentException
074    {
075        return ViewHelper.createViewItemAccessor(models);
076    }
077    
078    /**
079     * Creates a {@link View} with the given items
080     * @param model the model containing items definitions
081     * @param itemPaths the paths of the items to put in the view
082     * @return the created {@link View}
083     * @throws IllegalArgumentException if the model is <code>null</code> or if an item path is <code>null</code>, empty, or is not defined in the given models
084     * @throws BadItemTypeException if a segment in a path (but not the last) does not represent a group item
085     */
086    public static View of(Model model, String... itemPaths) throws IllegalArgumentException, BadItemTypeException
087    {
088        if (model == null)
089        {
090            throw new IllegalArgumentException("Unable to create the view from a null model");
091        }
092        else
093        {
094            return ViewHelper.createViewItemAccessor(List.of(model), itemPaths);
095        }
096    }
097    
098    /**
099     * Creates a {@link View} with the given items
100     * @param models the models containing items definitions
101     * @param itemPaths the paths of the items to put in the view
102     * @return the created {@link View}
103     * @throws IllegalArgumentException if the models collection is empty or if an item path is <code>null</code>, empty, or is not defined in the given models
104     * @throws BadItemTypeException if a segment in a path (but not the last) does not represent a group item
105     */
106    public static View of(Collection<? extends Model> models, String... itemPaths) throws IllegalArgumentException, BadItemTypeException
107    {
108        return ViewHelper.createViewItemAccessor(models, itemPaths);
109    }
110    
111    /**
112     * Creates a {@link View} with the given items. If the items are in a group, the hierarchy will be kept and the corresponding containers will be created
113     * @param modelItems the items to put in the view
114     * @return the created {@link View}
115     */
116    public static View of(ModelItem... modelItems)
117    {
118        View view = new View();
119        
120        for (ModelItem modelItem : modelItems)
121        {
122            ViewHelper.addViewItem(modelItem.getPath(), view, modelItem.getModel());
123        }
124        
125        return view;
126    }
127    
128    /**
129     * Copy the current view in the given one.
130     * Its view items are not copied
131     * @param view the copy
132     */
133    public void copyTo(View view)
134    {
135        view.setName(getName());
136        view.setLabel(getLabel());
137        view.setDescription(getDescription());
138        view.setInternal(isInternal());
139        
140        view.setIconGlyph(getIconGlyph());
141        view.setIconDecorator(getIconDecorator());
142        view.setSmallIcon(getSmallIcon());
143        view.setMediumIcon(getMediumIcon());
144        view.setLargeIcon(getLargeIcon());
145    }
146
147    public String getName()
148    {
149        return _name;
150    }
151
152    public void setName(String name)
153    {
154        _name = name;
155    }
156    
157    public I18nizableText getLabel()
158    {
159        return _label;
160    }
161
162    public void setLabel(I18nizableText label)
163    {
164        _label = label;
165    }
166    
167    public I18nizableText getDescription()
168    {
169        return _description;
170    }
171    
172    public void setDescription(I18nizableText description)
173    {
174        _description = description;
175    }
176    
177    /**
178     * Retrieves the CSS class to use for glyph icon
179     * @return the glyph name.
180     */
181    public String getIconGlyph()
182    {
183        return _iconGlyph;
184    }
185    
186    /**
187     * Set the CSS class to use for glyph icon
188     * @param iconGlyph the glyph name.
189     */
190    public void setIconGlyph(String iconGlyph)
191    {
192        _iconGlyph = iconGlyph;
193    }
194    
195    /**
196     * Retrieves the CSS class to use for decorator above the main icon
197     * @return the glyph name.
198     */
199    public String getIconDecorator()
200    {
201        return _iconDecorator;
202    }
203    
204    /**
205     * Set the CSS class to use for decorator above the main icon
206     * @param iconDecorator the glyph name.
207     */
208    public void setIconDecorator(String iconDecorator)
209    {
210        _iconDecorator = iconDecorator;
211    }
212    
213    /**
214     * Retrieves the URL of the small icon without the context path.
215     * @return the icon URL for the small image 16x16.
216     */
217    public String getSmallIcon()
218    {
219        return _smallIcon;
220    }
221    
222    /**
223     * Set the URL of the small icon.
224     * @param smallIcon the URL of the small icon, without the context path.
225     */
226    public void setSmallIcon(String smallIcon)
227    {
228        _smallIcon = smallIcon;
229    }
230    
231    /**
232     * Retrieves the URL of the small icon without the context path.
233     * @return the icon URL for the medium sized image 32x32.
234     */
235    public String getMediumIcon()
236    {
237        return _mediumIcon;
238    }
239    
240    /**
241     * Set the URL of the medium icon.
242     * @param mediumIcon the URL of the medium icon, without the context path.
243     */
244    public void setMediumIcon(String mediumIcon)
245    {
246        _mediumIcon = mediumIcon;
247    }
248    
249    /**
250     * Retrieves the URL of the small icon without the context path.
251     * @return the icon URL for the large image 48x48.
252     */
253    public String getLargeIcon()
254    {
255        return _largeIcon;
256    }
257    
258    /**
259     * Set the URL of the large icon.
260     * @param largeIcon the URL of the large icon, without the context path.
261     */
262    public void setLargeIcon(String largeIcon)
263    {
264        _largeIcon = largeIcon;
265    }
266    
267    /**
268     * Determines if the view is for internal use only
269     * @return <code>true</code> if the view is for internal use only, <code>false</code> otherwise
270     */
271    public boolean isInternal()
272    {
273        return _isInternal;
274    }
275
276    /**
277     * Set the internal status
278     * @param isInternal <code>true</code> to make the view for internal use only, <code>false</code> otherwise
279     */
280    public void setInternal(boolean isInternal)
281    {
282        _isInternal = isInternal;
283    }
284    
285    public List<ViewItem> getViewItems()
286    {
287        return Collections.unmodifiableList(this._items);
288    }
289    
290    public void addViewItem(ViewItem item)
291    {
292        _items.add(item);
293    }
294    
295    public void insertViewItem(ViewItem item, int index)
296    {
297        if (index >= 0 && index <= _items.size())
298        {
299            _items.add(index, item);
300        }
301        else
302        {
303            throw new IllegalArgumentException("Unable to insert an item at index " + index + ". This group contains " + _items.size() + " items.");
304        }
305    }
306    
307    public boolean removeViewItem(ViewItem item)
308    {
309        return _items.remove(item);
310    }
311    
312    public void clear()
313    {
314        _items.clear();
315    }
316
317    /**
318     * Converts the view in a JSON map
319     * @param context the context of the definitions included in the view
320     * @return The view as a JSON map
321     * @throws ProcessingException If an error occurs when converting the view
322     */
323    public Map<String, Object> toJSON(DefinitionContext context) throws ProcessingException
324    {
325        Map<String, Object> result = new LinkedHashMap<>();
326        
327        result.put("name", getName());
328        result.put("label", getLabel());
329        result.put("description", getDescription());
330        
331        result.put("icon-glyph", getIconGlyph());
332        result.put("icon-decorator", getIconDecorator());
333        result.put("small-icon-path", getSmallIcon());
334        result.put("medium-icon-path", getMediumIcon());
335        result.put("large-icon-path", getLargeIcon());
336    
337        result.put("elements", ViewHelper.viewItemsToJSON(getViewItems(), context));
338        
339        return result;
340    }
341    
342    /**
343     * Include the given view to the current one.
344     * Add the items of the view to include if they are not already present in the current view
345     * @param viewToInclude the view to include
346     */
347    public void includeView(View viewToInclude)
348    {
349        View referenceView = new View();
350        referenceView.addViewItems(getViewItems());
351        ViewHelper.addViewAccessorItems(this, viewToInclude, referenceView);
352    }
353
354    @Override
355    public int hashCode()
356    {
357        return Objects.hash(_items, _name);
358    }
359    
360    @Override
361    public boolean equals(Object obj)
362    {
363        if (this == obj)
364        {
365            return true;
366        }
367        if (obj == null)
368        {
369            return false;
370        }
371        if (getClass() != obj.getClass())
372        {
373            return false;
374        }
375        View other = (View) obj;
376        return Objects.equals(_items, other._items) && Objects.equals(_name, other._name);
377    }
378
379    /**
380     * Indicates whether some other object is "equal to" this one.
381     * @param obj the reference object with which to compare.
382     * @param checkDetails <code>true</code> to check the view's details during comparison (label, description, icon, ...)
383     * @return <code>true</code> if this object is the same as the given obj, <code>false</code> otherwise.
384     */
385    public boolean equals(Object obj, boolean checkDetails)
386    {
387        if (!equals(obj))
388        {
389            return false;
390        }
391        else if (checkDetails)
392        {
393            View other = (View) obj;
394            
395            for (int i = 0; i < _items.size(); i++)
396            {
397                ViewItem item = _items.get(i);
398                ViewItem otherItem = other._items.get(i);
399                
400                if (!item.equals(otherItem, checkDetails))
401                {
402                    return false;
403                }
404            }
405            
406            return Objects.equals(_description, other._description) && Objects.equals(_iconDecorator, other._iconDecorator) && Objects.equals(_iconGlyph, other._iconGlyph)
407                    && _isInternal == other._isInternal && Objects.equals(_label, other._label);
408        }
409        else
410        {
411            return true;
412        }
413    }
414}