001/*
002 *  Copyright 2016 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.core.ui.ribbonconfiguration;
017
018import java.util.ArrayList;
019import java.util.List;
020import java.util.UUID;
021
022import org.apache.avalon.framework.configuration.Configuration;
023import org.apache.avalon.framework.configuration.ConfigurationException;
024import org.apache.avalon.framework.configuration.DefaultConfiguration;
025import org.apache.cocoon.xml.AttributesImpl;
026import org.apache.cocoon.xml.XMLUtils;
027import org.slf4j.Logger;
028import org.xml.sax.ContentHandler;
029import org.xml.sax.SAXException;
030
031import org.ametys.core.ui.RibbonManager;
032import org.ametys.core.ui.RibbonTabsManager;
033import org.ametys.runtime.i18n.I18nizableText;
034
035/**
036 * A tab of the ribbon
037 */
038public class Tab
039{
040    /** The label of the tab */
041    protected I18nizableText _label;
042    
043    /** The optional id of the contextual client side element determining the state of the ribbon */
044    protected String _controlId;
045    
046    /** The color (between 1 and 6) for a contextual tab */
047    protected String _contextualColor;
048    
049    /** The id of the contextual group (can be null for single contextual tab) */
050    protected String _contextualGroup;
051    
052    /** The label of the contextual group */
053    protected I18nizableText _contextualLabel;
054    
055    /** The tab order */
056    protected Object _order;
057    
058    /** True to order before a tab specified by _order */
059    protected Boolean _orderBefore;
060    
061    /** True to override an existing tab instead of defining a new one */
062    protected Boolean _override;
063    
064    /** The list of groups in the tab */
065    protected List<Group> _groups = new ArrayList<>();
066    
067    /** helper for group injection */
068    protected RibbonElementsInjectionHelper<Group> _tabOverrideHelper;
069    
070    /** Logger */
071    protected Logger _log;
072    
073    /**
074     * Creates a tab
075     * @param tabConfiguration The configuration of the tab
076     * @param ribbonManager The ribbon manager
077     * @param defaultOrder The default tab order, if not specified. Can be null
078     * @param logger The logger
079     * @throws ConfigurationException if an error occurs in the configuration
080     */
081    public Tab(Configuration tabConfiguration, RibbonManager ribbonManager, Integer defaultOrder, Logger logger) throws ConfigurationException
082    {
083        _log = logger;
084        
085        if (_log.isDebugEnabled())
086        {
087            _log.debug("Creating tab");
088        }
089
090        _configureId(tabConfiguration);
091        if (tabConfiguration.getAttribute("ref-id", null) != null || tabConfiguration.getChild("tab-control", false) != null)
092        {
093            _generateTabControl(tabConfiguration, ribbonManager);
094        }
095        
096        this._label = new I18nizableText("application", tabConfiguration.getAttribute("label"));
097        if (_log.isDebugEnabled())
098        {
099            _log.debug("Tab label is " + this._label);
100        }
101        
102        this._override = tabConfiguration.getAttributeAsBoolean("override", false);
103
104        _configureGroups(tabConfiguration, ribbonManager);
105        _configureOrder(tabConfiguration, defaultOrder);
106    }
107    
108    /**
109     * Get the id of this tab;
110     * @return the id
111     */
112    public String getId()
113    {
114        return _controlId;
115    }
116    
117    /**
118     * Return true if the tab is contextual
119     * @return true if the tab is contextual
120     */
121    public Boolean isContextual()
122    {
123        return _controlId != null;
124    }
125
126    /**
127     * Configure tab optional id
128     * @param tabConfiguration One tab configuration
129     * @throws ConfigurationException if an error occurred
130     */
131    protected void _configureId(Configuration tabConfiguration) throws ConfigurationException
132    {
133        this._controlId = tabConfiguration.getAttribute("controlId", null);
134        this._contextualColor = tabConfiguration.getAttribute("contextualColor", null);
135        this._contextualGroup = tabConfiguration.getAttribute("contextualGroup", null);
136        
137        String contextualLabelString = tabConfiguration.getAttribute("contextualLabel", null);
138        if (contextualLabelString != null)
139        {
140            this._contextualLabel = new I18nizableText("application", contextualLabelString);
141        }
142        
143        if (_log.isDebugEnabled() && this._controlId != null)
144        {
145            _log.debug("Tab control id is " + this._controlId);
146        }
147    }
148    
149    /**
150     * Generate a new tab control on the fly
151     * @param tabConfiguration The tab configuration
152     * @param ribbonManager The ribbon manager
153     * @throws ConfigurationException If an error occurs
154     */
155    protected void _generateTabControl(Configuration tabConfiguration, RibbonManager ribbonManager) throws ConfigurationException
156    {
157        if (this._controlId == null)
158        {
159            this._controlId = UUID.randomUUID().toString();
160        }
161        
162        DefaultConfiguration defaultConfig = new DefaultConfiguration(tabConfiguration.getChild("tab-control"));
163        String refId = defaultConfig.getAttribute("ref-id", null);
164        
165        String classname = tabConfiguration.getAttribute("class", null);
166        if (classname == null)
167        {
168            if (refId != null)
169            {
170                defaultConfig.setAttribute("point", tabConfiguration.getAttribute("point", RibbonTabsManager.ROLE));
171            }
172            else
173            {
174                classname = org.ametys.core.ui.StaticClientSideElement.class.getName();
175                defaultConfig.setAttribute("class", classname);
176            }
177        }
178        
179        ribbonManager.addExtension(this._controlId, "core-ui", null, defaultConfig);
180        
181        if (_log.isDebugEnabled())
182        {
183            _log.debug("Generated Tab control id is " + this._controlId);
184        }
185    }
186
187    /**
188     * Configure tabs groups
189     * @param tabConfiguration One tab configuration
190     * @param ribbonManager The ribbon manager
191     * @throws ConfigurationException if an error occurred
192     */
193    protected void _configureGroups(Configuration tabConfiguration, RibbonManager ribbonManager) throws ConfigurationException
194    {
195        Configuration[] groupsConfigurations = tabConfiguration.getChild("groups").getChildren("group");
196        for (Configuration groupConfiguration : groupsConfigurations)
197        {
198            Group group = new Group(groupConfiguration, ribbonManager, _log);
199            _groups.add(group);
200        }
201    }
202    
203    private void _configureOrder(Configuration tabConfiguration, Integer defaultOrder)
204    {
205        String order = tabConfiguration.getAttribute("order", null);
206        try
207        {
208            _order = Integer.parseInt(order);
209        }
210        catch (NumberFormatException e)
211        {
212            _order = order != null ? order : defaultOrder;
213        }
214        
215        _orderBefore = tabConfiguration.getAttributeAsBoolean("order-before", false);
216    }
217    
218    /**
219     * Get the tab label
220     * @return Return the tab label
221     */
222    public String getLabel()
223    {
224        return _label.toString();
225    }
226    
227    /**
228     * Get the order attribute of the tab
229     * @return Return the order as a String, or null
230     */
231    public String getOrderAsString()
232    {
233        if (_order instanceof String)
234        {
235            return (String) _order;
236        }
237        return null;
238    }
239    
240    /**
241     * Get the order attribute of the tab
242     * @return Return the order as an Integer, or null
243     */
244    public Integer getOrderAsInteger()
245    {
246        if (_order instanceof Integer)
247        {
248            return (Integer) _order;
249        }
250        return null;
251    }
252    
253    /**
254     * Set the order attribute of the tab
255     * @param order The new order value, either a String or an Integer
256     */
257    public void setOrder(Object order)
258    {
259        _order = order;
260    }
261    
262    /**
263     * True if the tab should be ordered before the tab referenced by the attribute order
264     * @return True if the tab should be ordered before the tab referenced by the attribute order
265     */
266    public boolean orderBefore()
267    {
268        return _orderBefore;
269    }
270    
271    /**
272     * Retrieve the list of configured groups
273     * @return The list of groups
274     */
275    public List<Group> getGroups()
276    {
277        return _groups;
278    }
279
280    /**
281     * Return true if this tab overrides an existing tab
282     * @return True if overrides
283     */
284    public boolean isOverride()
285    {
286        return _override;
287    }
288    
289    /**
290     * Inject a list of groups into this tab
291     * @param groups The list of groups to inject
292     */
293    public void injectGroups(List<Group> groups)
294    {
295        for (Group group : groups)
296        {
297            if (!group.isOverride())
298            {
299                if (_tabOverrideHelper == null)
300                {
301                    _tabOverrideHelper = new RibbonElementsInjectionHelper<>(_groups, _log);
302                }
303                
304                if (_log.isDebugEnabled())
305                {
306                    _log.debug("RibbonConfigurationManager : new group '" + group._label.toString() + "' injected into tab '" + _label.toString() + "'");
307                }
308
309                _tabOverrideHelper.injectElements(group, group.getOrder());
310            }
311        }
312    }
313    
314    /**
315     * Inject a list of overriding groups into this tab
316     * @param groups The list of groups to inject
317     */
318    public void injectGroupsOverride(List<Group> groups)
319    {
320        for (Group group : groups)
321        {
322            if (group.isOverride())
323            {
324                for (Group selfGroup : _groups)
325                {
326                    if (selfGroup._label.equals(group._label))
327                    {
328                        if (_log.isDebugEnabled())
329                        {
330                            _log.debug("RibbonConfigurationManager : overriding group '" + group._label + "' of tab '" + _label + "' to inject new controls");
331                        }
332                        
333                        selfGroup.injectGroup(group);
334                    }
335                }
336            }
337        }
338    }
339    
340    /**
341     * Sax the configuration of the tab.
342     * @param handler The content handler where to sax
343     * @param groups The list of groups to sax
344     * @throws SAXException if an error occurs
345     */
346    public void saxGroups(ContentHandler handler, List<Group> groups) throws SAXException
347    {
348        AttributesImpl attrs = new AttributesImpl();
349        attrs.addCDATAAttribute("label", _label.getCatalogue() + ":" + _label.getKey());
350        StringBuilder i18nAttr = new StringBuilder("label");
351        
352        if (_controlId != null)
353        {
354            attrs.addCDATAAttribute("controlId", _controlId);
355            if (_contextualColor != null)
356            {
357                attrs.addCDATAAttribute("contextualColor", _contextualColor);
358            }
359            if (_contextualGroup != null)
360            {
361                attrs.addCDATAAttribute("contextualGroup", _contextualGroup);
362            }
363            if (_contextualLabel != null)
364            {
365                attrs.addCDATAAttribute("contextualLabel", _contextualLabel.getCatalogue() + ":" + _contextualLabel.getKey());
366                i18nAttr.append(" contextualLabel");
367            }
368        }
369        
370        attrs.addCDATAAttribute("http://apache.org/cocoon/i18n/2.1", "attr", "i18n:attr", i18nAttr.toString());
371        
372        XMLUtils.startElement(handler, "tab", attrs);
373
374        XMLUtils.startElement(handler, "groups");
375        for (Group group : groups)
376        {
377            group.toSAX(handler);
378        }
379        XMLUtils.endElement(handler, "groups");
380        
381        XMLUtils.endElement(handler, "tab");
382    }
383    
384    @Override
385    public String toString()
386    {
387        return super.toString() + "[" + _label + "]";
388    }
389}