001/**
002 *
003 * Copyright © 2016-2021 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.smackx.iot.data.provider;
018
019import java.io.IOException;
020import java.text.ParseException;
021import java.util.ArrayList;
022import java.util.Date;
023import java.util.List;
024import java.util.logging.Logger;
025
026import org.jivesoftware.smack.packet.XmlEnvironment;
027import org.jivesoftware.smack.provider.ExtensionElementProvider;
028import org.jivesoftware.smack.util.ParserUtils;
029import org.jivesoftware.smack.xml.XmlPullParser;
030import org.jivesoftware.smack.xml.XmlPullParserException;
031
032import org.jivesoftware.smackx.iot.data.element.IoTDataField;
033import org.jivesoftware.smackx.iot.data.element.IoTFieldsExtension;
034import org.jivesoftware.smackx.iot.data.element.NodeElement;
035import org.jivesoftware.smackx.iot.data.element.TimestampElement;
036import org.jivesoftware.smackx.iot.element.NodeInfo;
037import org.jivesoftware.smackx.iot.parser.NodeInfoParser;
038
039import org.jxmpp.util.XmppDateTime;
040
041public class IoTFieldsExtensionProvider extends ExtensionElementProvider<IoTFieldsExtension> {
042
043    private static final Logger LOGGER = Logger.getLogger(IoTFieldsExtensionProvider.class.getName());
044
045    @Override
046    public IoTFieldsExtension parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment)
047                    throws IOException, XmlPullParserException, ParseException {
048        int seqNr = ParserUtils.getIntegerAttributeOrThrow(parser, "seqnr", "IoT data request <accepted/> without sequence number");
049        boolean done = ParserUtils.getBooleanAttribute(parser, "done", false);
050        List<NodeElement> nodes = new ArrayList<>();
051        outerloop: while (true) {
052            final XmlPullParser.Event eventType = parser.next();
053            final String name = parser.getName();
054            switch (eventType) {
055            case START_ELEMENT:
056                switch (name) {
057                case NodeElement.ELEMENT:
058                    NodeElement node = parseNode(parser);
059                    nodes.add(node);
060                    break;
061                }
062                break;
063            case END_ELEMENT:
064                if (parser.getDepth() == initialDepth) {
065                    break outerloop;
066                }
067                break;
068            default:
069                // Catch all for incomplete switch (MissingCasesInEnumSwitch) statement.
070                break;
071            }
072        }
073        return new IoTFieldsExtension(seqNr, done, nodes);
074    }
075
076    public NodeElement parseNode(XmlPullParser parser) throws XmlPullParserException, IOException, ParseException {
077        final int initialDepth = parser.getDepth();
078        final NodeInfo nodeInfo = NodeInfoParser.parse(parser);
079        List<TimestampElement> timestampElements = new ArrayList<>();
080        outerloop: while (true) {
081            final XmlPullParser.Event eventType = parser.next();
082            final String name = parser.getName();
083            switch (eventType) {
084            case START_ELEMENT:
085                switch (name){
086                case TimestampElement.ELEMENT:
087                    TimestampElement timestampElement = parseTimestampElement(parser);
088                    timestampElements.add(timestampElement);
089                    break;
090                }
091                break;
092            case END_ELEMENT:
093                if (parser.getDepth() == initialDepth) {
094                    break outerloop;
095                }
096                break;
097            default:
098                // Catch all for incomplete switch (MissingCasesInEnumSwitch) statement.
099                break;
100            }
101        }
102        return new NodeElement(nodeInfo, timestampElements);
103    }
104
105    public TimestampElement parseTimestampElement(XmlPullParser parser)
106                    throws XmlPullParserException, IOException, ParseException {
107        final int initialDepth = parser.getDepth();
108        final String dateString = parser.getAttributeValue(null, "value");
109        Date date = XmppDateTime.parseDate(dateString);
110        List<IoTDataField> fields = new ArrayList<>();
111        outerloop: while (true) {
112            final XmlPullParser.Event eventType = parser.next();
113            switch (eventType) {
114            case START_ELEMENT:
115                final String name = parser.getName();
116                IoTDataField field = null;
117                final String fieldName = parser.getAttributeValue(null, "name");
118                final String fieldValue = parser.getAttributeValue(null, "value");
119                switch (name) {
120                case "int": {
121                    int value = Integer.parseInt(fieldValue);
122                    field = new IoTDataField.IntField(fieldName, value);
123                    }
124                    break;
125                case "boolean": {
126                    boolean value = Boolean.parseBoolean(fieldValue);
127                    field = new IoTDataField.BooleanField(fieldName, value);
128                    }
129                    break;
130                default:
131                    LOGGER.warning("IoT Data field type '" + name + "' not implement yet. Ignoring.");
132                    break;
133                }
134                if (field != null) {
135                    fields.add(field);
136                }
137                break;
138            case END_ELEMENT:
139                if (parser.getDepth() == initialDepth) {
140                    break outerloop;
141                }
142                break;
143            default:
144                // Catch all for incomplete switch (MissingCasesInEnumSwitch) statement.
145                break;
146            }
147        }
148        return new TimestampElement(date, fields);
149    }
150}