001/** 002 * 003 * Copyright © 2014-2023 Florian Schmaus 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.jivesoftware.smack.util; 018 019import java.io.IOException; 020import java.net.URI; 021import java.net.URISyntaxException; 022import java.text.ParseException; 023import java.util.Date; 024import java.util.Locale; 025 026import javax.xml.namespace.QName; 027 028import org.jivesoftware.smack.datatypes.UInt16; 029import org.jivesoftware.smack.datatypes.UInt32; 030import org.jivesoftware.smack.packet.XmlEnvironment; 031import org.jivesoftware.smack.parsing.SmackParsingException; 032import org.jivesoftware.smack.parsing.SmackParsingException.RequiredAttributeMissingException; 033import org.jivesoftware.smack.parsing.SmackParsingException.SmackTextParseException; 034import org.jivesoftware.smack.parsing.SmackParsingException.SmackUriSyntaxParsingException; 035import org.jivesoftware.smack.xml.XmlPullParser; 036import org.jivesoftware.smack.xml.XmlPullParserException; 037 038import org.jxmpp.jid.EntityBareJid; 039import org.jxmpp.jid.EntityFullJid; 040import org.jxmpp.jid.EntityJid; 041import org.jxmpp.jid.Jid; 042import org.jxmpp.jid.impl.JidCreate; 043import org.jxmpp.jid.parts.Resourcepart; 044import org.jxmpp.stringprep.XmppStringprepException; 045import org.jxmpp.util.XmppDateTime; 046 047public class ParserUtils { 048 049 /** 050 * The constant String "jid". 051 */ 052 public static final String JID = "jid"; 053 054 public static void assertAtStartTag(XmlPullParser parser) throws XmlPullParserException { 055 assert parser.getEventType() == XmlPullParser.Event.START_ELEMENT; 056 } 057 058 public static void assertAtStartTag(XmlPullParser parser, String name) throws XmlPullParserException { 059 assertAtStartTag(parser); 060 assert name.equals(parser.getName()); 061 } 062 063 public static void assertAtEndTag(XmlPullParser parser) throws XmlPullParserException { 064 assert parser.getEventType() == XmlPullParser.Event.END_ELEMENT; 065 } 066 067 public static void forwardToStartElement(XmlPullParser parser) throws XmlPullParserException, IOException { 068 // Wind the parser forward to the first start tag 069 XmlPullParser.Event event = parser.getEventType(); 070 while (event != XmlPullParser.Event.START_ELEMENT) { 071 if (event == XmlPullParser.Event.END_DOCUMENT) { 072 throw new IllegalArgumentException("Document contains no start tag"); 073 } 074 event = parser.next(); 075 } 076 } 077 078 public static void forwardToEndTagOfDepth(XmlPullParser parser, int depth) 079 throws XmlPullParserException, IOException { 080 XmlPullParser.Event event = parser.getEventType(); 081 while (!(event == XmlPullParser.Event.END_ELEMENT && parser.getDepth() == depth)) { 082 assert event != XmlPullParser.Event.END_DOCUMENT; 083 event = parser.next(); 084 } 085 } 086 087 public static Jid getJidAttribute(XmlPullParser parser) throws XmppStringprepException { 088 return getJidAttribute(parser, JID); 089 } 090 091 public static Jid getJidAttribute(XmlPullParser parser, String name) throws XmppStringprepException { 092 final String jidString = parser.getAttributeValue("", name); 093 if (jidString == null) { 094 return null; 095 } 096 return JidCreate.from(jidString); 097 } 098 099 public static EntityBareJid getBareJidAttribute(XmlPullParser parser) throws XmppStringprepException { 100 return getBareJidAttribute(parser, JID); 101 } 102 103 public static EntityBareJid getBareJidAttribute(XmlPullParser parser, String name) throws XmppStringprepException { 104 final String jidString = parser.getAttributeValue("", name); 105 if (jidString == null) { 106 return null; 107 } 108 return JidCreate.entityBareFrom(jidString); 109 } 110 111 public static EntityFullJid getFullJidAttribute(XmlPullParser parser) throws XmppStringprepException { 112 return getFullJidAttribute(parser, JID); 113 } 114 115 public static EntityFullJid getFullJidAttribute(XmlPullParser parser, String name) throws XmppStringprepException { 116 final String jidString = parser.getAttributeValue("", name); 117 if (jidString == null) { 118 return null; 119 } 120 return JidCreate.entityFullFrom(jidString); 121 } 122 123 public static EntityJid getEntityJidAttribute(XmlPullParser parser, String name) throws XmppStringprepException { 124 final String jidString = parser.getAttributeValue("", name); 125 if (jidString == null) { 126 return null; 127 } 128 Jid jid = JidCreate.from(jidString); 129 130 if (!jid.hasLocalpart()) return null; 131 132 EntityFullJid fullJid = jid.asEntityFullJidIfPossible(); 133 if (fullJid != null) { 134 return fullJid; 135 } 136 137 EntityBareJid bareJid = jid.asEntityBareJidIfPossible(); 138 return bareJid; 139 } 140 141 public static Resourcepart getResourcepartAttribute(XmlPullParser parser, String name) throws XmppStringprepException { 142 final String resourcepartString = parser.getAttributeValue("", name); 143 if (resourcepartString == null) { 144 return null; 145 } 146 return Resourcepart.from(resourcepartString); 147 } 148 149 /** 150 * Prase a string to a boolean value as per "xs:boolean". Valid input strings are "true", "1" for true, and "false", "0" for false. 151 * 152 * @param booleanString the input string. 153 * @return the boolean representation of the input string 154 * @throws IllegalArgumentException if the input string is not valid. 155 * @since 4.3.2 156 */ 157 public static boolean parseXmlBoolean(String booleanString) { 158 switch (booleanString) { 159 case "true": 160 case "1": 161 return true; 162 case "false": 163 case "0": 164 return false; 165 default: 166 throw new IllegalArgumentException(booleanString + " is not a valid boolean string"); 167 } 168 } 169 170 /** 171 * Get the boolean value of an argument. 172 * 173 * @param parser TODO javadoc me please 174 * @param name TODO javadoc me please 175 * @return the boolean value or null of no argument of the given name exists 176 */ 177 public static Boolean getBooleanAttribute(XmlPullParser parser, String name) { 178 String valueString = parser.getAttributeValue("", name); 179 if (valueString == null) 180 return null; 181 valueString = valueString.toLowerCase(Locale.US); 182 return parseXmlBoolean(valueString); 183 } 184 185 public static boolean getBooleanAttribute(XmlPullParser parser, String name, 186 boolean defaultValue) { 187 Boolean bool = getBooleanAttribute(parser, name); 188 if (bool == null) { 189 return defaultValue; 190 } 191 else { 192 return bool; 193 } 194 } 195 196 public static Byte getByteAttributeFromNextText(XmlPullParser parser) throws IOException, XmlPullParserException { 197 String nextText = parser.nextText(); 198 return Byte.valueOf(nextText); 199 } 200 201 public static int getIntegerAttributeOrThrow(XmlPullParser parser, String name, String throwMessage) 202 throws IOException { 203 Integer res = getIntegerAttribute(parser, name); 204 if (res == null) { 205 // TODO Should be SmackParseException. 206 throw new IOException(throwMessage); 207 } 208 return res; 209 } 210 211 public static Integer getIntegerAttribute(XmlPullParser parser, String name) { 212 String valueString = parser.getAttributeValue("", name); 213 if (valueString == null) 214 return null; 215 return Integer.valueOf(valueString); 216 } 217 218 public static int getIntegerAttribute(XmlPullParser parser, String name, int defaultValue) { 219 Integer integer = getIntegerAttribute(parser, name); 220 if (integer == null) { 221 return defaultValue; 222 } 223 else { 224 return integer; 225 } 226 } 227 228 public static UInt16 getUInt16Attribute(XmlPullParser parser, String name) { 229 Integer integer = getIntegerAttribute(parser, name); 230 if (integer == null) { 231 return null; 232 } 233 return UInt16.from(integer); 234 } 235 236 public static UInt16 getRequiredUInt16Attribute(XmlPullParser parser, String name) throws RequiredAttributeMissingException { 237 UInt16 uint16 = getUInt16Attribute(parser, name); 238 if (uint16 == null) { 239 throw new SmackParsingException.RequiredAttributeMissingException(name); 240 } 241 return uint16; 242 } 243 244 public static int getIntegerFromNextText(XmlPullParser parser) throws XmlPullParserException, IOException { 245 String intString = parser.nextText(); 246 return Integer.valueOf(intString); 247 } 248 249 public static Long getLongAttribute(XmlPullParser parser, String name) { 250 String valueString = parser.getAttributeValue("", name); 251 if (valueString == null) 252 return null; 253 return Long.valueOf(valueString); 254 } 255 256 public static long getLongAttribute(XmlPullParser parser, String name, long defaultValue) { 257 Long l = getLongAttribute(parser, name); 258 if (l == null) { 259 return defaultValue; 260 } 261 else { 262 return l; 263 } 264 } 265 266 public static UInt32 getUInt32Attribute(XmlPullParser parser, String name) { 267 Long l = getLongAttribute(parser, name); 268 if (l == null) { 269 return null; 270 } 271 return UInt32.from(l); 272 } 273 274 public static double getDoubleFromNextText(XmlPullParser parser) throws XmlPullParserException, IOException { 275 String doubleString = parser.nextText(); 276 return Double.valueOf(doubleString); 277 } 278 279 public static Double getDoubleAttribute(XmlPullParser parser, String name) { 280 String valueString = parser.getAttributeValue("", name); 281 if (valueString == null) 282 return null; 283 return Double.valueOf(valueString); 284 } 285 286 public static double getDoubleAttribute(XmlPullParser parser, String name, long defaultValue) { 287 Double d = getDoubleAttribute(parser, name); 288 if (d == null) { 289 return defaultValue; 290 } 291 else { 292 return d; 293 } 294 } 295 296 public static Short getShortAttribute(XmlPullParser parser, String name) { 297 String valueString = parser.getAttributeValue("", name); 298 if (valueString == null) { 299 return null; 300 } 301 return Short.valueOf(valueString); 302 } 303 304 public static short getShortAttribute(XmlPullParser parser, String name, short defaultValue) { 305 Short s = getShortAttribute(parser, name); 306 if (s == null) { 307 return defaultValue; 308 } 309 return s; 310 } 311 312 public static Date getDateFromOptionalXep82String(String dateString) throws SmackTextParseException { 313 if (dateString == null) { 314 return null; 315 } 316 return getDateFromXep82String(dateString); 317 } 318 319 public static Date getDateFromXep82String(String dateString) throws SmackTextParseException { 320 try { 321 return XmppDateTime.parseXEP0082Date(dateString); 322 } catch (ParseException e) { 323 throw new SmackParsingException.SmackTextParseException(e); 324 } 325 } 326 327 public static Date getDateFromString(String dateString) throws SmackTextParseException { 328 try { 329 return XmppDateTime.parseDate(dateString); 330 } catch (ParseException e) { 331 throw new SmackParsingException.SmackTextParseException(e); 332 } 333 } 334 335 public static Date getDateFromNextText(XmlPullParser parser) throws XmlPullParserException, IOException, SmackTextParseException { 336 String dateString = parser.nextText(); 337 return getDateFromString(dateString); 338 } 339 340 public static URI getUriFromNextText(XmlPullParser parser) throws XmlPullParserException, IOException, SmackUriSyntaxParsingException { 341 String uriString = parser.nextText(); 342 try { 343 return new URI(uriString); 344 } 345 catch (URISyntaxException e) { 346 throw new SmackParsingException.SmackUriSyntaxParsingException(e); 347 } 348 } 349 350 public static String getRequiredAttribute(XmlPullParser parser, String name) throws IOException { 351 String value = parser.getAttributeValue("", name); 352 if (StringUtils.isNullOrEmpty(value)) { 353 throw new IOException("Attribute " + name + " is null or empty (" + value + ')'); 354 } 355 return value; 356 } 357 358 public static String getRequiredNextText(XmlPullParser parser) throws XmlPullParserException, IOException { 359 String text = parser.nextText(); 360 if (StringUtils.isNullOrEmpty(text)) { 361 throw new IOException("Next text is null or empty (" + text + ')'); 362 } 363 return text; 364 } 365 366 public static String getXmlLang(XmlPullParser parser, XmlEnvironment xmlEnvironment) { 367 String currentXmlLang = getXmlLang(parser); 368 if (currentXmlLang != null) { 369 return currentXmlLang; 370 } 371 return xmlEnvironment.getEffectiveLanguage(); 372 } 373 374 public static String getXmlLang(XmlPullParser parser) { 375 return parser.getAttributeValue("http://www.w3.org/XML/1998/namespace", "lang"); 376 } 377 378 /** 379 * Get the QName of the current element. 380 * 381 * @param parser the parser. 382 * @return the Qname. 383 * @deprecated use {@link XmlPullParser#getQName()} instead. 384 */ 385 @Deprecated 386 // TODO: Remove in Smack 4.5 387 public static QName getQName(XmlPullParser parser) { 388 return parser.getQName(); 389 } 390 391 public static InternetAddress getInternetAddressIngoringZoneIdAttribute(XmlPullParser parser, String attribute) { 392 String inetAddressString = parser.getAttributeValue(attribute); 393 if (inetAddressString == null) { 394 return null; 395 } 396 return InternetAddress.fromIgnoringZoneId(inetAddressString); 397 } 398}