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