From 363354f237b9d840f0e17347d9567b345fffe4a5 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Thu, 20 Mar 2014 14:35:38 +0100 Subject: [PATCH] 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. --- .../smack/DirectoryRosterStore.java | 71 +++++----- .../jivesoftware/smack/util/StringUtils.java | 25 +--- .../smack/util/XmlStringBuilder.java | 133 ++++++++++++++++++ 3 files changed, 173 insertions(+), 56 deletions(-) create mode 100644 core/src/main/java/org/jivesoftware/smack/util/XmlStringBuilder.java diff --git a/core/src/main/java/org/jivesoftware/smack/DirectoryRosterStore.java b/core/src/main/java/org/jivesoftware/smack/DirectoryRosterStore.java index 203b1ebd0..38065bf8e 100644 --- a/core/src/main/java/org/jivesoftware/smack/DirectoryRosterStore.java +++ b/core/src/main/java/org/jivesoftware/smack/DirectoryRosterStore.java @@ -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(""); - for (String group : item.getGroupNames()) { - s.append(""); - } - s.append(""); - return FileUtils.writeFile(getBareJidFile(item.getUser()), s.toString()); + xml.closeElement("item"); + + return FileUtils.writeFile(getBareJidFile(item.getUser()), xml.toString()); } diff --git a/core/src/main/java/org/jivesoftware/smack/util/StringUtils.java b/core/src/main/java/org/jivesoftware/smack/util/StringUtils.java index cea2a674f..07cab1082 100644 --- a/core/src/main/java/org/jivesoftware/smack/util/StringUtils.java +++ b/core/src/main/java/org/jivesoftware/smack/util/StringUtils.java @@ -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) { diff --git a/core/src/main/java/org/jivesoftware/smack/util/XmlStringBuilder.java b/core/src/main/java/org/jivesoftware/smack/util/XmlStringBuilder.java new file mode 100644 index 000000000..5ca8538c8 --- /dev/null +++ b/core/src/main/java/org/jivesoftware/smack/util/XmlStringBuilder.java @@ -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("'); + 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(); + } +}