mirror of
https://codeberg.org/Mercury-IM/Smack
synced 2024-12-22 20:47:57 +01:00
Improve StringUtils.escapeForXml()
This commit is contained in:
parent
f2a748db9a
commit
e6a9027cc6
11 changed files with 169 additions and 43 deletions
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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("<b>", StringUtils.escapeForXML(input));
|
||||
assertCharSequenceEquals("<b>", StringUtils.escapeForXml(input));
|
||||
|
||||
input = "\"";
|
||||
assertCharSequenceEquals(""", StringUtils.escapeForXML(input));
|
||||
assertCharSequenceEquals(""", StringUtils.escapeForXml(input));
|
||||
|
||||
input = "&";
|
||||
assertCharSequenceEquals("&", StringUtils.escapeForXML(input));
|
||||
assertCharSequenceEquals("&", StringUtils.escapeForXml(input));
|
||||
|
||||
input = "<b>\n\t\r</b>";
|
||||
assertCharSequenceEquals("<b>\n\t\r</b>", StringUtils.escapeForXML(input));
|
||||
assertCharSequenceEquals("<b>\n\t\r</b>", StringUtils.escapeForXml(input));
|
||||
|
||||
input = " & ";
|
||||
assertCharSequenceEquals(" & ", StringUtils.escapeForXML(input));
|
||||
assertCharSequenceEquals(" & ", StringUtils.escapeForXml(input));
|
||||
|
||||
input = " \" ";
|
||||
assertCharSequenceEquals(" " ", StringUtils.escapeForXML(input));
|
||||
assertCharSequenceEquals(" " ", StringUtils.escapeForXml(input));
|
||||
|
||||
input = "> of me <";
|
||||
assertCharSequenceEquals("> of me <", StringUtils.escapeForXML(input));
|
||||
assertCharSequenceEquals("> of me <", StringUtils.escapeForXml(input));
|
||||
|
||||
input = "> of me & you<";
|
||||
assertCharSequenceEquals("> of me & you<", StringUtils.escapeForXML(input));
|
||||
assertCharSequenceEquals("> of me & you<", StringUtils.escapeForXml(input));
|
||||
|
||||
input = "& <";
|
||||
assertCharSequenceEquals("& <", StringUtils.escapeForXML(input));
|
||||
assertCharSequenceEquals("& <", StringUtils.escapeForXml(input));
|
||||
|
||||
input = "&";
|
||||
assertCharSequenceEquals("&", StringUtils.escapeForXML(input));
|
||||
assertCharSequenceEquals("&", StringUtils.escapeForXml(input));
|
||||
|
||||
input = "It's a good day today";
|
||||
assertCharSequenceEquals("It's a good day today", StringUtils.escapeForXML(input));
|
||||
assertCharSequenceEquals("It's a good day today", StringUtils.escapeForXml(input));
|
||||
}
|
||||
|
||||
public static void assertCharSequenceEquals(CharSequence expected, CharSequence actual) {
|
||||
|
|
|
@ -186,7 +186,7 @@ public abstract class AbstractHttpOverXmppProvider<H extends AbstractHttpOverXmp
|
|||
builder.append('>');
|
||||
startClosed = true;
|
||||
}
|
||||
builder.append(StringUtils.escapeForXML(parser.getText()));
|
||||
builder.append(StringUtils.escapeForXmlText(parser.getText()));
|
||||
} else {
|
||||
throw new IllegalArgumentException("unexpected eventType: " + eventType);
|
||||
}
|
||||
|
@ -206,7 +206,7 @@ public abstract class AbstractHttpOverXmppProvider<H extends AbstractHttpOverXmp
|
|||
builder.append(' ');
|
||||
builder.append(parser.getAttributeName(i));
|
||||
builder.append("=\"");
|
||||
builder.append(StringUtils.escapeForXML(parser.getAttributeValue(i)));
|
||||
builder.append(StringUtils.escapeForXml(parser.getAttributeValue(i)));
|
||||
builder.append('"');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -332,7 +332,7 @@ public class StreamInitiation extends IQ {
|
|||
.append(getNamespace()).append("\" ");
|
||||
|
||||
if (getName() != null) {
|
||||
buffer.append("name=\"").append(StringUtils.escapeForXML(getName())).append("\" ");
|
||||
buffer.append("name=\"").append(StringUtils.escapeForXmlAttribute(getName())).append("\" ");
|
||||
}
|
||||
|
||||
if (getSize() > 0) {
|
||||
|
@ -350,7 +350,7 @@ public class StreamInitiation extends IQ {
|
|||
if ((desc != null && desc.length() > 0) || isRanged) {
|
||||
buffer.append('>');
|
||||
if (getDesc() != null && desc.length() > 0) {
|
||||
buffer.append("<desc>").append(StringUtils.escapeForXML(getDesc())).append("</desc>");
|
||||
buffer.append("<desc>").append(StringUtils.escapeForXmlText(getDesc())).append("</desc>");
|
||||
}
|
||||
if (isRanged()) {
|
||||
buffer.append("<range/>");
|
||||
|
|
|
@ -525,13 +525,13 @@ public class VCard extends IQ {
|
|||
private void updateFN() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (firstName != null) {
|
||||
sb.append(StringUtils.escapeForXML(firstName)).append(' ');
|
||||
sb.append(StringUtils.escapeForXml(firstName)).append(' ');
|
||||
}
|
||||
if (middleName != null) {
|
||||
sb.append(StringUtils.escapeForXML(middleName)).append(' ');
|
||||
sb.append(StringUtils.escapeForXml(middleName)).append(' ');
|
||||
}
|
||||
if (lastName != null) {
|
||||
sb.append(StringUtils.escapeForXML(lastName));
|
||||
sb.append(StringUtils.escapeForXml(lastName));
|
||||
}
|
||||
setField("FN", sb.toString());
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ public class DataLayoutTest {
|
|||
+ "<text>SectionText - & \u00E9 \u00E1 </text>"
|
||||
+ "</section>"
|
||||
+ "<text>PageText - & \u00E9 \u00E1 </text>"
|
||||
+ "<section label='<html>Number of Persons by<br/> Nationality and Status</html>'>"
|
||||
+ "<section label='<html>Number of Persons by<br/> Nationality and Status</html>'>"
|
||||
+ "<reportedref/>"
|
||||
+ "</section>"
|
||||
+ "<text><html><font color='red'><em>DO NOT DELAY</em></font><br/>supply further information</html></text>"
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
</section>
|
||||
<text>PageText - & é á </text>
|
||||
<section
|
||||
label='<html>Number of Persons by<br/> Nationality and Status</html>'>
|
||||
label='<html>Number of Persons by<br/> Nationality and Status</html>'>
|
||||
<reportedref />
|
||||
</section>
|
||||
<text><![CDATA[<html><font color='red'><em>DO NOT DELAY</em></font><br/>supply further information</html>]]></text>
|
||||
|
|
|
@ -85,7 +85,7 @@ public class Macros extends IQ {
|
|||
if (getPersonalMacroGroup() != null) {
|
||||
// CHECKSTYLE:OFF
|
||||
buf.append("<personalMacro>");
|
||||
buf.append(StringUtils.escapeForXML(getPersonalMacroGroup().toXML()));
|
||||
buf.append(StringUtils.escapeForXmlText(getPersonalMacroGroup().toXML()));
|
||||
buf.append("</personalMacro>");
|
||||
// CHECKSTYLE:ON
|
||||
}
|
||||
|
|
|
@ -97,7 +97,7 @@ public class MetaDataUtils {
|
|||
for (Iterator<String> it = value.iterator(); it.hasNext();) {
|
||||
String v = it.next();
|
||||
buf.append("<value name=\"").append(key).append("\">");
|
||||
buf.append(StringUtils.escapeForXML(v));
|
||||
buf.append(StringUtils.escapeForXmlText(v));
|
||||
buf.append("</value>");
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue