Make DirectoryRosterStore use elements for whitespace content

If whitespace (e.g. \n \t) needs to be preserved, then XML elements
should be used instead of attributes. Since we do this now in
DirectoryRosterStore, there is also no longer a need for an
specialized StringUtils.escapeForXML().

Also introduce XmlStringBuilder, which should become the default way
to implement Packet.toXML() and the like.
This commit is contained in:
Florian Schmaus 2014-03-20 14:35:38 +01:00
parent 6b4c53bfc5
commit 363354f237
3 changed files with 173 additions and 56 deletions

View File

@ -28,7 +28,7 @@ import org.jivesoftware.smack.packet.RosterPacket;
import org.jivesoftware.smack.packet.RosterPacket.Item;
import org.jivesoftware.smack.util.Base32Encoder;
import org.jivesoftware.smack.util.FileUtils;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smack.util.XmlStringBuilder;
import org.xmlpull.v1.XmlPullParserFactory;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@ -189,6 +189,7 @@ public class DirectoryRosterStore implements RosterStore {
return null;
}
String parserName;
String user = null;
String name = null;
String type = null;
@ -203,15 +204,31 @@ public class DirectoryRosterStore implements RosterStore {
boolean done = false;
while (!done) {
int eventType = parser.next();
parserName = parser.getName();
if (eventType == XmlPullParser.START_TAG) {
if (parser.getName().equals("item")) {
user = parser.getAttributeValue(null, "user");
name = parser.getAttributeValue(null, "name");
type = parser.getAttributeValue(null, "type");
status = parser.getAttributeValue(null, "status");
if (parserName.equals("item")) {
user = name = type = status = null;
}
if (parser.getName().equals("group")) {
String group = parser.getAttributeValue(null, "name");
else if (parserName.equals("user")) {
parser.next();
user = parser.getText();
}
else if (parserName.equals("name")) {
parser.next();
name = parser.getText();
}
else if (parserName.equals("type")) {
parser.next();
type = parser.getText();
}
else if (parserName.equals("status")) {
parser.next();
status = parser.getText();
}
else if (parserName.equals("group")) {
parser.next();
parser.next();
String group = parser.getText();
if (group != null) {
groupNames.add(group);
}
@ -222,7 +239,7 @@ public class DirectoryRosterStore implements RosterStore {
}
}
else if (eventType == XmlPullParser.END_TAG) {
if (parser.getName().equals("item")) {
if (parserName.equals("item")) {
done = true;
}
}
@ -271,30 +288,20 @@ public class DirectoryRosterStore implements RosterStore {
private boolean addEntryRaw (Item item) {
StringBuilder s = new StringBuilder();
s.append("<item ");
s.append(StringUtils.xmlAttrib("user", item.getUser()));
s.append(" ");
if (item.getName() != null) {
s.append(StringUtils.xmlAttrib("name", item.getName()));
s.append(" ");
XmlStringBuilder xml = new XmlStringBuilder();
xml.openElement("item");
xml.element("user", item.getUser());
xml.element("name", item.getName());
xml.element("type", item.getItemType());
xml.element("status", item.getItemStatus());
for (String groupName : item.getGroupNames()) {
xml.openElement("group");
xml.element("groupName", groupName);
xml.closeElement("group");
}
if (item.getItemType() != null) {
s.append(StringUtils.xmlAttrib("type", item.getItemType().name()));
s.append(" ");
}
if (item.getItemStatus() != null) {
s.append(StringUtils.xmlAttrib("status", item.getItemStatus().toString()));
s.append(" ");
}
s.append(">");
for (String group : item.getGroupNames()) {
s.append("<group ");
s.append(StringUtils.xmlAttrib("name", group));
s.append(" />");
}
s.append("</item>");
return FileUtils.writeFile(getBareJidFile(item.getUser()), s.toString());
xml.closeElement("item");
return FileUtils.writeFile(getBareJidFile(item.getUser()), xml.toString());
}

View File

@ -283,17 +283,6 @@ public class StringUtils {
return buf.toString();
}
/**
* Returns a string representing a XML attribute. The value parameter is escaped as necessary. In particular,
* white spaces are encoded as character references, such that they are not replaced by ' ' on parsing.
* @param name name of the XML attribute
* @param value value of the XML attribute
*/
public static String xmlAttrib(String name, String value) {
return name + "=\"" + escapeForXML(value, true) + "\"";
}
/**
* Escapes all necessary characters in the String so that it can be used
* in an XML doc.
@ -301,11 +290,7 @@ public class StringUtils {
* @param string the string to escape.
* @return the string with appropriate characters escaped.
*/
public static String escapeForXML(String string) {
return escapeForXML(string, false);
}
public static String escapeForXML(final String string, final boolean escapeWhitespace) {
public static String escapeForXML(final String string) {
if (string == null) {
return null;
}
@ -336,14 +321,6 @@ public class StringUtils {
toAppend = APOS_ENCODE;
break;
default:
// includes \t, \n, \r
if (escapeWhitespace && (ch <= 0x1f || (0x7f <= ch && ch <= 0x9f))) {
StringBuilder sb = new StringBuilder();
sb.append("&#x");
sb.append(String.format("%X", (int) ch));
sb.append(';');
toAppend = sb;
}
break;
}
if (toAppend != null) {

View File

@ -0,0 +1,133 @@
/**
*
* Copyright 2014 Florian Schmaus
*
* 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.jivesoftware.smack.util;
public class XmlStringBuilder implements Appendable, CharSequence {
private final StringBuilder sb;
public XmlStringBuilder() {
sb = new StringBuilder();
}
/**
* Does nothing if content is null.
*
* @param name
* @param content
* @return
*/
public XmlStringBuilder element(String name, String content) {
if (content == null)
return this;
openElement(name);
escape(content);
closeElement(name);
return this;
}
public XmlStringBuilder element(String name, Enum<?> content) {
if (content != null) {
element(name, content.name());
}
return this;
}
public XmlStringBuilder halfOpenElement(String name) {
sb.append('<').append(name);
return this;
}
public XmlStringBuilder openElement(String name) {
halfOpenElement(name).append('>');
return this;
}
public XmlStringBuilder closeElement(String name) {
sb.append("</").append(name).append('>');
return this;
}
public XmlStringBuilder emptyElementClose() {
sb.append("/>");
return this;
}
/**
* Does nothing if value is null.
*
* @param name
* @param value
* @return
*/
public XmlStringBuilder attribute(String name, String value) {
if (value == null)
return this;
sb.append(' ').append(name).append("='");
escape(value);
sb.append('\'');
return this;
}
public XmlStringBuilder xmlnsAttribute(String value) {
attribute("xmlns", value);
return this;
}
public XmlStringBuilder escape(String text) {
sb.append(StringUtils.escapeForXML(text));
return this;
}
@Override
public XmlStringBuilder append(CharSequence csq) {
sb.append(csq);
return this;
}
@Override
public XmlStringBuilder append(CharSequence csq, int start, int end) {
sb.append(csq, start, end);
return this;
}
@Override
public XmlStringBuilder append(char c) {
sb.append(c);
return this;
}
@Override
public int length() {
return sb.length();
}
@Override
public char charAt(int index) {
return sb.charAt(index);
}
@Override
public CharSequence subSequence(int start, int end) {
return sb.subSequence(start, end);
}
@Override
public String toString() {
return sb.toString();
}
}