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