001/*
002 *  Copyright 2010 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.web.skin;
017
018import java.io.File;
019import java.io.FileInputStream;
020import java.io.InputStream;
021import java.util.Date;
022import java.util.HashMap;
023import java.util.HashSet;
024import java.util.Map;
025import java.util.Set;
026
027import org.apache.avalon.framework.configuration.Configuration;
028import org.apache.avalon.framework.configuration.ConfigurationException;
029import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder;
030import org.slf4j.Logger;
031import org.slf4j.LoggerFactory;
032
033import org.ametys.runtime.i18n.I18nizableText;
034
035/**
036 * A skin
037 */
038public class Skin
039{
040    private static Logger _logger = LoggerFactory.getLogger(Skin.class);
041    
042    /** The skin id (e.g. the directory name) */
043    protected String _id;
044    /** The skin directory */
045    protected File _file;
046    /** The skin name */
047    protected I18nizableText _label;
048    /** The skin description */
049    protected I18nizableText _description;
050    /** The skin thumbnail 16x16 */
051    protected String _smallImage;
052    /** The skin thumbnail 32x32 */
053    protected String _mediumImage;
054    /** The skin thumbnail 48x48 */
055    protected String _largeImage;
056    /** The map of templates id and associated templates */
057    protected Map<String, SkinTemplate> _templates = new HashMap<>();
058    
059    /** The last time the file was loaded */
060    protected long _lastConfUpdate; 
061    
062    /**
063     * Creates a skin
064     * @param id The id of the skin (e.g. the directory name)
065     * @param file The skin file
066     */
067    public Skin(String id, File file)
068    {
069        this._id = id;
070        this._file = file;
071    }
072    
073    /**
074     * Get the list of existing templates
075     * @return A set of skin names. Can be null if there is an error.
076     */
077    public Set<String> getTemplates()
078    {
079        try
080        {
081            Set<String> template = new HashSet<>();
082
083            File tplDir = new File (_file, "/templates");
084            
085            for (File child : tplDir.listFiles())
086            {
087                if (_templateExists(child.getName()))
088                {
089                    template.add(child.getName());
090                }
091            }
092            
093            return template;
094        }
095        catch (Exception e)
096        {
097            _logger.error("Can not determine the list of skins available", e);
098            return null;
099        }
100    }
101    
102    /**
103     * Get a template
104     * @param id The id of the template
105     * @return The template or null if the template doesn't exists
106     */
107    @SuppressWarnings("null")
108    public SkinTemplate getTemplate(String id)
109    {
110        try
111        {
112            boolean templateDirExists = _templateExists(id);
113            SkinTemplate template = _templates.get(id);
114            if (template == null && templateDirExists)
115            {
116                template = new SkinTemplate(this._id, id, new File (_file, "/templates/" + id));
117                _templates.put(id, template);
118            }
119            else if (!templateDirExists)
120            {
121                _templates.put(id, null);
122                return null;
123            }
124            
125            template.refreshValues();
126            return template;
127        }
128        catch (Exception e)
129        {
130            throw new IllegalStateException("Can not create the template DAO for skin '" + this._id + "' and the template '" + id + "'", e);
131        }
132    }
133    
134    private boolean _templateExists(String id)
135    {
136        File tplFile = new File (_file, "/templates/" + id + "/stylesheets/template.xsl");
137        return tplFile.exists();
138    }
139    
140    /**
141     * The configuration default values (if configuration file does not exist or is unreadable)
142     */
143    protected void _defaultValues()
144    {
145        _lastConfUpdate = new Date().getTime();
146        
147        this._label = new I18nizableText(this._id);
148        this._description = new I18nizableText("");
149        this._smallImage = "/plugins/web/resources/img/skin/skin_16.png";
150        this._mediumImage = "/plugins/web/resources/img/skin/skin_32.png";
151        this._largeImage = "/plugins/web/resources/img/skin/skin_48.png";
152    }
153    
154    /**
155     * Refresh the conf values
156     */
157    public void refreshValues()
158    {
159        File configurationFile = null;
160
161        try
162        {
163            configurationFile = new File (_file, "/conf/skin.xml");
164            if (configurationFile.exists())
165            {
166                
167                if (_lastConfUpdate < configurationFile.lastModified())
168                {
169                    _lastConfUpdate = configurationFile.lastModified();
170                    try (InputStream is = new FileInputStream(configurationFile))
171                    {
172                        Configuration configuration = new DefaultConfigurationBuilder().build(is);
173                        
174                        this._label = _configureI18n(configuration.getChild("label", false), new I18nizableText(this._id));
175                        this._description = _configureI18n(configuration.getChild("description", false), new I18nizableText(""));
176                        this._smallImage = _configureThumbnail(configuration.getChild("thumbnail").getChild("small").getValue(null), "/plugins/web/resources/img/skin/skin_16.png");
177                        this._mediumImage = _configureThumbnail(configuration.getChild("thumbnail").getChild("medium").getValue(null), "/plugins/web/resources/img/skin/skin_32.png");
178                        this._largeImage = _configureThumbnail(configuration.getChild("thumbnail").getChild("large").getValue(null), "/plugins/web/resources/img/skin/skin_48.png");
179                    }
180                }
181            }
182            else
183            {
184                _defaultValues();
185            }
186        }
187        catch (Exception e)
188        {
189            _defaultValues();
190            if (_logger.isWarnEnabled())
191            {
192                _logger.warn("Cannot read the configuration file conf/skin.xml for the skin '" + this._id + "'. Continue as if file was not existing", e);
193            }
194        }
195    }
196    
197    private String _configureThumbnail(String value, String defaultImage)
198    {
199        if (value == null)
200        {
201            return defaultImage;
202        }
203        else
204        {
205            return "/skins/" + this._id + "/resources/" + value;
206        }
207    }
208
209    private I18nizableText _configureI18n(Configuration child, I18nizableText defaultValue) throws ConfigurationException
210    {
211        if (child != null)
212        {
213            String value = child.getValue();
214            if (child.getAttributeAsBoolean("i18n", false))
215            {
216                return new I18nizableText("skin." + this._id, value);
217            }
218            else
219            {
220                return new I18nizableText(value);
221            }
222        }
223        else
224        {
225            return defaultValue;
226        }
227    }
228
229    /**
230     * The skin id
231     * @return the id
232     */
233    public String getId()
234    {
235        return _id;
236    }
237    
238    /**
239     * The skin label
240     * @return The label
241     */
242    public I18nizableText getLabel()
243    {
244        return _label;
245    }
246    /**
247     * The skin description
248     * @return The description. Can not be null but can be empty
249     */
250    public I18nizableText getDescription()
251    {
252        return _description;
253    }
254    
255    /**
256     * The small image file uri
257     * @return The small image file uri
258     */
259    public String getSmallImage()
260    {
261        return _smallImage;
262    }
263
264    /**
265     * The medium image file uri
266     * @return The medium image file uri
267     */
268    public String getMediumImage()
269    {
270        return _mediumImage;
271    }
272    
273    /**
274     * The large image file uri
275     * @return The large image file uri
276     */
277    public String getLargeImage()
278    {
279        return _largeImage;
280    }
281
282    /**
283     * Get the skin's file directory
284     * @return the skin's file directory
285     */
286    public File getFile ()
287    {
288        return _file;
289    }
290    
291    /**
292     * Get the absolute path of the skin's file directory
293     * @return the absolute path of the skin's file directory
294     */
295    public String getLocation ()
296    {
297        return _file.getAbsolutePath();
298    }
299}