mirror of
https://codeberg.org/Mercury-IM/Smack
synced 2024-11-22 14:22:05 +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();
|
CharSequence text = parser.getText();
|
||||||
if (event == XmlPullParser.TEXT) {
|
if (event == XmlPullParser.TEXT) {
|
||||||
// TODO the toString() can be removed in Smack 4.2.
|
// TODO the toString() can be removed in Smack 4.2.
|
||||||
text = StringUtils.escapeForXML(text.toString());
|
text = StringUtils.escapeForXmlText(text.toString());
|
||||||
}
|
}
|
||||||
sb.append(text);
|
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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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();
|
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
|
* Escapes all necessary characters in the CharSequence so that it can be used
|
||||||
* in an XML doc.
|
* in an XML doc.
|
||||||
|
@ -48,7 +116,7 @@ public class StringUtils {
|
||||||
* @param input the CharSequence to escape.
|
* @param input the CharSequence to escape.
|
||||||
* @return the string with appropriate characters escaped.
|
* @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) {
|
if (input == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -61,23 +129,75 @@ public class StringUtils {
|
||||||
while (i < len) {
|
while (i < len) {
|
||||||
toAppend = null;
|
toAppend = null;
|
||||||
ch = input.charAt(i);
|
ch = input.charAt(i);
|
||||||
switch(ch) {
|
switch (xmlEscapeMode) {
|
||||||
case '<':
|
case safe:
|
||||||
toAppend = LT_ENCODE;
|
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;
|
break;
|
||||||
case '>':
|
case forAttribute:
|
||||||
toAppend = GT_ENCODE;
|
// 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;
|
break;
|
||||||
case '&':
|
case forAttributeApos:
|
||||||
toAppend = AMP_ENCODE;
|
// 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;
|
break;
|
||||||
case '"':
|
case forText:
|
||||||
toAppend = QUOTE_ENCODE;
|
// No need to escape '"', '\'', and '>' for text.
|
||||||
break;
|
switch(ch) {
|
||||||
case '\'':
|
case '<':
|
||||||
toAppend = APOS_ENCODE;
|
toAppend = LT_ENCODE;
|
||||||
break;
|
break;
|
||||||
default:
|
case '&':
|
||||||
|
toAppend = AMP_ENCODE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (toAppend != null) {
|
if (toAppend != null) {
|
||||||
|
|
|
@ -233,7 +233,7 @@ public class XmlStringBuilder implements Appendable, CharSequence {
|
||||||
public XmlStringBuilder attribute(String name, String value) {
|
public XmlStringBuilder attribute(String name, String value) {
|
||||||
assert value != null;
|
assert value != null;
|
||||||
sb.append(' ').append(name).append("='");
|
sb.append(' ').append(name).append("='");
|
||||||
escape(value);
|
escapeAttributeValue(value);
|
||||||
sb.append('\'');
|
sb.append('\'');
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -357,7 +357,13 @@ public class XmlStringBuilder implements Appendable, CharSequence {
|
||||||
|
|
||||||
public XmlStringBuilder escape(String text) {
|
public XmlStringBuilder escape(String text) {
|
||||||
assert text != null;
|
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;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,43 +28,43 @@ import org.junit.Test;
|
||||||
*/
|
*/
|
||||||
public class StringUtilsTest {
|
public class StringUtilsTest {
|
||||||
@Test
|
@Test
|
||||||
public void testEscapeForXML() {
|
public void testEscapeForXml() {
|
||||||
String input = null;
|
String input = null;
|
||||||
|
|
||||||
assertNull(StringUtils.escapeForXML(null));
|
assertNull(StringUtils.escapeForXml(null));
|
||||||
|
|
||||||
input = "<b>";
|
input = "<b>";
|
||||||
assertCharSequenceEquals("<b>", StringUtils.escapeForXML(input));
|
assertCharSequenceEquals("<b>", StringUtils.escapeForXml(input));
|
||||||
|
|
||||||
input = "\"";
|
input = "\"";
|
||||||
assertCharSequenceEquals(""", StringUtils.escapeForXML(input));
|
assertCharSequenceEquals(""", StringUtils.escapeForXml(input));
|
||||||
|
|
||||||
input = "&";
|
input = "&";
|
||||||
assertCharSequenceEquals("&", StringUtils.escapeForXML(input));
|
assertCharSequenceEquals("&", StringUtils.escapeForXml(input));
|
||||||
|
|
||||||
input = "<b>\n\t\r</b>";
|
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 = " & ";
|
input = " & ";
|
||||||
assertCharSequenceEquals(" & ", StringUtils.escapeForXML(input));
|
assertCharSequenceEquals(" & ", StringUtils.escapeForXml(input));
|
||||||
|
|
||||||
input = " \" ";
|
input = " \" ";
|
||||||
assertCharSequenceEquals(" " ", StringUtils.escapeForXML(input));
|
assertCharSequenceEquals(" " ", StringUtils.escapeForXml(input));
|
||||||
|
|
||||||
input = "> of me <";
|
input = "> of me <";
|
||||||
assertCharSequenceEquals("> of me <", StringUtils.escapeForXML(input));
|
assertCharSequenceEquals("> of me <", StringUtils.escapeForXml(input));
|
||||||
|
|
||||||
input = "> of me & you<";
|
input = "> of me & you<";
|
||||||
assertCharSequenceEquals("> of me & you<", StringUtils.escapeForXML(input));
|
assertCharSequenceEquals("> of me & you<", StringUtils.escapeForXml(input));
|
||||||
|
|
||||||
input = "& <";
|
input = "& <";
|
||||||
assertCharSequenceEquals("& <", StringUtils.escapeForXML(input));
|
assertCharSequenceEquals("& <", StringUtils.escapeForXml(input));
|
||||||
|
|
||||||
input = "&";
|
input = "&";
|
||||||
assertCharSequenceEquals("&", StringUtils.escapeForXML(input));
|
assertCharSequenceEquals("&", StringUtils.escapeForXml(input));
|
||||||
|
|
||||||
input = "It's a good day today";
|
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) {
|
public static void assertCharSequenceEquals(CharSequence expected, CharSequence actual) {
|
||||||
|
|
|
@ -186,7 +186,7 @@ public abstract class AbstractHttpOverXmppProvider<H extends AbstractHttpOverXmp
|
||||||
builder.append('>');
|
builder.append('>');
|
||||||
startClosed = true;
|
startClosed = true;
|
||||||
}
|
}
|
||||||
builder.append(StringUtils.escapeForXML(parser.getText()));
|
builder.append(StringUtils.escapeForXmlText(parser.getText()));
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalArgumentException("unexpected eventType: " + eventType);
|
throw new IllegalArgumentException("unexpected eventType: " + eventType);
|
||||||
}
|
}
|
||||||
|
@ -206,7 +206,7 @@ public abstract class AbstractHttpOverXmppProvider<H extends AbstractHttpOverXmp
|
||||||
builder.append(' ');
|
builder.append(' ');
|
||||||
builder.append(parser.getAttributeName(i));
|
builder.append(parser.getAttributeName(i));
|
||||||
builder.append("=\"");
|
builder.append("=\"");
|
||||||
builder.append(StringUtils.escapeForXML(parser.getAttributeValue(i)));
|
builder.append(StringUtils.escapeForXml(parser.getAttributeValue(i)));
|
||||||
builder.append('"');
|
builder.append('"');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -332,7 +332,7 @@ public class StreamInitiation extends IQ {
|
||||||
.append(getNamespace()).append("\" ");
|
.append(getNamespace()).append("\" ");
|
||||||
|
|
||||||
if (getName() != null) {
|
if (getName() != null) {
|
||||||
buffer.append("name=\"").append(StringUtils.escapeForXML(getName())).append("\" ");
|
buffer.append("name=\"").append(StringUtils.escapeForXmlAttribute(getName())).append("\" ");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getSize() > 0) {
|
if (getSize() > 0) {
|
||||||
|
@ -350,7 +350,7 @@ public class StreamInitiation extends IQ {
|
||||||
if ((desc != null && desc.length() > 0) || isRanged) {
|
if ((desc != null && desc.length() > 0) || isRanged) {
|
||||||
buffer.append('>');
|
buffer.append('>');
|
||||||
if (getDesc() != null && desc.length() > 0) {
|
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()) {
|
if (isRanged()) {
|
||||||
buffer.append("<range/>");
|
buffer.append("<range/>");
|
||||||
|
|
|
@ -525,13 +525,13 @@ public class VCard extends IQ {
|
||||||
private void updateFN() {
|
private void updateFN() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
if (firstName != null) {
|
if (firstName != null) {
|
||||||
sb.append(StringUtils.escapeForXML(firstName)).append(' ');
|
sb.append(StringUtils.escapeForXml(firstName)).append(' ');
|
||||||
}
|
}
|
||||||
if (middleName != null) {
|
if (middleName != null) {
|
||||||
sb.append(StringUtils.escapeForXML(middleName)).append(' ');
|
sb.append(StringUtils.escapeForXml(middleName)).append(' ');
|
||||||
}
|
}
|
||||||
if (lastName != null) {
|
if (lastName != null) {
|
||||||
sb.append(StringUtils.escapeForXML(lastName));
|
sb.append(StringUtils.escapeForXml(lastName));
|
||||||
}
|
}
|
||||||
setField("FN", sb.toString());
|
setField("FN", sb.toString());
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,7 +58,7 @@ public class DataLayoutTest {
|
||||||
+ "<text>SectionText - & \u00E9 \u00E1 </text>"
|
+ "<text>SectionText - & \u00E9 \u00E1 </text>"
|
||||||
+ "</section>"
|
+ "</section>"
|
||||||
+ "<text>PageText - & \u00E9 \u00E1 </text>"
|
+ "<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/>"
|
+ "<reportedref/>"
|
||||||
+ "</section>"
|
+ "</section>"
|
||||||
+ "<text><html><font color='red'><em>DO NOT DELAY</em></font><br/>supply further information</html></text>"
|
+ "<text><html><font color='red'><em>DO NOT DELAY</em></font><br/>supply further information</html></text>"
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
</section>
|
</section>
|
||||||
<text>PageText - & é á </text>
|
<text>PageText - & é á </text>
|
||||||
<section
|
<section
|
||||||
label='<html>Number of Persons by<br/> Nationality and Status</html>'>
|
label='<html>Number of Persons by<br/> Nationality and Status</html>'>
|
||||||
<reportedref />
|
<reportedref />
|
||||||
</section>
|
</section>
|
||||||
<text><![CDATA[<html><font color='red'><em>DO NOT DELAY</em></font><br/>supply further information</html>]]></text>
|
<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) {
|
if (getPersonalMacroGroup() != null) {
|
||||||
// CHECKSTYLE:OFF
|
// CHECKSTYLE:OFF
|
||||||
buf.append("<personalMacro>");
|
buf.append("<personalMacro>");
|
||||||
buf.append(StringUtils.escapeForXML(getPersonalMacroGroup().toXML()));
|
buf.append(StringUtils.escapeForXmlText(getPersonalMacroGroup().toXML()));
|
||||||
buf.append("</personalMacro>");
|
buf.append("</personalMacro>");
|
||||||
// CHECKSTYLE:ON
|
// CHECKSTYLE:ON
|
||||||
}
|
}
|
||||||
|
|
|
@ -97,7 +97,7 @@ public class MetaDataUtils {
|
||||||
for (Iterator<String> it = value.iterator(); it.hasNext();) {
|
for (Iterator<String> it = value.iterator(); it.hasNext();) {
|
||||||
String v = it.next();
|
String v = it.next();
|
||||||
buf.append("<value name=\"").append(key).append("\">");
|
buf.append("<value name=\"").append(key).append("\">");
|
||||||
buf.append(StringUtils.escapeForXML(v));
|
buf.append(StringUtils.escapeForXmlText(v));
|
||||||
buf.append("</value>");
|
buf.append("</value>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue