/**
* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
package com.liferay.portal.template.xsl.internal;
import com.liferay.portal.kernel.io.unsync.UnsyncStringWriter;
import com.liferay.portal.kernel.template.StringTemplateResource;
import com.liferay.portal.kernel.template.Template;
import com.liferay.portal.kernel.template.TemplateConstants;
import com.liferay.portal.kernel.template.TemplateException;
import com.liferay.portal.kernel.template.TemplateResource;
import com.liferay.portal.kernel.util.LocaleUtil;
import com.liferay.portal.kernel.util.StringBundler;
import com.liferay.portal.template.TemplateContextHelper;
import com.liferay.portal.template.xsl.configuration.XSLEngineConfiguration;
import com.liferay.portal.xsl.XSLTemplateResource;
import com.liferay.portal.xsl.XSLURIResolver;
import java.io.Writer;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.Collection;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.xml.XMLConstants;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
/**
* @author Tina Tian
* @author Peter Fellwock
*/
public class XSLTemplate implements Template {
public XSLTemplate(
XSLTemplateResource xslTemplateResource,
TemplateResource errorTemplateResource,
TemplateContextHelper templateContextHelper,
XSLEngineConfiguration xslEngineConfiguration) {
if (xslTemplateResource == null) {
throw new IllegalArgumentException("XSL template resource is null");
}
if (templateContextHelper == null) {
throw new IllegalArgumentException(
"Template context helper is null");
}
_xslTemplateResource = xslTemplateResource;
_errorTemplateResource = errorTemplateResource;
_templateContextHelper = templateContextHelper;
_transformerFactory = TransformerFactory.newInstance();
try {
_transformerFactory.setFeature(
XMLConstants.FEATURE_SECURE_PROCESSING,
xslEngineConfiguration.secureProcessingEnabled());
}
catch (TransformerConfigurationException tce) {
}
_context = new HashMap<>();
}
@Override
public void clear() {
_context.clear();
}
@Override
public boolean containsKey(Object key) {
return _context.containsKey(key);
}
@Override
public boolean containsValue(Object value) {
return _context.containsValue(value);
}
@Override
public void doProcessTemplate(Writer writer) throws Exception {
String languageId = null;
XSLURIResolver xslURIResolver =
_xslTemplateResource.getXSLURIResolver();
if (xslURIResolver != null) {
languageId = xslURIResolver.getLanguageId();
}
Locale locale = LocaleUtil.fromLanguageId(languageId);
XSLErrorListener xslErrorListener = new XSLErrorListener(locale);
_transformerFactory.setErrorListener(xslErrorListener);
_transformerFactory.setURIResolver(xslURIResolver);
_xmlStreamSource = new StreamSource(
_xslTemplateResource.getXMLReader());
Transformer transformer = null;
if (_errorTemplateResource == null) {
try {
transformer = _getTransformer(
_transformerFactory, _xslTemplateResource);
transformer.transform(
_xmlStreamSource, new StreamResult(writer));
return;
}
catch (Exception e) {
throw new TemplateException(
"Unable to process XSL template " +
_xslTemplateResource.getTemplateId(),
e);
}
}
UnsyncStringWriter unsyncStringWriter = new UnsyncStringWriter();
transformer = _getTransformer(
_transformerFactory, _xslTemplateResource);
transformer.setParameter(TemplateConstants.WRITER, unsyncStringWriter);
transformer.transform(
_xmlStreamSource, new StreamResult(unsyncStringWriter));
StringBundler sb = unsyncStringWriter.getStringBundler();
sb.writeTo(writer);
}
@Override
public Set<Entry<String, Object>> entrySet() {
return _context.entrySet();
}
@Override
public Object get(Object key) {
return _context.get(key);
}
@Override
public Object get(String key) {
return _context.get(key);
}
@Override
public String[] getKeys() {
Set<String> keys = _context.keySet();
return keys.toArray(new String[keys.size()]);
}
@Override
public boolean isEmpty() {
return _context.isEmpty();
}
@Override
public Set<String> keySet() {
return _context.keySet();
}
@Override
public void prepare(HttpServletRequest request) {
_templateContextHelper.prepare(this, request);
}
@Override
public void processTemplate(Writer writer) throws TemplateException {
try {
doProcessTemplate(writer);
}
catch (Exception e1) {
Transformer errorTransformer = _getTransformer(
_transformerFactory, _errorTemplateResource);
errorTransformer.setParameter(TemplateConstants.WRITER, writer);
XSLErrorListener xslErrorListener =
(XSLErrorListener)_transformerFactory.getErrorListener();
errorTransformer.setParameter(
"exception", xslErrorListener.getMessageAndLocation());
if (_errorTemplateResource instanceof StringTemplateResource) {
StringTemplateResource stringTemplateResource =
(StringTemplateResource)_errorTemplateResource;
errorTransformer.setParameter(
"script", stringTemplateResource.getContent());
}
if (xslErrorListener.getLocation() != null) {
errorTransformer.setParameter(
"column",
Integer.valueOf(xslErrorListener.getColumnNumber()));
errorTransformer.setParameter(
"line", Integer.valueOf(xslErrorListener.getLineNumber()));
}
try {
errorTransformer.transform(
_xmlStreamSource, new StreamResult(writer));
}
catch (Exception e2) {
throw new TemplateException(
"Unable to process XSL template " +
_errorTemplateResource.getTemplateId(),
e2);
}
}
}
@Override
public Object put(String key, Object value) {
if (value == null) {
return null;
}
return _context.put(key, value);
}
@Override
public void putAll(Map<? extends String, ? extends Object> m) {
_context.putAll(m);
}
@Override
public Object remove(Object key) {
return _context.remove(key);
}
@Override
public int size() {
return _context.size();
}
@Override
public Collection<Object> values() {
return _context.values();
}
private Transformer _getTransformer(
TransformerFactory transformerFactory,
TemplateResource templateResource)
throws TemplateException {
try {
StreamSource scriptSource = new StreamSource(
templateResource.getReader());
Transformer transformer = AccessController.doPrivileged(
new TransformerPrivilegedExceptionAction(
transformerFactory, scriptSource));
for (Map.Entry<String, Object> entry : _context.entrySet()) {
transformer.setParameter(entry.getKey(), entry.getValue());
}
return transformer;
}
catch (PrivilegedActionException pae) {
throw new TemplateException(
"Unable to get Transformer for template " +
templateResource.getTemplateId(),
pae.getException());
}
catch (Exception e) {
throw new TemplateException(
"Unable to get Transformer for template " +
templateResource.getTemplateId(),
e);
}
}
private final Map<String, Object> _context;
private TemplateResource _errorTemplateResource;
private final TemplateContextHelper _templateContextHelper;
private TransformerFactory _transformerFactory;
private StreamSource _xmlStreamSource;
private final XSLTemplateResource _xslTemplateResource;
private static class TransformerPrivilegedExceptionAction
implements PrivilegedExceptionAction<Transformer> {
public TransformerPrivilegedExceptionAction(
TransformerFactory transformerFactory, StreamSource scriptSource) {
_transformerFactory = transformerFactory;
_scriptSource = scriptSource;
}
@Override
public Transformer run() throws Exception {
return _transformerFactory.newTransformer(_scriptSource);
}
private final StreamSource _scriptSource;
private TransformerFactory _transformerFactory;
}
}