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