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.cocoon;
017
018import java.io.IOException;
019import java.util.Map;
020import java.util.MissingResourceException;
021
022import org.apache.avalon.framework.component.ComponentException;
023import org.apache.avalon.framework.configuration.Configuration;
024import org.apache.avalon.framework.configuration.ConfigurationException;
025import org.apache.avalon.framework.configuration.DefaultConfiguration;
026import org.apache.avalon.framework.parameters.Parameters;
027import org.apache.avalon.framework.service.ServiceException;
028import org.apache.avalon.framework.service.ServiceManager;
029import org.apache.cocoon.ProcessingException;
030import org.apache.cocoon.environment.ObjectModelHelper;
031import org.apache.cocoon.environment.Request;
032import org.apache.cocoon.environment.SourceResolver;
033import org.apache.cocoon.xml.ParamSaxBuffer;
034import org.apache.commons.lang.StringUtils;
035import org.xml.sax.SAXException;
036
037import org.ametys.core.cocoon.XMLResourceBundle;
038import org.ametys.web.skin.Skin;
039import org.ametys.web.skin.SkinModelsManager;
040import org.ametys.web.skin.SkinsManager;
041
042/**
043 * CMS version of the I18nTransformer, dealing with skin catalogues.<br>
044 * It is also able to reconfigure itself dynamically, to reflect changes.
045 */
046public class I18nTransformer extends org.ametys.core.cocoon.I18nTransformer
047{
048    // The last time, "needsReload" was called
049    private static long _needsReloadTime;
050
051    /** the avalon service manager */
052    protected ServiceManager _manager;
053    
054    private Configuration _conf;
055    
056    /** The time the configure method was lastly called on this instance */
057    private long _lastConfigureTime;
058
059    private SkinsManager _skinsManager;
060    private SkinModelsManager _skinModelsManager;
061    
062    @Override
063    public void configure(Configuration conf) throws ConfigurationException
064    {
065        _conf = conf;
066        _configure();
067    }
068    
069    @Override
070    public void service(ServiceManager smanager) throws ServiceException
071    {
072        super.service(smanager);
073        _manager = smanager;
074        _skinsManager = (SkinsManager) smanager.lookup(SkinsManager.ROLE);
075        _skinModelsManager = (SkinModelsManager) smanager.lookup(SkinModelsManager.ROLE);
076    }
077    
078    private void _configure() throws ConfigurationException
079    {
080        _lastConfigureTime = System.currentTimeMillis();
081        
082        DefaultConfiguration newConf = new DefaultConfiguration("i18n");
083
084        Configuration cataloguesConf = _conf.getChild("catalogues", false);
085        DefaultConfiguration catalogues = new DefaultConfiguration("catalogues");
086        newConf.addChild(catalogues);
087
088        if (cataloguesConf != null)
089        {
090            catalogues.addAll(cataloguesConf);
091        }
092
093        _configureSkinsCatalogue(catalogues);
094        _configureModelsCatalogue(catalogues);
095        
096        super.configure(newConf);
097    }
098
099    private void _configureSkinsCatalogue(DefaultConfiguration catalogues)
100    {
101        for (String skinId : _skinsManager.getSkins())
102        {
103            String name = skinId;
104            String id = "skin." + name;
105
106            DefaultConfiguration catalogue = new DefaultConfiguration("catalogue");
107            catalogue.setAttribute("id", id);
108            catalogue.setAttribute("name", "messages");
109            
110            Skin skin = _skinsManager.getSkin(skinId);
111            for (Skin skinOrParentSkin : _skinsManager.getSkinAndParents(skin))
112            {
113                DefaultConfiguration location = new DefaultConfiguration("location");
114                location.setValue("skin-raw:" + skinOrParentSkin.getId() + "://i18n");
115                catalogue.addChild(location);
116            }
117
118            catalogues.addChild(catalogue);
119        }
120    }
121    
122    
123    private void _configureModelsCatalogue(DefaultConfiguration catalogues)
124    {
125        for (String modelId : _skinModelsManager.getModels())
126        {
127            String name = modelId;
128            String id = "model." + name;
129
130            DefaultConfiguration catalogue = new DefaultConfiguration("catalogue");
131            catalogue.setAttribute("id", id);
132            catalogue.setAttribute("name", "messages");
133            catalogue.setAttribute("location", "model:" + modelId + "://i18n");
134
135            catalogues.addChild(catalogue);
136        }
137    }
138
139    @Override
140    protected ParamSaxBuffer getMessage(String catalogueID, String key)
141    {
142        Request request = ObjectModelHelper.getRequest(objectModel);
143        
144        String skinLocation = (String) request.getAttribute("skin-location");
145        if (skinLocation != null && catalogueID != null && catalogueID.startsWith("skin"))
146        {
147            ParamSaxBuffer message = null;
148            
149            String skinName = _skinsManager.getSkinNameFromRequest(request);
150            
151            if (StringUtils.isNotEmpty(skinName))
152            {
153                message = _getExternalMessage("ametys-home://" + skinLocation + "/" + skinName + "/i18n", key);
154            }
155            
156            if (message == null)
157            {
158                message = getUntranslatedMessage(catalogueID, key);
159            }
160            
161            return message;
162        }
163        else
164        {
165            return super.getMessage(catalogueID, key);
166        }
167    }
168    
169    private ParamSaxBuffer _getExternalMessage (String externalLocation, String key)
170    {
171        try
172        {
173            XMLResourceBundle bundle = (XMLResourceBundle) factory.select(externalLocation, "messages", locale);
174            if (bundle == null) 
175            {
176                // Can't translate
177                if (getLogger().isDebugEnabled())
178                {
179                    getLogger().debug("No bundle found for catalogue location '" + externalLocation + "'");
180                }
181                return null;
182            }
183            
184            try 
185            {
186                return (ParamSaxBuffer) bundle.getObject(key);
187            } 
188            catch (MissingResourceException e)  
189            {
190                if (getLogger().isDebugEnabled())
191                {
192                    getLogger().debug("Untranslated key: '" + key + "'");
193                }
194            }
195        }
196        catch (ComponentException e)
197        {
198            if (getLogger().isDebugEnabled())
199            {
200                getLogger().debug("Untranslated key: '" + key + "'");
201            }
202        }
203        
204        return null;
205        
206    }
207
208    /**
209     * Call this method when you change the list of i18n catalogues <br> by adding a new skin for example.
210     */
211    public static void needsReload()
212    {
213        _needsReloadTime = System.currentTimeMillis();
214    }
215    
216    @Override
217    public void setup(SourceResolver resolver, Map objModel, String source, Parameters parameters) throws ProcessingException, SAXException, IOException
218    {
219        try
220        {
221            if (_needsReloadTime > _lastConfigureTime)
222            {
223                _configure();
224            }
225        }
226        catch (ConfigurationException e)
227        {
228            String errorMessage = "Error with i18n on work dir";
229            getLogger().error(errorMessage, e);
230            throw new SAXException(errorMessage, e);
231        }
232        
233        super.setup(resolver, objModel, source, parameters);
234    }
235}