/* * ============================================================================= * * Copyright (c) 2011-2016, The THYMELEAF team (http://www.thymeleaf.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ============================================================================= */ package org.thymeleaf.util; import java.io.IOException; import java.io.Writer; import org.thymeleaf.IEngineConfiguration; import org.thymeleaf.exceptions.TemplateProcessingException; import org.thymeleaf.standard.serializer.IStandardCSSSerializer; import org.thymeleaf.standard.serializer.IStandardJavaScriptSerializer; import org.thymeleaf.standard.serializer.StandardSerializers; import org.thymeleaf.templatemode.TemplateMode; import org.unbescape.html.HtmlEscape; import org.unbescape.xml.XmlEscape; /** * <p> * Character sequence that performs a lazy escaping of a text, so that it is directly written to a <tt>Writer</tt> * output during the escape operation itself. * </p> * <p> * It is used sometimes internally by the engine in order to avoid the creation of extra String objects in * some scenarios (e.g. th:text). * </p> * <p> * This is mostly an <strong>internal class</strong>, and its use is not recommended from user's code. * </p> * <p> * This class is <strong>not</strong> thread-safe. * </p> * * * @author Daniel Fernández * * @since 3.0.0 * */ public final class LazyEscapingCharSequence extends AbstractLazyCharSequence { private final IEngineConfiguration configuration; private final TemplateMode templateMode; private final Object input; public LazyEscapingCharSequence(final IEngineConfiguration configuration, final TemplateMode templateMode, final Object input) { super(); if (configuration == null) { throw new IllegalArgumentException("Engine Configuraion is null, which is forbidden"); } if (templateMode == null) { throw new IllegalArgumentException("Template Mode is null, which is forbidden"); } this.configuration = configuration; this.templateMode = templateMode; this.input = input; } @Override protected String resolveText() { final Writer stringWriter = new FastStringWriter(); produceEscapedOutput(stringWriter); return stringWriter.toString(); } @Override protected void writeUnresolved(final Writer writer) throws IOException { produceEscapedOutput(writer); } private void produceEscapedOutput(final Writer writer) { /* * Producing ESCAPED output is somewhat simple in HTML or XML modes, as it simply consists of converting * input into a String and HTML-or-XML-escaping it. * * But for JavaScript or CSS, it becomes a bit more complicated than that. JavaScript will output a complete * literal (incl. single quotes) if input is a String or a non-recognized value type, but will print input * as an object, number, boolean, etc. if it is recognized to be of one of these types. CSS will output * quoted literals. * * Note that, when in TEXT mode, HTML escaping will be used (as TEXT is many times used for * processing HTML templates) */ try { switch (templateMode) { case TEXT: // fall-through case HTML: if (this.input != null) { HtmlEscape.escapeHtml4Xml(this.input.toString(), writer); } return; case XML: if (this.input != null) { XmlEscape.escapeXml10(this.input.toString(), writer); } return; case JAVASCRIPT: final IStandardJavaScriptSerializer javaScriptSerializer = StandardSerializers.getJavaScriptSerializer(this.configuration); javaScriptSerializer.serializeValue(this.input, writer); return; case CSS: final IStandardCSSSerializer cssSerializer = StandardSerializers.getCSSSerializer(this.configuration); cssSerializer.serializeValue(this.input, writer); return; case RAW: if (this.input != null) { writer.write(this.input.toString()); } return; default: throw new TemplateProcessingException( "Unrecognized template mode " + templateMode + ". Cannot produce escaped output for " + "this template mode."); } } catch (final IOException e) { throw new TemplateProcessingException("An error happened while trying to produce escaped output", e); } } }