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.TemplateConstants;
020    import com.liferay.portal.kernel.template.TemplateException;
021    import com.liferay.portal.kernel.template.TemplateResource;
022    import com.liferay.portal.kernel.util.LocaleUtil;
023    import com.liferay.portal.kernel.util.StringBundler;
024    import com.liferay.portal.template.AbstractProcessingTemplate;
025    import com.liferay.portal.template.TemplateContextHelper;
026    
027    import java.io.Writer;
028    
029    import java.security.AccessController;
030    import java.security.PrivilegedActionException;
031    import java.security.PrivilegedExceptionAction;
032    
033    import java.util.HashMap;
034    import java.util.Locale;
035    import java.util.Map;
036    import java.util.Set;
037    
038    import javax.servlet.http.HttpServletRequest;
039    
040    import javax.xml.transform.Transformer;
041    import javax.xml.transform.TransformerFactory;
042    import javax.xml.transform.stream.StreamResult;
043    import javax.xml.transform.stream.StreamSource;
044    
045    /**
046     * @author Tina Tian
047     */
048    public class XSLTemplate extends AbstractProcessingTemplate {
049    
050            public XSLTemplate(
051                    XSLTemplateResource xslTemplateResource,
052                    TemplateResource errorTemplateResource, Map<String, Object> context,
053                    TemplateContextHelper templateContextHelper) {
054    
055                    if (xslTemplateResource == null) {
056                            throw new IllegalArgumentException("XSL template resource is null");
057                    }
058    
059                    if (templateContextHelper == null) {
060                            throw new IllegalArgumentException(
061                                    "Template context helper is null");
062                    }
063    
064                    _xslTemplateResource = xslTemplateResource;
065                    _errorTemplateResource = errorTemplateResource;
066                    _templateContextHelper = templateContextHelper;
067    
068                    _context = new HashMap<String, Object>();
069    
070                    if (context != null) {
071                            for (Map.Entry<String, Object> entry : context.entrySet()) {
072                                    put(entry.getKey(), entry.getValue());
073                            }
074                    }
075            }
076    
077            public Object get(String key) {
078                    return _context.get(key);
079            }
080    
081            public String[] getKeys() {
082                    Set<String> keys = _context.keySet();
083    
084                    return keys.toArray(new String[keys.size()]);
085            }
086    
087            @Override
088            public TemplateContextHelper getTemplateContextHelper() {
089                    return _templateContextHelper;
090            }
091    
092            public void prepare(HttpServletRequest request) {
093                    _templateContextHelper.prepare(this, request);
094            }
095    
096            public void put(String key, Object value) {
097                    if (value == null) {
098                            return;
099                    }
100    
101                    _context.put(key, value);
102            }
103    
104            @Override
105            protected void doProcessTemplate(Writer writer) throws TemplateException {
106                    TransformerFactory transformerFactory =
107                            TransformerFactory.newInstance();
108    
109                    String languageId = null;
110    
111                    XSLURIResolver xslURIResolver =
112                            _xslTemplateResource.getXSLURIResolver();
113    
114                    if (xslURIResolver != null) {
115                            languageId = xslURIResolver.getLanguageId();
116                    }
117    
118                    Locale locale = LocaleUtil.fromLanguageId(languageId);
119    
120                    XSLErrorListener xslErrorListener = new XSLErrorListener(locale);
121    
122                    transformerFactory.setErrorListener(xslErrorListener);
123    
124                    transformerFactory.setURIResolver(xslURIResolver);
125    
126                    StreamSource xmlSource = new StreamSource(
127                            _xslTemplateResource.getXMLReader());
128    
129                    Transformer transformer = null;
130    
131                    if (_errorTemplateResource == null) {
132                            try {
133                                    transformer = _getTransformer(
134                                            transformerFactory, _xslTemplateResource);
135    
136                                    transformer.transform(xmlSource, new StreamResult(writer));
137    
138                                    return;
139                            }
140                            catch (Exception e) {
141                                    throw new TemplateException(
142                                            "Unable to process XSL template " +
143                                                    _xslTemplateResource.getTemplateId(),
144                                            e);
145                            }
146                    }
147    
148                    try {
149                            UnsyncStringWriter unsyncStringWriter = new UnsyncStringWriter();
150    
151                            transformer = _getTransformer(
152                                    transformerFactory, _xslTemplateResource);
153    
154                            transformer.setParameter(
155                                    TemplateConstants.WRITER, unsyncStringWriter);
156    
157                            transformer.transform(
158                                    xmlSource, new StreamResult(unsyncStringWriter));
159    
160                            StringBundler sb = unsyncStringWriter.getStringBundler();
161    
162                            sb.writeTo(writer);
163                    }
164                    catch (Exception e1) {
165                            Transformer errorTransformer = _getTransformer(
166                                    transformerFactory, _errorTemplateResource);
167    
168                            errorTransformer.setParameter(TemplateConstants.WRITER, writer);
169                            errorTransformer.setParameter(
170                                    "exception", xslErrorListener.getMessageAndLocation());
171    
172                            if (_errorTemplateResource instanceof StringTemplateResource) {
173                                    StringTemplateResource stringTemplateResource =
174                                            (StringTemplateResource)_errorTemplateResource;
175    
176                                    errorTransformer.setParameter(
177                                            "script", stringTemplateResource.getContent());
178                            }
179    
180                            if (xslErrorListener.getLocation() != null) {
181                                    errorTransformer.setParameter(
182                                            "column", new Integer(xslErrorListener.getColumnNumber()));
183                                    errorTransformer.setParameter(
184                                            "line", new Integer(xslErrorListener.getLineNumber()));
185                            }
186    
187                            try {
188                                    errorTransformer.transform(xmlSource, new StreamResult(writer));
189                            }
190                            catch (Exception e2) {
191                                    throw new TemplateException(
192                                            "Unable to process XSL template " +
193                                                    _errorTemplateResource.getTemplateId(),
194                                            e2);
195                            }
196                    }
197            }
198    
199            private Transformer _getTransformer(
200                            TransformerFactory transformerFactory,
201                            TemplateResource templateResource)
202                    throws TemplateException {
203    
204                    try {
205                            StreamSource scriptSource = new StreamSource(
206                                    templateResource.getReader());
207    
208                            Transformer transformer = AccessController.doPrivileged(
209                                    new TransformerPrivilegedExceptionAction(
210                                            transformerFactory, scriptSource));
211    
212                            for (Map.Entry<String, Object> entry : _context.entrySet()) {
213                                    transformer.setParameter(entry.getKey(), entry.getValue());
214                            }
215    
216                            return transformer;
217                    }
218                    catch (PrivilegedActionException pae) {
219                            throw new TemplateException(
220                                    "Unable to get Transformer for template " +
221                                            templateResource.getTemplateId(),
222                                    pae.getException());
223                    }
224                    catch (Exception e) {
225                            throw new TemplateException(
226                                    "Unable to get Transformer for template " +
227                                            templateResource.getTemplateId(),
228                                    e);
229                    }
230            }
231    
232            private Map<String, Object> _context;
233            private TemplateResource _errorTemplateResource;
234            private TemplateContextHelper _templateContextHelper;
235            private XSLTemplateResource _xslTemplateResource;
236    
237            private class TransformerPrivilegedExceptionAction
238                    implements PrivilegedExceptionAction<Transformer> {
239    
240                    public TransformerPrivilegedExceptionAction(
241                            TransformerFactory transformerFactory, StreamSource scriptSource) {
242    
243                            _transformerFactory = transformerFactory;
244                            _scriptSource = scriptSource;
245                    }
246    
247                    public Transformer run() throws Exception {
248                            return _transformerFactory.newTransformer(_scriptSource);
249                    }
250    
251                    private StreamSource _scriptSource;
252                    private TransformerFactory _transformerFactory;
253    
254            }
255    
256    }