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,
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    
071            @Override
072            public Object get(String key) {
073                    return _context.get(key);
074            }
075    
076            @Override
077            public String[] getKeys() {
078                    Set<String> keys = _context.keySet();
079    
080                    return keys.toArray(new String[keys.size()]);
081            }
082    
083            @Override
084            public TemplateContextHelper getTemplateContextHelper() {
085                    return _templateContextHelper;
086            }
087    
088            @Override
089            public void prepare(HttpServletRequest request) {
090                    _templateContextHelper.prepare(this, request);
091            }
092    
093            @Override
094            public void put(String key, Object value) {
095                    if (value == null) {
096                            return;
097                    }
098    
099                    _context.put(key, value);
100            }
101    
102            @Override
103            protected void doProcessTemplate(Writer writer) throws TemplateException {
104                    TransformerFactory transformerFactory =
105                            TransformerFactory.newInstance();
106    
107                    String languageId = null;
108    
109                    XSLURIResolver xslURIResolver =
110                            _xslTemplateResource.getXSLURIResolver();
111    
112                    if (xslURIResolver != null) {
113                            languageId = xslURIResolver.getLanguageId();
114                    }
115    
116                    Locale locale = LocaleUtil.fromLanguageId(languageId);
117    
118                    XSLErrorListener xslErrorListener = new XSLErrorListener(locale);
119    
120                    transformerFactory.setErrorListener(xslErrorListener);
121    
122                    transformerFactory.setURIResolver(xslURIResolver);
123    
124                    StreamSource xmlSource = new StreamSource(
125                            _xslTemplateResource.getXMLReader());
126    
127                    Transformer transformer = null;
128    
129                    if (_errorTemplateResource == null) {
130                            try {
131                                    transformer = _getTransformer(
132                                            transformerFactory, _xslTemplateResource);
133    
134                                    transformer.transform(xmlSource, new StreamResult(writer));
135    
136                                    return;
137                            }
138                            catch (Exception e) {
139                                    throw new TemplateException(
140                                            "Unable to process XSL template " +
141                                                    _xslTemplateResource.getTemplateId(),
142                                            e);
143                            }
144                    }
145    
146                    try {
147                            UnsyncStringWriter unsyncStringWriter = new UnsyncStringWriter();
148    
149                            transformer = _getTransformer(
150                                    transformerFactory, _xslTemplateResource);
151    
152                            transformer.setParameter(
153                                    TemplateConstants.WRITER, unsyncStringWriter);
154    
155                            transformer.transform(
156                                    xmlSource, new StreamResult(unsyncStringWriter));
157    
158                            StringBundler sb = unsyncStringWriter.getStringBundler();
159    
160                            sb.writeTo(writer);
161                    }
162                    catch (Exception e1) {
163                            Transformer errorTransformer = _getTransformer(
164                                    transformerFactory, _errorTemplateResource);
165    
166                            errorTransformer.setParameter(TemplateConstants.WRITER, writer);
167                            errorTransformer.setParameter(
168                                    "exception", xslErrorListener.getMessageAndLocation());
169    
170                            if (_errorTemplateResource instanceof StringTemplateResource) {
171                                    StringTemplateResource stringTemplateResource =
172                                            (StringTemplateResource)_errorTemplateResource;
173    
174                                    errorTransformer.setParameter(
175                                            "script", stringTemplateResource.getContent());
176                            }
177    
178                            if (xslErrorListener.getLocation() != null) {
179                                    errorTransformer.setParameter(
180                                            "column", new Integer(xslErrorListener.getColumnNumber()));
181                                    errorTransformer.setParameter(
182                                            "line", new Integer(xslErrorListener.getLineNumber()));
183                            }
184    
185                            try {
186                                    errorTransformer.transform(xmlSource, new StreamResult(writer));
187                            }
188                            catch (Exception e2) {
189                                    throw new TemplateException(
190                                            "Unable to process XSL template " +
191                                                    _errorTemplateResource.getTemplateId(),
192                                            e2);
193                            }
194                    }
195            }
196    
197            private Transformer _getTransformer(
198                            TransformerFactory transformerFactory,
199                            TemplateResource templateResource)
200                    throws TemplateException {
201    
202                    try {
203                            StreamSource scriptSource = new StreamSource(
204                                    templateResource.getReader());
205    
206                            Transformer transformer = AccessController.doPrivileged(
207                                    new TransformerPrivilegedExceptionAction(
208                                            transformerFactory, scriptSource));
209    
210                            for (Map.Entry<String, Object> entry : _context.entrySet()) {
211                                    transformer.setParameter(entry.getKey(), entry.getValue());
212                            }
213    
214                            return transformer;
215                    }
216                    catch (PrivilegedActionException pae) {
217                            throw new TemplateException(
218                                    "Unable to get Transformer for template " +
219                                            templateResource.getTemplateId(),
220                                    pae.getException());
221                    }
222                    catch (Exception e) {
223                            throw new TemplateException(
224                                    "Unable to get Transformer for template " +
225                                            templateResource.getTemplateId(),
226                                    e);
227                    }
228            }
229    
230            private Map<String, Object> _context;
231            private TemplateResource _errorTemplateResource;
232            private TemplateContextHelper _templateContextHelper;
233            private XSLTemplateResource _xslTemplateResource;
234    
235            private class TransformerPrivilegedExceptionAction
236                    implements PrivilegedExceptionAction<Transformer> {
237    
238                    public TransformerPrivilegedExceptionAction(
239                            TransformerFactory transformerFactory, StreamSource scriptSource) {
240    
241                            _transformerFactory = transformerFactory;
242                            _scriptSource = scriptSource;
243                    }
244    
245                    @Override
246                    public Transformer run() throws Exception {
247                            return _transformerFactory.newTransformer(_scriptSource);
248                    }
249    
250                    private StreamSource _scriptSource;
251                    private TransformerFactory _transformerFactory;
252    
253            }
254    
255    }