1
0
Fork 0
mirror of https://github.com/vanitasvitae/Smack.git synced 2025-09-10 17:49:38 +02:00

Improve StringUtils.escapeForXml()

This commit is contained in:
Florian Schmaus 2016-01-13 15:04:01 +01:00
parent f2a748db9a
commit e6a9027cc6
11 changed files with 169 additions and 43 deletions

View file

@ -497,7 +497,7 @@ public class PacketParserUtils {
CharSequence text = parser.getText();
if (event == XmlPullParser.TEXT) {
// TODO the toString() can be removed in Smack 4.2.
text = StringUtils.escapeForXML(text.toString());
text = StringUtils.escapeForXmlText(text.toString());
}
sb.append(text);
}

View file

@ -1,6 +1,6 @@
/**
*
* Copyright 2003-2007 Jive Software.
* Copyright 2003-2007 Jive Software, 2016 Florian Schmaus.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -41,6 +41,74 @@ public class StringUtils {
public static final char[] HEX_CHARS = "0123456789abcdef".toCharArray();
/**
* Escape <code>input</code> for XML.
*
* @param input the input to escape.
* @return the XML escaped variant of <code>input</code>.
* @deprecated use {@link #escapeForXml(CharSequence)} instead.
*/
// Remove in 4.3.
@Deprecated
public static CharSequence escapeForXML(CharSequence input) {
return escapeForXml(input);
}
/**
* Escape <code>input</code> for XML.
*
* @param input the input to escape.
* @return the XML escaped variant of <code>input</code>.
*/
public static CharSequence escapeForXml(CharSequence input) {
return escapeForXml(input, XmlEscapeMode.safe);
}
/**
* Escape <code>input</code> for XML.
*
* @param input the input to escape.
* @return the XML escaped variant of <code>input</code>.
* @since 4.2
*/
public static CharSequence escapeForXmlAttribute(CharSequence input) {
return escapeForXml(input, XmlEscapeMode.forAttribute);
}
/**
* Escape <code>input</code> for XML.
* <p>
* This is an optimized variant of {@link #escapeForXmlAttribute(CharSequence)} for XML where the
* XML attribute is quoted using ''' (Apos).
* </p>
*
* @param input the input to escape.
* @return the XML escaped variant of <code>input</code>.
* @since 4.2
*/
public static CharSequence escapeForXmlAttributeApos(CharSequence input) {
return escapeForXml(input, XmlEscapeMode.forAttributeApos);
}
/**
* Escape <code>input</code> for XML.
*
* @param input the input to escape.
* @return the XML escaped variant of <code>input</code>.
* @since 4.2
*/
public static CharSequence escapeForXmlText(CharSequence input) {
return escapeForXml(input, XmlEscapeMode.forText);
}
private enum XmlEscapeMode {
safe,
forAttribute,
forAttributeApos,
forText,
;
};
/**
* Escapes all necessary characters in the CharSequence so that it can be used
* in an XML doc.
@ -48,7 +116,7 @@ public class StringUtils {
* @param input the CharSequence to escape.
* @return the string with appropriate characters escaped.
*/
public static CharSequence escapeForXML(final CharSequence input) {
private static CharSequence escapeForXml(final CharSequence input, final XmlEscapeMode xmlEscapeMode) {
if (input == null) {
return null;
}
@ -61,23 +129,75 @@ public class StringUtils {
while (i < len) {
toAppend = null;
ch = input.charAt(i);
switch(ch) {
case '<':
toAppend = LT_ENCODE;
switch (xmlEscapeMode) {
case safe:
switch (ch) {
case '<':
toAppend = LT_ENCODE;
break;
case '>':
toAppend = GT_ENCODE;
break;
case '&':
toAppend = AMP_ENCODE;
break;
case '"':
toAppend = QUOTE_ENCODE;
break;
case '\'':
toAppend = APOS_ENCODE;
break;
default:
break;
}
break;
case '>':
toAppend = GT_ENCODE;
case forAttribute:
// No need to escape '>' for attributes.
switch(ch) {
case '<':
toAppend = LT_ENCODE;
break;
case '&':
toAppend = AMP_ENCODE;
break;
case '"':
toAppend = QUOTE_ENCODE;
break;
case '\'':
toAppend = APOS_ENCODE;
break;
default:
break;
}
break;
case '&':
toAppend = AMP_ENCODE;
case forAttributeApos:
// No need to escape '>' and '"' for attributes using '\'' as quote.
switch(ch) {
case '<':
toAppend = LT_ENCODE;
break;
case '&':
toAppend = AMP_ENCODE;
break;
case '\'':
toAppend = APOS_ENCODE;
break;
default:
break;
}
break;
case '"':
toAppend = QUOTE_ENCODE;
break;
case '\'':
toAppend = APOS_ENCODE;
break;
default:
case forText:
// No need to escape '"', '\'', and '>' for text.
switch(ch) {
case '<':
toAppend = LT_ENCODE;
break;
case '&':
toAppend = AMP_ENCODE;
break;
default:
break;
}
break;
}
if (toAppend != null) {

View file

@ -233,7 +233,7 @@ public class XmlStringBuilder implements Appendable, CharSequence {
public XmlStringBuilder attribute(String name, String value) {
assert value != null;
sb.append(' ').append(name).append("='");
escape(value);
escapeAttributeValue(value);
sb.append('\'');
return this;
}
@ -357,7 +357,13 @@ public class XmlStringBuilder implements Appendable, CharSequence {
public XmlStringBuilder escape(String text) {
assert text != null;
sb.append(StringUtils.escapeForXML(text));
sb.append(StringUtils.escapeForXml(text));
return this;
}
public XmlStringBuilder escapeAttributeValue(String value) {
assert value != null;
sb.append(StringUtils.escapeForXmlAttributeApos(value));
return this;
}

View file

@ -28,43 +28,43 @@ import org.junit.Test;
*/
public class StringUtilsTest {
@Test
public void testEscapeForXML() {
public void testEscapeForXml() {
String input = null;
assertNull(StringUtils.escapeForXML(null));
assertNull(StringUtils.escapeForXml(null));
input = "<b>";
assertCharSequenceEquals("&lt;b&gt;", StringUtils.escapeForXML(input));
assertCharSequenceEquals("&lt;b&gt;", StringUtils.escapeForXml(input));
input = "\"";
assertCharSequenceEquals("&quot;", StringUtils.escapeForXML(input));
assertCharSequenceEquals("&quot;", StringUtils.escapeForXml(input));
input = "&";
assertCharSequenceEquals("&amp;", StringUtils.escapeForXML(input));
assertCharSequenceEquals("&amp;", StringUtils.escapeForXml(input));
input = "<b>\n\t\r</b>";
assertCharSequenceEquals("&lt;b&gt;\n\t\r&lt;/b&gt;", StringUtils.escapeForXML(input));
assertCharSequenceEquals("&lt;b&gt;\n\t\r&lt;/b&gt;", StringUtils.escapeForXml(input));
input = " & ";
assertCharSequenceEquals(" &amp; ", StringUtils.escapeForXML(input));
assertCharSequenceEquals(" &amp; ", StringUtils.escapeForXml(input));
input = " \" ";
assertCharSequenceEquals(" &quot; ", StringUtils.escapeForXML(input));
assertCharSequenceEquals(" &quot; ", StringUtils.escapeForXml(input));
input = "> of me <";
assertCharSequenceEquals("&gt; of me &lt;", StringUtils.escapeForXML(input));
assertCharSequenceEquals("&gt; of me &lt;", StringUtils.escapeForXml(input));
input = "> of me & you<";
assertCharSequenceEquals("&gt; of me &amp; you&lt;", StringUtils.escapeForXML(input));
assertCharSequenceEquals("&gt; of me &amp; you&lt;", StringUtils.escapeForXml(input));
input = "& <";
assertCharSequenceEquals("&amp; &lt;", StringUtils.escapeForXML(input));
assertCharSequenceEquals("&amp; &lt;", StringUtils.escapeForXml(input));
input = "&";
assertCharSequenceEquals("&amp;", StringUtils.escapeForXML(input));
assertCharSequenceEquals("&amp;", StringUtils.escapeForXml(input));
input = "It's a good day today";
assertCharSequenceEquals("It&apos;s a good day today", StringUtils.escapeForXML(input));
assertCharSequenceEquals("It&apos;s a good day today", StringUtils.escapeForXml(input));
}
public static void assertCharSequenceEquals(CharSequence expected, CharSequence actual) {