001/*
002 *  Copyright 2012 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.cocoon;
017
018import java.io.IOException;
019import java.util.Map;
020
021import org.apache.avalon.framework.configuration.Configuration;
022import org.apache.avalon.framework.configuration.ConfigurationException;
023import org.apache.avalon.framework.configuration.DefaultConfiguration;
024import org.apache.avalon.framework.context.ContextException;
025import org.apache.avalon.framework.context.Contextualizable;
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.Constants;
030import org.apache.cocoon.ProcessingException;
031import org.apache.cocoon.environment.Context;
032import org.apache.cocoon.xml.ParamSaxBuffer;
033import org.apache.excalibur.source.SourceResolver;
034import org.xml.sax.SAXException;
035
036import org.ametys.core.util.I18nUtils;
037import org.ametys.runtime.plugin.PluginsManager;
038import org.ametys.runtime.workspace.WorkspaceManager;
039
040
041/**
042 * This class extends the classic I18nTransormer by automatically filling it with plugins catalogues.
043 * It also handles special sitemap parameters :<br>
044 * <ul>
045 * <li><code>plugin</code> : when specified and when the given plugin exists, the default catalogue id is set to plugin.<code>plugin</code>
046 * <li><code>workspace</code> : when specified and when the given workspace exists, the default catalogue id is set to workspace.<code>workspace</code>
047 * </ul>
048 * If the specified pugin or workspace does not exist, the specified default catalogue id is used, if any.
049 */
050public class I18nTransformer extends org.apache.cocoon.transformation.I18nTransformer implements Contextualizable
051{
052    /** Cocoon context */
053    protected Context _context;
054
055    /** Source Resolver */
056    protected SourceResolver _resolver;
057
058    /** The i18n utils */
059    protected I18nUtils _i18nUtils;
060    
061    public void contextualize(org.apache.avalon.framework.context.Context context) throws ContextException
062    {
063        _context = (Context) context.get(Constants.CONTEXT_ENVIRONMENT_CONTEXT);
064    }
065    
066    @Override
067    public void service(ServiceManager smanager) throws ServiceException
068    {
069        super.service(smanager);
070        _resolver = (SourceResolver) smanager.lookup(SourceResolver.ROLE);
071        _i18nUtils = (I18nUtils) smanager.lookup(I18nUtils.ROLE);
072    }
073    
074    @Override
075    public void configure(Configuration conf) throws ConfigurationException
076    {
077        // Add plugins catalogues to the configuration
078        DefaultConfiguration newConf = new DefaultConfiguration("i18n");
079        newConf.addChild(conf.getChild("untranslated-text"));
080        newConf.addChild(conf.getChild("cache-at-startup"));
081
082        Configuration cataloguesConf = conf.getChild("catalogues", false);
083        DefaultConfiguration catalogues = new DefaultConfiguration("catalogues");
084        newConf.addChild(catalogues);
085
086        if (cataloguesConf != null)
087        {
088            catalogues.addAll(cataloguesConf);
089        }
090
091        _configurePlugins(catalogues);
092        _configureWorkspaces(catalogues);
093        _configureParams(catalogues);
094
095        // Load the configuration
096        super.configure(newConf);
097    }
098    
099    /**
100     * Search for all i18n folders in  WEB-INF/param/*
101     * @param catalogues catalog to use
102     */
103    private void _configureParams(DefaultConfiguration catalogues)
104    {
105        for (String name : _i18nUtils.getParamsFoldersWithI18n())
106        {
107            String id = "param." + name;
108            
109            DefaultConfiguration catalogue = new DefaultConfiguration("catalogue");
110            catalogue.setAttribute("id", id);
111            catalogue.setAttribute("name", "messages");
112            
113            DefaultConfiguration location = new DefaultConfiguration("location");
114            location.setValue("context://WEB-INF/param/" + name + "/i18n");
115            catalogue.addChild(location);
116
117            catalogues.addChild(catalogue);
118        }
119    }
120    
121    private void _configurePlugins(DefaultConfiguration catalogues)
122    {
123        PluginsManager pm = PluginsManager.getInstance();
124        
125        for (String pluginName : pm.getPluginNames())
126        {
127            String id = "plugin." + pluginName;
128            
129            DefaultConfiguration catalogue = new DefaultConfiguration("catalogue");
130            catalogue.setAttribute("id", id);
131            catalogue.setAttribute("name", "messages");
132            
133            DefaultConfiguration location1 = new DefaultConfiguration("location");
134            location1.setValue("context://WEB-INF/i18n/plugins/" + pluginName);
135            catalogue.addChild(location1);
136            
137            DefaultConfiguration location2 = new DefaultConfiguration("location");
138            location2.setValue("plugin:" + pluginName + "://i18n");
139            catalogue.addChild(location2);
140
141            catalogues.addChild(catalogue);
142        }
143    }
144    
145    private void _configureWorkspaces(DefaultConfiguration catalogues)
146    {
147        WorkspaceManager wm = WorkspaceManager.getInstance();
148        
149        for (String workspace : wm.getWorkspaceNames())
150        {
151            String id = "workspace." + workspace;
152
153            DefaultConfiguration catalogue = new DefaultConfiguration("catalogue");
154            catalogue.setAttribute("id", id);
155            catalogue.setAttribute("name", "messages");
156
157            DefaultConfiguration location1 = new DefaultConfiguration("location");
158            location1.setValue("context://WEB-INF/i18n/workspaces/" + workspace);
159            catalogue.addChild(location1);
160            
161            DefaultConfiguration location2 = new DefaultConfiguration("location");
162            location2.setValue("workspace:" + workspace + "://i18n");
163            catalogue.addChild(location2);
164           
165            catalogues.addChild(catalogue);
166        }
167    }
168    
169    @Override
170    public void setup(org.apache.cocoon.environment.SourceResolver resolver, Map objModel, String source, Parameters parameters) throws ProcessingException, SAXException, IOException
171    {
172        Parameters newParam = new Parameters();
173        
174        // Copie de tous les paramètres qui ne sont pas directement utilisés ici
175        String[] names = parameters.getNames();
176
177        for (int i = 0; i < names.length; i++)
178        {
179            String name = names[i];
180            
181            if (!"plugin".equals(name) && !"workspace".equals(name) && !I18N_DEFAULT_CATALOGUE_ID.equals(name))
182            {
183                String value = parameters.getParameter(name, null);
184                newParam.setParameter(name, value);
185            }
186        }
187        
188        String defaultCatalogueId = parameters.getParameter(I18N_DEFAULT_CATALOGUE_ID, null);
189        String pluginName = parameters.getParameter("plugin", null);
190        String workspaceName = parameters.getParameter("workspace", null);
191
192        boolean useDefault = true;
193        
194        if (pluginName != null)
195        {
196            if (PluginsManager.getInstance().getPluginNames().contains(pluginName))
197            {
198                newParam.setParameter(I18N_DEFAULT_CATALOGUE_ID, "plugin." + pluginName);
199                useDefault = false;
200            }
201        }
202        else if (workspaceName != null)
203        {
204            if (WorkspaceManager.getInstance().getWorkspaceNames().contains(workspaceName))
205            {
206                newParam.setParameter(I18N_DEFAULT_CATALOGUE_ID, "workspace." + workspaceName);
207                useDefault = false;
208            }
209        }
210        
211        if (useDefault)
212        {
213            newParam.setParameter(I18N_DEFAULT_CATALOGUE_ID, defaultCatalogueId);
214        }
215        
216        super.setup(resolver, objModel, source, newParam);
217    }
218    
219    @Override
220    protected ParamSaxBuffer getMessage(String catalogueID, String key)
221    {
222        ParamSaxBuffer message = super.getMessage(catalogueID, key);
223        if (message == null)
224        {
225            return getUntranslatedMessage (catalogueID, key);
226        }
227
228        return message;
229    }
230    
231    /**
232     * Retrieve the message when the key is not found
233     *
234     * @param catalogueID The catalogue id
235     * @param key The i18n key
236     * @return SaxBuffer containing the message for untranslated key
237     */
238    protected ParamSaxBuffer getUntranslatedMessage(String catalogueID, String key)
239    {
240        if (getLogger().isWarnEnabled())
241        {
242            getLogger().warn("Translation not found for key " + key + " in catalogue " + catalogueID);
243        }
244        
245        try
246        {
247            String value = catalogueID + ':' + key;
248            ParamSaxBuffer paramSaxBuffer = new ParamSaxBuffer();
249            paramSaxBuffer.characters(value.toCharArray(), 0, value.length());
250
251            return paramSaxBuffer;
252        }
253        catch (SAXException e)
254        {
255            throw new RuntimeException(e);
256        }
257    }
258}