001/*
002 *  Copyright 2011 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.skinfactory.generators;
017
018import java.io.IOException;
019import java.io.InputStream;
020import java.nio.file.Files;
021import java.nio.file.Path;
022import java.util.ArrayList;
023import java.util.Collection;
024import java.util.HashMap;
025import java.util.LinkedHashMap;
026import java.util.List;
027import java.util.Map;
028import java.util.regex.Pattern;
029
030import org.apache.avalon.framework.service.ServiceException;
031import org.apache.avalon.framework.service.ServiceManager;
032import org.apache.cocoon.ProcessingException;
033import org.apache.cocoon.generation.ServiceableGenerator;
034import org.apache.cocoon.xml.AttributesImpl;
035import org.apache.cocoon.xml.XMLUtils;
036import org.apache.commons.lang.StringUtils;
037import org.apache.excalibur.xml.sax.SAXParser;
038import org.xml.sax.InputSource;
039import org.xml.sax.SAXException;
040
041import org.ametys.core.util.I18nUtils;
042import org.ametys.core.util.IgnoreRootHandler;
043import org.ametys.runtime.i18n.I18nizableText;
044import org.ametys.skinfactory.SkinFactoryComponent;
045import org.ametys.skinfactory.parameters.AbstractSkinParameter;
046import org.ametys.skinfactory.parameters.AbstractSkinParameter.SkinParameterType;
047import org.ametys.web.skin.SkinModel;
048import org.ametys.web.skin.SkinModelsManager;
049
050/**
051 * Generate the ribbon file
052 */
053public class GenerateRibbonFile extends ServiceableGenerator
054{
055    private SkinFactoryComponent _skinFactoryManager;
056    private SkinModelsManager _modelsManager;
057    private I18nUtils _i18nUtils;
058    
059    private final HashMap<Pattern, String> _glyphAssociations = new LinkedHashMap<>();
060    private SAXParser _saxParser;
061    {
062        _glyphAssociations.put(Pattern.compile("^(h1|h2|h3|h4)$"), "ametysicon-header");
063        _glyphAssociations.put(Pattern.compile("text"), "ametysicon-text");
064        _glyphAssociations.put(Pattern.compile("link"), "ametysicon-link23");
065        _glyphAssociations.put(Pattern.compile("list"), "ametysicon-list4");
066        _glyphAssociations.put(Pattern.compile("vmenu^$"), "ametysicon-vmenu");
067        _glyphAssociations.put(Pattern.compile("menu"), "ametysicon-menu");
068        _glyphAssociations.put(Pattern.compile("^(item|subitem)$"), "ametysicon-menu-element");
069        _glyphAssociations.put(Pattern.compile("title|header"), "ametysicon-header");
070        _glyphAssociations.put(Pattern.compile("table"), "ametysicon-tables1");
071        _glyphAssociations.put(Pattern.compile("border"), "ametysicon-tables1"); // FIXME when new icon has arrived
072        _glyphAssociations.put(Pattern.compile("image"), "ametysicon-image2");
073        // Default glyph for unknown style
074        _glyphAssociations.put(Pattern.compile("."), "ametysicon-designer");
075    }
076    
077    @Override
078    public void service(ServiceManager smanager) throws ServiceException 
079    {
080        super.service(smanager);
081        _skinFactoryManager = (SkinFactoryComponent) smanager.lookup(SkinFactoryComponent.ROLE);
082        _modelsManager = (SkinModelsManager)  smanager.lookup(SkinModelsManager.ROLE);
083        _i18nUtils = (I18nUtils) smanager.lookup(I18nUtils.ROLE);
084        _saxParser = (SAXParser) manager.lookup(SAXParser.ROLE);
085    }
086    
087    @Override
088    public void generate() throws IOException, SAXException, ProcessingException
089    {
090        contentHandler.startDocument();
091        XMLUtils.startElement(contentHandler, "CMS");
092        
093        String modelName = parameters.getParameter("modelName", null);
094        SkinModel model = _modelsManager.getModel(modelName);
095        
096        Path ribbonFile = model.getPath().resolve("model/cms-ribbon.xml");
097        if (Files.exists(ribbonFile))
098        {
099            try (InputStream is = Files.newInputStream(ribbonFile))
100            {
101                _saxParser.parse(new InputSource(is), new IgnoreRootHandler(contentHandler));
102            }
103        }
104        
105        Map<String, RibbonTab> tabs = _getGroupedParams(_skinFactoryManager.getModelParameters(modelName));
106        
107        XMLUtils.startElement(contentHandler, "parameters");
108        for (RibbonTab tab : tabs.values())
109        {
110            AttributesImpl attrs = new AttributesImpl();
111            attrs.addCDATAAttribute("id", tab.getId());
112            attrs.addCDATAAttribute("label", _i18nUtils.translate(tab.getLabel()));
113            XMLUtils.startElement(contentHandler, "tab", attrs);
114            
115            XMLUtils.startElement(contentHandler, "groups");
116            
117            Collection<RibbonGroup> groups = tab.getGroups();
118            for (RibbonGroup group : groups)
119            {
120                attrs = new AttributesImpl();
121                attrs.addCDATAAttribute("id", group.getId());
122                attrs.addCDATAAttribute("label", _i18nUtils.translate(group.getLabel()));
123                XMLUtils.startElement(contentHandler, "group", attrs);
124                
125                Collection<RibbonElement> elmts = group.getRibbonElements();
126                for (RibbonElement elt : elmts)
127                {
128                    if (elt instanceof ParameterControl)
129                    {
130                        attrs = new AttributesImpl();
131                        attrs.addCDATAAttribute("id", elt.getId());
132                        XMLUtils.createElement(contentHandler, "parameter", attrs);
133                    }
134                    else if (elt instanceof Menu)
135                    {
136                        _saxMenu ((Menu) elt);
137                    }
138                }
139                XMLUtils.endElement(contentHandler, "group");
140            }
141            XMLUtils.endElement(contentHandler, "groups");
142            
143            XMLUtils.endElement(contentHandler, "tab");
144        }
145        XMLUtils.endElement(contentHandler, "parameters");
146        
147        XMLUtils.endElement(contentHandler, "CMS");
148        contentHandler.endDocument();
149        
150    }
151    
152    private void _saxMenu (Menu menu) throws SAXException
153    {
154        AttributesImpl attrs = new AttributesImpl();
155        attrs.addCDATAAttribute("id", menu.getId());
156        attrs.addCDATAAttribute("label", _i18nUtils.translate(menu.getLabel()));
157        _saxMenuIcons (menu, attrs);
158        XMLUtils.startElement(contentHandler, "menu", attrs);
159        
160        for (String itemId : menu.getItems())
161        {
162            attrs = new AttributesImpl();
163            attrs.addCDATAAttribute("id", itemId);
164            XMLUtils.createElement(contentHandler, "parameter", attrs);
165        }
166        
167        for (Menu submenu : menu.getMenus())
168        {
169            _saxMenu(submenu);
170        }
171        
172        XMLUtils.endElement(contentHandler, "menu");
173    }
174    
175    private Map<String, RibbonTab> _getGroupedParams (Map<String, AbstractSkinParameter> skinParameters)
176    {
177        Map<String, RibbonTab> tabs = new HashMap<>();
178        
179        for (AbstractSkinParameter param : skinParameters.values())
180        {   
181            String id = param.getId();
182            String[] parts = id.split("\\.");
183            
184            String tabId = "org.ametys.skinfactory.Tab." + (parts.length >= 3 ? parts[0] : "default");
185            String groupId = parts.length >= 3 ? StringUtils.join(parts, ".", 0, 2) : "default.default";
186            String eltId = null;
187            
188            String itemId = null;
189            String subItemId = null;
190            
191            if (parts.length <= 3 || !param.getType().equals(SkinParameterType.CSS))
192            {
193                eltId = StringUtils.join(parts, ".");
194            }
195            else
196            {
197                eltId = StringUtils.join(parts, ".", 0, 3);
198                itemId = parts.length ==  4 ? StringUtils.join(parts, ".") : StringUtils.join(parts, ".", 0, 4);
199                
200                if (parts.length > 4)
201                {
202                    subItemId = StringUtils.join(parts, ".");
203                }
204            }
205            
206            _addGroupedParam(tabs, tabId, groupId, eltId, itemId, subItemId);
207        }
208        
209        return tabs;
210    }
211    
212    private void _addGroupedParam (Map<String, RibbonTab> tabs, String tabId, String groupId, String controlId, String itemId, String subItemId)
213    {
214        if (!tabs.containsKey(tabId))
215        {
216            tabs.put(tabId, new RibbonTab(tabId, new I18nizableText(tabId.substring(tabId.lastIndexOf(".") + 1))));
217        }
218        
219        RibbonTab ribbonTab = tabs.get(tabId);
220        if (!ribbonTab.hasGroup(groupId))
221        {
222            ribbonTab.addGroup(new RibbonGroup(groupId, new I18nizableText(groupId.substring(groupId.lastIndexOf(".") + 1))));
223        }
224        
225        RibbonGroup ribbonGroup = ribbonTab.getGroup(groupId);
226        
227        if (itemId != null)
228        {
229            if (!ribbonGroup.hasRibbonElement(controlId))
230            {
231                ribbonGroup.addRibbonElement(new Menu(controlId, new I18nizableText(controlId.substring(controlId.lastIndexOf(".") + 1))));
232            }
233            
234            RibbonElement ribbonControl = ribbonGroup.getRibbonElement(controlId);
235            
236            if (ribbonControl instanceof Menu)
237            {
238                Menu menu = (Menu) ribbonControl;
239                
240                if (subItemId == null)
241                {
242                    menu.addItem(itemId);
243                }
244                else
245                {
246                    if (!menu.hasMenu(itemId))
247                    {
248                        menu.addMenu(new Menu(itemId, new I18nizableText(itemId.substring(itemId.lastIndexOf(".") + 1))));
249                    }
250                    
251                    Menu subMenu = menu.getMenu(itemId);
252                    subMenu.addItem(subItemId);
253                }
254            }
255        }
256        else
257        {
258            ribbonGroup.addRibbonElement(new ParameterControl(controlId));
259        }
260    }
261    
262    private void _saxMenuIcons (Menu menu, AttributesImpl attrs)
263    {
264        String label = _i18nUtils.translate(menu.getLabel());
265        
266        for (Map.Entry<Pattern, String> entry : _glyphAssociations.entrySet())
267        {
268            Pattern pattern = entry.getKey();
269            if (pattern.matcher(label).matches())
270            {
271                attrs.addCDATAAttribute("iconGlyph", entry.getValue());
272                return;
273            }
274        }
275    }
276    
277    class RibbonTab
278    {
279        private I18nizableText _label;
280        private String _id;
281        private Map<String, RibbonGroup> _groups;
282        
283        public RibbonTab(String id, I18nizableText label)
284        {
285            _id = id;
286            _label = label;
287            _groups = new HashMap<>();
288        }
289        
290        String getId ()
291        {
292            return _id;
293        }
294        
295        I18nizableText getLabel ()
296        {
297            return _label;
298        }
299        
300        Collection<RibbonGroup> getGroups ()
301        {
302            return _groups.values();
303        }
304        
305        void addGroup (RibbonGroup group)
306        {
307            _groups.put(group.getId(), group);
308        }
309        
310        boolean hasGroup (String groupId)
311        {
312            return _groups.containsKey(groupId);
313        }
314        
315        RibbonGroup getGroup (String groupId)
316        {
317            return _groups.get(groupId);
318        }
319    }
320    
321    class RibbonGroup 
322    {
323        private String _id;
324        private I18nizableText _label;
325        private Map<String, RibbonElement> _ribbonElts;
326        
327        public RibbonGroup(String id, I18nizableText label)
328        {
329            _id = id;
330            _label = label;
331            _ribbonElts = new HashMap<>();
332        }
333        
334        String getId()
335        {
336            return _id;
337        }
338        
339        I18nizableText getLabel ()
340        {
341            return _label;
342        }
343        
344        void addRibbonElement (RibbonElement control)
345        {
346            _ribbonElts.put(control.getId(), control);
347        }
348        
349        boolean hasRibbonElement (String controlId)
350        {
351            return _ribbonElts.containsKey(controlId);
352        }
353        
354        RibbonElement getRibbonElement (String controlId)
355        {
356            return _ribbonElts.get(controlId);
357        }
358        
359        Collection<RibbonElement> getRibbonElements ()
360        {
361            return _ribbonElts.values();
362        }
363    }
364    
365    interface RibbonElement
366    {
367        public String getId();
368    }
369    
370    class Menu implements RibbonElement
371    {
372        private String _id;
373        private I18nizableText _label;
374        private List<String> _items;
375        private Map<String, Menu> _menus;
376        
377        public Menu(String id, I18nizableText label)
378        {
379            _id = id;
380            _label = label;
381            _items = new ArrayList<>();
382            _menus = new HashMap<>();
383        }
384        
385        @Override
386        public String getId()
387        {
388            return _id;
389        }
390        
391        I18nizableText getLabel ()
392        {
393            return _label;
394        }
395        
396        void addItem (String id)
397        {
398            _items.add(id);
399        }
400        
401        void addMenu (Menu menu)
402        {
403            _menus.put(menu.getId(), menu);
404        }
405        
406        boolean hasMenu (String menuId)
407        {
408            return _menus.containsKey(menuId);
409        }
410        
411        Menu getMenu (String menuId)
412        {
413            return _menus.get(menuId);
414        }
415        
416        List<String> getItems ()
417        {
418            return _items;
419        }
420        
421        Collection<Menu> getMenus()
422        {
423            return _menus.values();
424        }
425    }
426    
427    class ParameterControl implements RibbonElement
428    {
429        private String _id;
430        public ParameterControl(String id)
431        {
432            _id = id;
433        }
434        
435        @Override
436        public String getId()
437        {
438            return _id;
439        }
440    }
441}