return stream.IsOk();
}
-// flags for OutputStringEnt()
-enum
+enum EscapingMode
{
- XML_ESCAPE_QUOTES = 1
+ Escape_Text,
+ Escape_Attribute
};
// Same as above, but create entities first.
-// Translates '<' to "<", '>' to ">" and '&' to "&"
-bool OutputStringEnt(wxOutputStream& stream,
- const wxString& str,
- wxMBConv *convMem,
- wxMBConv *convFile,
- int flags = 0)
-{
- const size_t len = str.length();
- size_t i,
- last = 0;
- for (i = 0; i < len; i++)
- {
- wxChar c = str.GetChar(i);
- if (c == wxS('<') || c == wxS('>') ||
- (c == wxS('&') && str.substr(i+1, 4) != wxS("amp;")) ||
- ((flags & XML_ESCAPE_QUOTES) && c == wxS('"')))
- {
- if ( !OutputString(stream, str.substr(last, i - last),
- convMem, convFile) )
- return false;
+// Translates '<' to "<", '>' to ">" and so on, according to the spec:
+// http://www.w3.org/TR/2000/WD-xml-c14n-20000119.html#charescaping
+bool OutputEscapedString(wxOutputStream& stream,
+ const wxString& str,
+ wxMBConv *convMem,
+ wxMBConv *convFile,
+ EscapingMode mode)
+{
+ wxString escaped;
+ escaped.reserve(str.length());
- const char *escaped;
- switch ( c )
- {
- case wxS('<'):
- escaped = "<";
- break;
- case wxS('>'):
- escaped = ">";
- break;
- case wxS('&'):
- escaped = "&";
- break;
- case wxS('"'):
- escaped = """;
- break;
- default:
- wxFAIL_MSG( "logic error in the code" );
- return false;
- }
+ for ( wxString::const_iterator i = str.begin(); i != str.end(); ++i )
+ {
+ const wxChar c = *i;
- if ( !OutputString(stream, escaped, convMem, convFile) )
- return false;
+ switch ( c )
+ {
+ case wxS('<'):
+ escaped.append(wxS("<"));
+ break;
+ case wxS('>'):
+ escaped.append(wxS(">"));
+ break;
+ case wxS('&'):
+ escaped.append(wxS("&"));
+ break;
+ case wxS('\r'):
+ escaped.append(wxS("
"));
+ break;
+ default:
+ if ( mode == Escape_Attribute )
+ {
+ switch ( c )
+ {
+ case wxS('"'):
+ escaped.append(wxS("""));
+ break;
+ case wxS('\t'):
+ escaped.append(wxS("	"));
+ break;
+ case wxS('\n'):
+ escaped.append(wxS("
"));
+ break;
+ default:
+ escaped.append(c);
+ }
- last = i + 1;
+ }
+ else
+ {
+ escaped.append(c);
+ }
}
}
- return OutputString(stream, str.substr(last, i - last), convMem, convFile);
+ return OutputString(stream, escaped, convMem, convFile);
}
bool OutputIndentation(wxOutputStream& stream,
break;
case wxXML_TEXT_NODE:
- rc = OutputStringEnt(stream, node->GetContent(), convMem, convFile);
+ rc = OutputEscapedString(stream, node->GetContent(),
+ convMem, convFile,
+ Escape_Text);
break;
case wxXML_ELEMENT_NODE:
rc = OutputString(stream,
wxS(" ") + attr->GetName() + wxS("=\""),
convMem, convFile) &&
- OutputStringEnt(stream, attr->GetValue(),
- convMem, convFile,
- XML_ESCAPE_QUOTES) &&
+ OutputEscapedString(stream, attr->GetValue(),
+ convMem, convFile,
+ Escape_Attribute) &&
OutputString(stream, wxS("\""), convMem, convFile);
}
}
CPPUNIT_TEST( InsertChildAfter );
CPPUNIT_TEST( LoadSave );
CPPUNIT_TEST( CDATA );
+ CPPUNIT_TEST( Escaping );
CPPUNIT_TEST_SUITE_END();
void InsertChild();
void InsertChildAfter();
void LoadSave();
void CDATA();
+ void Escaping();
DECLARE_NO_COPY_CLASS(XmlTestCase)
};
// is not
CPPUNIT_ASSERT_EQUAL( "Giovanni Mittone", n->GetContent() );
}
+
+void XmlTestCase::Escaping()
+{
+ // Verify that attribute values are escaped correctly, see
+ // http://trac.wxwidgets.org/ticket/12275
+
+ const char *xmlText =
+"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+"<root text=\"hello
this is a new line\">\n"
+" <x/>\n"
+"</root>\n"
+ ;
+
+ wxStringInputStream sis(xmlText);
+
+ wxXmlDocument doc;
+ CPPUNIT_ASSERT( doc.Load(sis) );
+
+ wxStringOutputStream sos;
+ CPPUNIT_ASSERT( doc.Save(sos) );
+
+ CPPUNIT_ASSERT_EQUAL( xmlText, sos.GetString() );
+}