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