/* * Copyright 2013 the original author or authors. * * 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.grails.encoder.impl; import org.grails.encoder.AbstractCharReplacementEncoder; import org.grails.encoder.CodecIdentifier; import org.grails.encoder.DefaultCodecIdentifier; import org.springframework.util.ClassUtils; /** * Encoder implementation that escapes some characters for inclusion in XML documents * * Currently ', ", <, > and & characters are replaced with XML entities. * Additionally backslash (/), non-breaking space, backtick (`) and @ are also replaced for visibility/additional security. * * @author Lari Hotari * @since 2.3 */ public class BasicXMLEncoder extends AbstractCharReplacementEncoder { private static final String ESCAPED_APOS = xmlEscapeCharacter('\''); // html doesn't have apos, so use numeric entity private static final String ESCAPED_QUOTE = """; private static final String ESCAPED_GT = ">"; private static final String ESCAPED_LT = "<"; private static final String ESCAPED_AMP = "&"; // some extras private static final String ESCAPED_BACKSLASH = xmlEscapeCharacter('\\'); private static final char NBSP=(char)160; private static final String ESCAPED_NON_BREAKING_SPACE = xmlEscapeCharacter(NBSP); private static final String ESCAPED_BACKTICK = xmlEscapeCharacter('`'); private static final String ESCAPED_AT = xmlEscapeCharacter('@'); // IE Javascript conditional compilation rules private static final char LINE_SEPARATOR = '\u2028'; private static final String ESCAPED_LINE_SEPARATOR = xmlEscapeCharacter(LINE_SEPARATOR); private static final char PARAGRAPH_SEPARATOR = '\u2029'; private static final String ESCAPED_PARAGRAPH_SEPARATOR = xmlEscapeCharacter(PARAGRAPH_SEPARATOR); protected static final String xmlEscapeCharacter(char ch) { return "&#" + ((int) ch) + ";"; } public static final CodecIdentifier XML_CODEC_IDENTIFIER=new DefaultCodecIdentifier("XML"); public BasicXMLEncoder() { super(XML_CODEC_IDENTIFIER); } protected BasicXMLEncoder(CodecIdentifier codecIdentifier) { super(codecIdentifier); } /* (non-Javadoc) * @see AbstractCharReplacementEncoder#escapeCharacter(char, char) */ @Override protected String escapeCharacter(char ch, char previousChar) { if(ch < ' ' && ch != '\t' && ch != '\n' && ch != '\r') { return ""; } switch(ch) { case '&': return ESCAPED_AMP; case '<': return ESCAPED_LT; case '>': return ESCAPED_GT; case '"': return ESCAPED_QUOTE; case '\'': return ESCAPED_APOS; case '\\': return ESCAPED_BACKSLASH; case '@': return ESCAPED_AT; case '`': return ESCAPED_BACKTICK; case NBSP: return ESCAPED_NON_BREAKING_SPACE; case LINE_SEPARATOR: return ESCAPED_LINE_SEPARATOR; case PARAGRAPH_SEPARATOR: return ESCAPED_PARAGRAPH_SEPARATOR; } return null; } @Override public final Object encode(Object o) { return doEncode(o); } protected Object doEncode(Object o) { if(o == null) { return null; } if(o instanceof CharSequence || ClassUtils.isPrimitiveOrWrapper(o.getClass())) { return doCharReplacementEncoding(o); } else { return encodeAsXmlObject(o); } } protected Object encodeAsXmlObject(Object o) { return o; } }