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