001/**
002 *
003 * Copyright 2015-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.parsing;
018
019import java.io.IOException;
020import java.util.LinkedHashMap;
021import java.util.Map;
022
023import org.jivesoftware.smack.packet.StandardExtensionElement;
024import org.jivesoftware.smack.packet.XmlEnvironment;
025import org.jivesoftware.smack.provider.ExtensionElementProvider;
026import org.jivesoftware.smack.util.ParserUtils;
027import org.jivesoftware.smack.util.StringUtils;
028import org.jivesoftware.smack.xml.XmlPullParser;
029import org.jivesoftware.smack.xml.XmlPullParserException;
030
031/**
032 * The parser for {@link StandardExtensionElement}s.
033 *
034 * @author Florian Schmaus
035 *
036 */
037public class StandardExtensionElementProvider extends ExtensionElementProvider<StandardExtensionElement> {
038
039    public static StandardExtensionElementProvider INSTANCE = new StandardExtensionElementProvider();
040
041    @Override
042    public StandardExtensionElement parse(final XmlPullParser parser, final int initialDepth, XmlEnvironment xmlEnvironment)
043                    throws XmlPullParserException, IOException {
044        // Unlike most (all?) other providers, we don't know the name and namespace of the element
045        // we are parsing here.
046        String name = parser.getName();
047        String namespace = parser.getNamespace();
048        StandardExtensionElement.Builder builder = StandardExtensionElement.builder(name, namespace);
049        final int namespaceCount = parser.getNamespaceCount();
050        final int attributeCount = parser.getAttributeCount();
051        final Map<String, String> attributes = new LinkedHashMap<>(namespaceCount + attributeCount);
052        for (int i = 0; i < namespaceCount; i++) {
053            String nsprefix = parser.getNamespacePrefix(i);
054            if (nsprefix == null) {
055                // Skip the default namespace.
056                continue;
057            }
058            // XmlPullParser must either return null or a non-empty String.
059            assert StringUtils.isNotEmpty(nsprefix);
060            String nsuri = parser.getNamespaceUri(i);
061            attributes.put("xmlns:" + nsprefix, nsuri);
062        }
063        for (int i = 0; i < attributeCount; i++) {
064            String attributePrefix = parser.getAttributePrefix(i);
065            String attributeName = parser.getAttributeName(i);
066            String attributeValue = parser.getAttributeValue(i);
067            String attributeKey;
068            if (StringUtils.isNullOrEmpty(attributePrefix)) {
069                attributeKey = attributeName;
070            }
071            else {
072                attributeKey = attributePrefix + ':' + attributeName;
073            }
074            attributes.put(attributeKey, attributeValue);
075        }
076        builder.addAttributes(attributes);
077
078        outerloop: while (true) {
079            XmlPullParser.Event event = parser.next();
080            switch (event) {
081            case START_ELEMENT:
082                builder.addElement(parse(parser, parser.getDepth(), xmlEnvironment));
083                break;
084            case TEXT_CHARACTERS:
085                builder.setText(parser.getText());
086                break;
087            case END_ELEMENT:
088                if (initialDepth == parser.getDepth()) {
089                    break outerloop;
090                }
091                break;
092            default:
093                // Catch all for incomplete switch (MissingCasesInEnumSwitch) statement.
094                break;
095            }
096        }
097
098        ParserUtils.assertAtEndTag(parser);
099        return builder.build();
100    }
101}