001/**
002 *
003 * Copyright © 2016-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.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.parsing.SmackParsingException;
028import org.jivesoftware.smack.parsing.SmackParsingException.SmackTextParseException;
029import org.jivesoftware.smack.provider.ExtensionElementProvider;
030import org.jivesoftware.smack.util.ParserUtils;
031import org.jivesoftware.smack.xml.XmlPullParser;
032import org.jivesoftware.smack.xml.XmlPullParserException;
033
034import org.jivesoftware.smackx.iot.data.element.IoTDataField;
035import org.jivesoftware.smackx.iot.data.element.IoTFieldsExtension;
036import org.jivesoftware.smackx.iot.data.element.NodeElement;
037import org.jivesoftware.smackx.iot.data.element.TimestampElement;
038import org.jivesoftware.smackx.iot.element.NodeInfo;
039import org.jivesoftware.smackx.iot.parser.NodeInfoParser;
040
041import org.jxmpp.util.XmppDateTime;
042
043public class IoTFieldsExtensionProvider extends ExtensionElementProvider<IoTFieldsExtension> {
044
045    private static final Logger LOGGER = Logger.getLogger(IoTFieldsExtensionProvider.class.getName());
046
047    @Override
048    public IoTFieldsExtension parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws IOException, XmlPullParserException, SmackTextParseException {
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, SmackTextParseException {
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) throws XmlPullParserException, IOException, SmackTextParseException {
107        final int initialDepth = parser.getDepth();
108        final String dateString = parser.getAttributeValue(null, "value");
109        Date date;
110        try {
111            date = XmppDateTime.parseDate(dateString);
112        } catch (ParseException e) {
113            throw new SmackParsingException.SmackTextParseException(e);
114        }
115        List<IoTDataField> fields = new ArrayList<>();
116        outerloop: while (true) {
117            final XmlPullParser.Event eventType = parser.next();
118            switch (eventType) {
119            case START_ELEMENT:
120                final String name = parser.getName();
121                IoTDataField field = null;
122                final String fieldName = parser.getAttributeValue(null, "name");
123                final String fieldValue = parser.getAttributeValue(null, "value");
124                switch (name) {
125                case "int": {
126                    int value = Integer.parseInt(fieldValue);
127                    field = new IoTDataField.IntField(fieldName, value);
128                    }
129                    break;
130                case "boolean": {
131                    boolean value = Boolean.parseBoolean(fieldValue);
132                    field = new IoTDataField.BooleanField(fieldName, value);
133                    }
134                    break;
135                default:
136                    LOGGER.warning("IoT Data field type '" + name + "' not implement yet. Ignoring.");
137                    break;
138                }
139                if (field != null) {
140                    fields.add(field);
141                }
142                break;
143            case END_ELEMENT:
144                if (parser.getDepth() == initialDepth) {
145                    break outerloop;
146                }
147                break;
148            default:
149                // Catch all for incomplete switch (MissingCasesInEnumSwitch) statement.
150                break;
151            }
152        }
153        return new TimestampElement(date, fields);
154    }
155}