001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.xsl;
016    
017    import com.liferay.portal.kernel.io.unsync.UnsyncStringWriter;
018    import com.liferay.portal.kernel.template.StringTemplateResource;
019    import com.liferay.portal.kernel.template.Template;
020    import com.liferay.portal.kernel.template.TemplateConstants;
021    import com.liferay.portal.kernel.template.TemplateException;
022    import com.liferay.portal.kernel.template.TemplateResource;
023    import com.liferay.portal.kernel.util.LocaleUtil;
024    import com.liferay.portal.kernel.util.StringBundler;
025    import com.liferay.portal.template.TemplateContextHelper;
026    import com.liferay.portal.util.PropsValues;
027    
028    import java.io.Writer;
029    
030    import java.security.AccessController;
031    import java.security.PrivilegedActionException;
032    import java.security.PrivilegedExceptionAction;
033    
034    import java.util.HashMap;
035    import java.util.Locale;
036    import java.util.Map;
037    import java.util.Set;
038    
039    import javax.servlet.http.HttpServletRequest;
040    
041    import javax.xml.XMLConstants;
042    import javax.xml.transform.Transformer;
043    import javax.xml.transform.TransformerConfigurationException;
044    import javax.xml.transform.TransformerFactory;
045    import javax.xml.transform.stream.StreamResult;
046    import javax.xml.transform.stream.StreamSource;
047    
048    /**
049     * @author Tina Tian
050     */
051    public class XSLTemplate implements Template {
052    
053            public XSLTemplate(
054                    XSLTemplateResource xslTemplateResource,
055                    TemplateResource errorTemplateResource,
056                    TemplateContextHelper templateContextHelper) {
057    
058                    if (xslTemplateResource == null) {
059                            throw new IllegalArgumentException("XSL template resource is null");
060                    }
061    
062                    if (templateContextHelper == null) {
063                            throw new IllegalArgumentException(
064                                    "Template context helper is null");
065                    }
066    
067                    _xslTemplateResource = xslTemplateResource;
068                    _errorTemplateResource = errorTemplateResource;
069                    _templateContextHelper = templateContextHelper;
070    
071                    _context = new HashMap<String, Object>();
072            }
073    
074            @Override
075            public Object get(String key) {
076                    return _context.get(key);
077            }
078    
079            @Override
080            public String[] getKeys() {
081                    Set<String> keys = _context.keySet();
082    
083                    return keys.toArray(new String[keys.size()]);
084            }
085    
086            @Override
087            public void prepare(HttpServletRequest request) {
088                    _templateContextHelper.prepare(this, request);
089            }
090    
091            @Override
092            public void processTemplate(Writer writer) throws TemplateException {
093                    TransformerFactory transformerFactory =
094                            TransformerFactory.newInstance();
095    
096                    try {
097                            transformerFactory.setFeature(
098                                    XMLConstants.FEATURE_SECURE_PROCESSING,
099                                    PropsValues.XSL_TEMPLATE_SECURE_PROCESSING_ENABLED);
100                    }
101                    catch (TransformerConfigurationException tce) {
102                    }
103    
104                    String languageId = null;
105    
106                    XSLURIResolver xslURIResolver =
107                            _xslTemplateResource.getXSLURIResolver();
108    
109                    if (xslURIResolver != null) {
110                            languageId = xslURIResolver.getLanguageId();
111                    }
112    
113                    Locale locale = LocaleUtil.fromLanguageId(languageId);
114    
115                    XSLErrorListener xslErrorListener = new XSLErrorListener(locale);
116    
117                    transformerFactory.setErrorListener(xslErrorListener);
118    
119                    transformerFactory.setURIResolver(xslURIResolver);
120    
121                    StreamSource xmlSource = new StreamSource(
122                            _xslTemplateResource.getXMLReader());
123    
124                    Transformer transformer = null;
125    
126                    if (_errorTemplateResource == null) {
127                            try {
128                                    transformer = _getTransformer(
129                                            transformerFactory, _xslTemplateResource);
130    
131                                    transformer.transform(xmlSource, new StreamResult(writer));
132    
133                                    return;
134                            }
135                            catch (Exception e) {
136                                    throw new TemplateException(
137                                            "Unable to process XSL template " +
138                                                    _xslTemplateResource.getTemplateId(),
139                                            e);
140                            }
141                    }
142    
143                    try {
144                            UnsyncStringWriter unsyncStringWriter = new UnsyncStringWriter();
145    
146                            transformer = _getTransformer(
147                                    transformerFactory, _xslTemplateResource);
148    
149                            transformer.setParameter(
150                                    TemplateConstants.WRITER, unsyncStringWriter);
151    
152                            transformer.transform(
153                                    xmlSource, new StreamResult(unsyncStringWriter));
154    
155                            StringBundler sb = unsyncStringWriter.getStringBundler();
156    
157                            sb.writeTo(writer);
158                    }
159                    catch (Exception e1) {
160                            Transformer errorTransformer = _getTransformer(
161                                    transformerFactory, _errorTemplateResource);
162    
163                            errorTransformer.setParameter(TemplateConstants.WRITER, writer);
164                            errorTransformer.setParameter(
165                                    "exception", xslErrorListener.getMessageAndLocation());
166    
167                            if (_errorTemplateResource instanceof StringTemplateResource) {
168                                    StringTemplateResource stringTemplateResource =
169                                            (StringTemplateResource)_errorTemplateResource;
170    
171                                    errorTransformer.setParameter(
172                                            "script", stringTemplateResource.getContent());
173                            }
174    
175                            if (xslErrorListener.getLocation() != null) {
176                                    errorTransformer.setParameter(
177                                            "column", new Integer(xslErrorListener.getColumnNumber()));
178                                    errorTransformer.setParameter(
179                                            "line", new Integer(xslErrorListener.getLineNumber()));
180                            }
181    
182                            try {
183                                    errorTransformer.transform(xmlSource, new StreamResult(writer));
184                            }
185                            catch (Exception e2) {
186                                    throw new TemplateException(
187                                            "Unable to process XSL template " +
188                                                    _errorTemplateResource.getTemplateId(),
189                                            e2);
190                            }
191                    }
192            }
193    
194            @Override
195            public void put(String key, Object value) {
196                    if (value == null) {
197                            return;
198                    }
199    
200                    _context.put(key, value);
201            }
202    
203            private Transformer _getTransformer(
204                            TransformerFactory transformerFactory,
205                            TemplateResource templateResource)
206                    throws TemplateException {
207    
208                    try {
209                            StreamSource scriptSource = new StreamSource(
210                                    templateResource.getReader());
211    
212                            Transformer transformer = AccessController.doPrivileged(
213                                    new TransformerPrivilegedExceptionAction(
214                                            transformerFactory, scriptSource));
215    
216                            for (Map.Entry<String, Object> entry : _context.entrySet()) {
217                                    transformer.setParameter(entry.getKey(), entry.getValue());
218                            }
219    
220                            return transformer;
221                    }
222                    catch (PrivilegedActionException pae) {
223                            throw new TemplateException(
224                                    "Unable to get Transformer for template " +
225                                            templateResource.getTemplateId(),
226                                    pae.getException());
227                    }
228                    catch (Exception e) {
229                            throw new TemplateException(
230                                    "Unable to get Transformer for template " +
231                                            templateResource.getTemplateId(),
232                                    e);
233                    }
234            }
235    
236            private Map<String, Object> _context;
237            private TemplateResource _errorTemplateResource;
238            private TemplateContextHelper _templateContextHelper;
239            private XSLTemplateResource _xslTemplateResource;
240    
241            private class TransformerPrivilegedExceptionAction
242                    implements PrivilegedExceptionAction<Transformer> {
243    
244                    public TransformerPrivilegedExceptionAction(
245                            TransformerFactory transformerFactory, StreamSource scriptSource) {
246    
247                            _transformerFactory = transformerFactory;
248                            _scriptSource = scriptSource;
249                    }
250    
251                    @Override
252                    public Transformer run() throws Exception {
253                            return _transformerFactory.newTransformer(_scriptSource);
254                    }
255    
256                    private StreamSource _scriptSource;
257                    private TransformerFactory _transformerFactory;
258    
259            }
260    
261    }