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}