001/**
002 *
003 * Copyright © 2014-2019 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            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 SmackTextParseException {
312        if (dateString == null) {
313            return null;
314        }
315        return getDateFromXep82String(dateString);
316    }
317
318    public static Date getDateFromXep82String(String dateString) throws SmackTextParseException {
319        try {
320            return XmppDateTime.parseXEP0082Date(dateString);
321        } catch (ParseException e) {
322            throw new SmackParsingException.SmackTextParseException(e);
323        }
324    }
325
326    public static Date getDateFromString(String dateString) throws SmackTextParseException {
327        try {
328            return XmppDateTime.parseDate(dateString);
329        } catch (ParseException e) {
330            throw new SmackParsingException.SmackTextParseException(e);
331        }
332    }
333
334    public static Date getDateFromNextText(XmlPullParser parser) throws XmlPullParserException, IOException, SmackTextParseException {
335        String dateString = parser.nextText();
336        return getDateFromString(dateString);
337    }
338
339    public static URI getUriFromNextText(XmlPullParser parser) throws XmlPullParserException, IOException, SmackUriSyntaxParsingException  {
340        String uriString = parser.nextText();
341        try {
342            return new URI(uriString);
343        }
344        catch (URISyntaxException e) {
345            throw new SmackParsingException.SmackUriSyntaxParsingException(e);
346        }
347    }
348
349    public static String getRequiredAttribute(XmlPullParser parser, String name) throws IOException {
350        String value = parser.getAttributeValue("", name);
351        if (StringUtils.isNullOrEmpty(value)) {
352            throw new IOException("Attribute " + name + " is null or empty (" + value + ')');
353        }
354        return value;
355    }
356
357    public static String getRequiredNextText(XmlPullParser parser) throws XmlPullParserException, IOException {
358        String text = parser.nextText();
359        if (StringUtils.isNullOrEmpty(text)) {
360            throw new IOException("Next text is null or empty (" + text + ')');
361        }
362        return text;
363    }
364
365    public static String getXmlLang(XmlPullParser parser, XmlEnvironment xmlEnvironment) {
366        String currentXmlLang = getXmlLang(parser);
367        if (currentXmlLang != null) {
368            return currentXmlLang;
369        }
370        return xmlEnvironment.getEffectiveLanguage();
371    }
372
373    public static String getXmlLang(XmlPullParser parser) {
374        return parser.getAttributeValue("http://www.w3.org/XML/1998/namespace", "lang");
375    }
376
377    /**
378     * Get the QName of the current element.
379     *
380     * @param parser the parser.
381     * @return the Qname.
382     * @deprecated use {@link XmlPullParser#getQName()} instead.
383     */
384    @Deprecated
385    // TODO: Remove in Smack 4.5
386    public static QName getQName(XmlPullParser parser) {
387        return parser.getQName();
388    }
389}