001/*
002 *
003 * Copyright © 2018 Paul Schaub
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.message_markup.provider;
018
019import java.io.IOException;
020import java.util.ArrayList;
021import java.util.HashSet;
022import java.util.List;
023import java.util.Set;
024
025import org.jivesoftware.smack.packet.XmlEnvironment;
026import org.jivesoftware.smack.provider.ExtensionElementProvider;
027import org.jivesoftware.smack.util.ParserUtils;
028import org.jivesoftware.smack.xml.XmlPullParser;
029import org.jivesoftware.smack.xml.XmlPullParserException;
030
031import org.jivesoftware.smackx.message_markup.element.BlockQuoteElement;
032import org.jivesoftware.smackx.message_markup.element.CodeBlockElement;
033import org.jivesoftware.smackx.message_markup.element.ListElement;
034import org.jivesoftware.smackx.message_markup.element.MarkupElement;
035import org.jivesoftware.smackx.message_markup.element.MarkupElement.MarkupChildElement;
036import org.jivesoftware.smackx.message_markup.element.SpanElement;
037
038import org.jxmpp.JxmppContext;
039
040public class MarkupElementProvider extends ExtensionElementProvider<MarkupElement> {
041
042    @Override
043    public MarkupElement parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment, JxmppContext jxmppContext) throws IOException, XmlPullParserException {
044
045        MarkupElement.Builder markup = MarkupElement.getBuilder();
046
047        int spanStart = -1, spanEnd = -1;
048        Set<SpanElement.SpanStyle> spanStyles = new HashSet<>();
049
050        int listStart = -1, listEnd = -1;
051        List<ListElement.ListEntryElement> lis = new ArrayList<>();
052
053        while (true) {
054            XmlPullParser.Event tag = parser.next();
055            String name;
056            int start, end;
057            switch (tag) {
058                case START_ELEMENT:
059                    name = parser.getName();
060                    switch (name) {
061                        case BlockQuoteElement.ELEMENT:
062                            start = ParserUtils.getIntegerAttributeOrThrow(parser, MarkupChildElement.ATTR_START,
063                                    "Message Markup BlockQuoteElement MUST contain a 'start' attribute.");
064                            end = ParserUtils.getIntegerAttributeOrThrow(parser, MarkupChildElement.ATTR_END,
065                                    "Message Markup BlockQuoteElement MUST contain a 'end' attribute.");
066                            markup.setBlockQuote(start, end);
067                            break;
068
069                        case CodeBlockElement.ELEMENT:
070                            start = ParserUtils.getIntegerAttributeOrThrow(parser, MarkupChildElement.ATTR_START,
071                                    "Message Markup CodeBlockElement MUST contain a 'start' attribute.");
072                            end = ParserUtils.getIntegerAttributeOrThrow(parser, MarkupChildElement.ATTR_END,
073                                    "Message Markup CodeBlockElement MUST contain a 'end' attribute.");
074                            markup.setCodeBlock(start, end);
075                            break;
076
077                        case SpanElement.ELEMENT:
078                            spanStyles = new HashSet<>();
079                            spanStart = ParserUtils.getIntegerAttributeOrThrow(parser, MarkupChildElement.ATTR_START,
080                                    "Message Markup SpanElement MUST contain a 'start' attribute.");
081                            spanEnd = ParserUtils.getIntegerAttributeOrThrow(parser, MarkupChildElement.ATTR_END,
082                                    "Message Markup SpanElement MUST contain a 'end' attribute.");
083                            break;
084
085                        case SpanElement.code:
086                            spanStyles.add(SpanElement.SpanStyle.code);
087                            break;
088
089                        case SpanElement.emphasis:
090                            spanStyles.add(SpanElement.SpanStyle.emphasis);
091                            break;
092
093                        case SpanElement.deleted:
094                            spanStyles.add(SpanElement.SpanStyle.deleted);
095                            break;
096
097                        case ListElement.ELEMENT:
098                            lis = new ArrayList<>();
099                            listStart = ParserUtils.getIntegerAttributeOrThrow(parser, MarkupChildElement.ATTR_START,
100                                    "Message Markup ListElement MUST contain a 'start' attribute.");
101                            listEnd = ParserUtils.getIntegerAttributeOrThrow(parser, MarkupChildElement.ATTR_END,
102                                    "Message Markup ListElement MUST contain a 'end' attribute.");
103                            break;
104
105                        case ListElement.ListEntryElement.ELEMENT:
106                            start = ParserUtils.getIntegerAttributeOrThrow(parser, MarkupChildElement.ATTR_START,
107                                    "Message Markup ListElement 'li' MUST contain a 'start' attribute.");
108                            lis.add(new ListElement.ListEntryElement(start));
109                            break;
110                    }
111                    break;
112
113                case END_ELEMENT:
114                    if (parser.getDepth() == initialDepth) {
115                        return markup.build();
116                    }
117
118                    name = parser.getName();
119                    switch (name) {
120                        case SpanElement.ELEMENT:
121                            markup.addSpan(spanStart, spanEnd, spanStyles);
122                            spanStart = -1; spanEnd = -1;
123                            break;
124
125                        case ListElement.ELEMENT:
126                            MarkupElement.Builder.ListBuilder listBuilder = markup.beginList();
127                            if (lis.size() > 0 && lis.get(0).getStart() != listStart) {
128                                // TODO: Should be SmackParseException.
129                                throw new IOException("Error while parsing incoming MessageMarkup ListElement: " +
130                                        "'start' attribute of first 'li' element must equal 'start' attribute of list.");
131                            }
132                            for (int i = 0; i < lis.size(); i++) {
133                                int elemStart = lis.get(i).getStart();
134                                int elemEnd = i < lis.size() - 1 ? lis.get(i + 1).getStart() : listEnd;
135                                listBuilder.addEntry(elemStart, elemEnd);
136                            }
137                            listBuilder.endList();
138                            break;
139                    }
140                    break;
141
142                default:
143                    // Catch all for incomplete switch (MissingCasesInEnumSwitch) statement.
144                    break;
145            }
146        }
147    }
148
149}