Java tutorial
/* * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. * * This 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 software 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. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.xwiki.velocity.tools; import java.util.Collection; import java.util.Map; import org.apache.commons.codec.EncoderException; import org.apache.commons.codec.net.BCodec; import org.apache.commons.codec.net.QCodec; import org.apache.commons.codec.net.QuotedPrintableCodec; import org.apache.commons.lang3.StringEscapeUtils; import org.xwiki.xml.XMLUtils; /** * <p> * Tool for working with escaping in Velocity templates. It provides methods to escape outputs for Velocity, Java, * JavaScript, HTML, XML and SQL. * </p> * <p> * Extends the default EscapeTool from velocity-tools since the XML escape performed by it doesn't work inside HTML * content, since {@code apos} is not a valid HTML entity name, and it always escapes non-ASCII characters, which * increases the HTML length considerably, while also making the source unreadable. * </p> * * @version $Id: 744b7679cd3a992428d932c2d7c141feb9903087 $ * @since 2.7RC1 */ public class EscapeTool extends org.apache.velocity.tools.generic.EscapeTool { /** Equals sign. */ private static final String EQUALS = "="; /** And sign. */ private static final String AND = "&"; /** * Escapes the XML special characters in a <code>String</code> using numerical XML entities. This overrides the base * implementation from Velocity, which is over-zealous and escapes any non-ASCII character. Since XWiki works with * Unicode-capable encodings (UTF-8), there is no need to escape non-special characters. * * @param content the text to escape, may be {@code null} * @return a new escaped {@code String}, {@code null} if {@code null} input */ @Override public String xml(Object content) { return XMLUtils.escape(content); } /** * Escapes the characters in a <code>String</code> using JSON String rules: escapes with backslash double quotes, * back and forward slashes, newlines, the control characters {@code \b}, {@code \t} and {@code \f}, and with * {@code \}{@code uXXXX} any non-ASCII characters. Unlike {@link #javascript(Object)}, it does not escape {@code '} * , which is not a special character in JSON, and it would be a syntax error to do so. * * @param string the string to escape, may be {@code null}; any non-string object will be converted to a string * first, using {@code String.valueOf(obj)} * @return String with escaped values, {@code null} if {@code null} input * @since 6.1M1 */ public String json(Object string) { if (string == null) { return null; } return StringEscapeUtils.escapeJson(String.valueOf(string)); } /** * Encode a text using the Quoted-Printable format, as specified in section 6.7 of <a * href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>. UTF-8 is used as the character encoding, and no line * breaking is performed. * * @param content the text to encode * @return the text converted into the Quoted-Printable format */ public String quotedPrintable(Object content) { if (content != null) { try { return new QuotedPrintableCodec().encode(String.valueOf(content)); } catch (EncoderException ex) { // Just return null } } return null; } /** * Encode a text using the Q encoding specified in <a href="http://www.ietf.org/rfc/rfc2047.txt">RFC 2047</a>. UTF-8 * is used as the character encoding, and no line breaking is performed. The resulting text is already wrapped with * the encoded word markers, starting with {@code =?UTF-8?Q?} and ending with {@code ?=}. * * @param content the text to encode * @return the text converted into an encoded word using the Q encoding */ public String q(Object content) { if (content != null) { try { return new QCodec().encode(String.valueOf(content)).replace(' ', '_'); } catch (EncoderException ex) { // Just return null } } return null; } /** * Encode a text using the B encoding specified in <a href="http://www.ietf.org/rfc/rfc2047.txt">RFC 2047</a>. UTF-8 * is used as the character encoding, and no line breaking is performed. The resulting text is already wrapped with * the encoded word markers, starting with {@code =?UTF-8?B?} and ending with {@code ?=}. * * @param content the text to encode * @return the text converted into an encoded word using the B encoding */ public String b(Object content) { if (content != null) { try { return new BCodec().encode(String.valueOf(content)); } catch (EncoderException ex) { // Just return null } } return null; } /** * Properly escape a parameter map representing a query string, so that it can be safely used in an URL. Parameters * can have multiple values in which case the value in the map is either an array or a {@link Collection}. If the * parameter name is {@code null} (the key is {@code null}) then the parameter is ignored. {@code null} values are * serialized as an empty string. * * @param parametersMap Map representing the query string. * @return the safe query string representing the passed parameters * @since 5.2M1 */ public String url(Map<String, ?> parametersMap) { StringBuilder queryStringBuilder = new StringBuilder(); for (Map.Entry<String, ?> entry : parametersMap.entrySet()) { if (entry.getKey() == null) { // Skip the parameter if its name is null. continue; } String cleanKey = this.url(entry.getKey()); Object mapValues = entry.getValue(); if (mapValues != null && mapValues.getClass().isArray()) { // A parameter with multiple values. Object[] values = (Object[]) mapValues; for (Object value : values) { addQueryStringPair(cleanKey, value, queryStringBuilder); } } else if (mapValues != null && Collection.class.isAssignableFrom(mapValues.getClass())) { // A parameter with multiple values. Collection<?> values = (Collection<?>) mapValues; for (Object value : values) { addQueryStringPair(cleanKey, value, queryStringBuilder); } } else { addQueryStringPair(cleanKey, mapValues, queryStringBuilder); } } return queryStringBuilder.toString(); } /** * Method to add an key / value pair to a query String. * * @param cleanKey Already escaped key * @param rawValue Raw value associated to the key * @param queryStringBuilder String Builder containing the current query string */ private void addQueryStringPair(String cleanKey, Object rawValue, StringBuilder queryStringBuilder) { // Serialize null values as an empty string. String valueAsString = rawValue == null ? "" : String.valueOf(rawValue); String cleanValue = this.url(valueAsString); if (queryStringBuilder.length() != 0) { queryStringBuilder.append(AND); } queryStringBuilder.append(cleanKey).append(EQUALS).append(cleanValue); } }