001/*
002 *  Copyright 2013 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.model;
017
018import java.io.File;
019import java.io.FileInputStream;
020import java.io.InputStream;
021import java.util.HashMap;
022import java.util.Map;
023
024import org.apache.avalon.framework.component.Component;
025import org.apache.avalon.framework.configuration.Configuration;
026import org.apache.avalon.framework.configuration.ConfigurationException;
027import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder;
028import org.apache.avalon.framework.logger.AbstractLogEnabled;
029import org.apache.avalon.framework.service.ServiceException;
030import org.apache.avalon.framework.service.ServiceManager;
031import org.apache.avalon.framework.service.Serviceable;
032import org.apache.avalon.framework.thread.ThreadSafe;
033import org.apache.excalibur.source.Source;
034import org.apache.excalibur.source.SourceResolver;
035import org.apache.excalibur.source.impl.FileSource;
036
037import org.ametys.runtime.i18n.I18nizableText;
038import org.ametys.skinfactory.SkinFactoryComponent;
039import org.ametys.skinfactory.parameters.I18nizableTextParameter;
040import org.ametys.skinfactory.parameters.ImageParameter;
041import org.ametys.skinfactory.parameters.AbstractSkinParameter;
042import org.ametys.web.skin.SkinModel;
043import org.ametys.web.skin.SkinModelsManager;
044
045/**
046 * Manages the design conceptions of a model
047 */
048public class ModelDesignsManager extends AbstractLogEnabled implements ThreadSafe, Serviceable, Component
049{
050    /** The avalon role name */
051    public static final String ROLE = ModelDesignsManager.class.getName();
052    
053    private SkinModelsManager _modelsManager;
054    private SkinFactoryComponent _skinFactoryManager;
055    private SourceResolver _resolver;
056    
057    private Map<String, Map<String, Design>> _designsCache = new HashMap<>();
058
059    @Override
060    public void service(ServiceManager smanager) throws ServiceException
061    {
062        _modelsManager = (SkinModelsManager) smanager.lookup(SkinModelsManager.ROLE);
063        _skinFactoryManager = (SkinFactoryComponent) smanager.lookup(SkinFactoryComponent.ROLE);
064        _resolver = (SourceResolver) smanager.lookup(SourceResolver.ROLE);
065    }
066    
067    /**
068     * Get all design instances for given model
069     * @param modelName The model name
070     * @return all design instances for given model
071     */
072    public Map<String, Design> getDesigns (String modelName)
073    {
074        if (!_designsCache.containsKey(modelName))
075        {
076            _configureDesigns (modelName);
077        }
078        return _designsCache.get(modelName);
079    }
080    
081    /**
082     * Get design instance of given id and model name
083     * @param modelName The model name
084     * @param id The id
085     * @return design instance
086     */
087    public Design getDesign (String modelName, String id)
088    {
089        if (!_designsCache.containsKey(modelName))
090        {
091            _configureDesigns (modelName);
092        }
093        return _designsCache.get(modelName).get(id);
094    }
095    
096    /**
097     * Apply a design
098     * @param modelName The model name
099     * @param id Id of design
100     * @param skinDir The skin directory (could be temp, work or skins)
101     */
102    public void applyDesign (String modelName, String id, File skinDir)
103    {
104        SkinModel model = _modelsManager.getModel(modelName);
105     
106        File file = new File(model.getFile(), "model/designs/" + id + ".xml");
107        if (file.exists())
108        {
109            // Apply color theme
110            String themeId = _getColorTheme (file);
111            if (themeId != null)
112            {
113                _skinFactoryManager.saveColorTheme(skinDir, themeId);
114            }
115            
116            // Apply values
117            Map<String, Object> values = _getParameterValues (modelName, file);
118            _skinFactoryManager.applyModelParameters(modelName, skinDir, values);
119        }
120    }
121    
122    
123    private void _configureDesigns (String modelName)
124    {
125        SkinModel model = _modelsManager.getModel(modelName);
126        
127        _designsCache.put(modelName, new HashMap<String, Design>());
128        Map<String, Design> designs = _designsCache.get(modelName);
129        
130        File designDir = new File(model.getFile(), "model/designs");
131        if (designDir.exists())
132        {
133            for (File child : designDir.listFiles())
134            {
135                if (child.getName().endsWith(".xml"))
136                {
137                    Design design = _configureDesign (modelName, child);
138                    if (design != null)
139                    {
140                        designs.put(design.getId(), design);
141                    }
142                }
143            }
144        }
145    }
146    
147    
148    private Design _configureDesign (String modelName, File configurationFile)
149    {
150        try (InputStream is = new FileInputStream(configurationFile))
151        {
152            String fileName = configurationFile.getName();
153            String id = fileName.substring(0, fileName.lastIndexOf("."));
154            
155            Configuration configuration = new DefaultConfigurationBuilder().build(is);
156            I18nizableText label = _configureI18nizableText(configuration.getChild("label", false), new I18nizableText(id), modelName);
157            I18nizableText description = _configureI18nizableText(configuration.getChild("description", false), new I18nizableText(id), modelName);
158            
159            String iconName = id + ".png";
160            String icon = "/plugins/skinfactory/resources/img/actions/designs_32.png";
161            File iconFile = new File(configurationFile.getParentFile(), iconName);
162            if (iconFile.exists())
163            {
164                icon = "/plugins/skinfactory/" + modelName + "/_thumbnail/32/32/model/designs/" + iconName;
165            }
166            
167            return new Design(id, label, description, icon);
168        }
169        catch (Exception e)
170        {
171            if (getLogger().isWarnEnabled())
172            {
173                getLogger().warn("Cannot read the configuration file model/designs/" + configurationFile.getName()  + " for the model '" + modelName + "'. Continue as if file was not existing", e);
174            }
175            return null;
176        }
177        
178    }
179    
180    private String _getColorTheme (File file)
181    {
182        Source src = null;
183        try
184        {
185            
186            src = new FileSource("file", file);
187            
188            Configuration configuration = new DefaultConfigurationBuilder(true).build(src.getInputStream());
189            return configuration.getChild("color-theme").getValue(null);
190        }
191        catch (Exception e)
192        {
193            getLogger().error("Unable to get color theme", e);
194            return null;
195        }
196        finally
197        {
198            if (src != null)
199            {
200                _resolver.release(src);
201            }
202        }
203    }
204    
205    private Map<String, Object> _getParameterValues (String modelName, File file)
206    {
207        Map<String, Object> values = new HashMap<>();
208        
209        Source src = null;
210        try
211        {
212            
213            src = new FileSource("file", file);
214            
215            Configuration configuration = new DefaultConfigurationBuilder(true).build(src.getInputStream());
216            Configuration[] parametersConf = configuration.getChild("parameters").getChildren("parameter");
217            
218            Map<String, AbstractSkinParameter> modelParameters = _skinFactoryManager.getModelParameters(modelName);
219            
220            for (Configuration paramConf : parametersConf)
221            {
222                String id = paramConf.getAttribute("id");
223                AbstractSkinParameter modelParam = modelParameters.get(id);
224                if (modelParam != null)
225                {
226                    if (modelParam instanceof I18nizableTextParameter)
227                    {
228                        Configuration[] children = paramConf.getChildren();
229                        Map<String, String> langValues = new HashMap<>();
230                        for (Configuration langConfig : children)
231                        {
232                            langValues.put(langConfig.getName(), langConfig.getValue(""));
233                        }
234                        values.put(id, langValues);
235                    }
236                    else if (modelParam instanceof ImageParameter)
237                    {
238                        values.put(id, new ImageParameter.FileValue(paramConf.getValue(""), false));
239                    }
240                    else
241                    {
242                        values.put(id, paramConf.getValue(""));
243                    }
244                }
245            }
246            
247            return values;
248        }
249        catch (Exception e)
250        {
251            getLogger().error("Unable to get values of all parameters", e);
252            return new HashMap<>();
253        }
254        finally
255        {
256            if (src != null)
257            {
258                _resolver.release(src);
259            }
260        }
261    }
262    
263    
264    private I18nizableText _configureI18nizableText(Configuration configuration, I18nizableText defaultValue, String modelName) throws ConfigurationException
265    {
266        if (configuration != null)
267        {
268            boolean i18nSupported = configuration.getAttributeAsBoolean("i18n", false);
269            if (i18nSupported)
270            {
271                String catalogue = configuration.getAttribute("catalogue", null);
272                if (catalogue == null)
273                {
274                    catalogue = "model." + modelName;
275                }
276
277                return new I18nizableText(catalogue, configuration.getValue());
278            }
279            else
280            {
281                return new I18nizableText(configuration.getValue(""));
282            }
283        }
284        else
285        {
286            return defaultValue;
287        }
288        
289    }
290    
291    /**
292     * Bean representing a model design
293     *
294     */
295    public class Design 
296    {
297        private String _id;
298        private I18nizableText _label;
299        private I18nizableText _description;
300        private String _icon;
301
302        /**
303         * Constructor
304         * @param id the theme id
305         * @param label the theme's label
306         * @param description the theme's description
307         * @param icon the icon
308         */
309        public Design (String id, I18nizableText label, I18nizableText description, String icon)
310        {
311            _id = id;
312            _label = label;
313            _description = description;
314            _icon = icon;
315        }
316        
317        /**
318         * Get the id
319         * @return the id
320         */
321        public String getId ()
322        {
323            return _id;
324        }
325        
326        /**
327         * Get the label
328         * @return the label
329         */
330        public I18nizableText getLabel ()
331        {
332            return _label;
333        }
334        
335        /**
336         * Get the description
337         * @return the description
338         */
339        public I18nizableText getDescription ()
340        {
341            return _description;
342        }
343        
344        /**
345         * Get the icon
346         * @return the icon
347         */
348        public String getIcon ()
349        {
350            return _icon;
351        }
352    }        
353
354}