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 static org.xmlpull.v1.XmlPullParser.END_TAG;
020import static org.xmlpull.v1.XmlPullParser.START_TAG;
021
022import java.util.ArrayList;
023import java.util.HashSet;
024import java.util.List;
025import java.util.Set;
026
027import org.jivesoftware.smack.SmackException;
028import org.jivesoftware.smack.provider.ExtensionElementProvider;
029import org.jivesoftware.smack.util.ParserUtils;
030import org.jivesoftware.smackx.message_markup.element.BlockQuoteElement;
031import org.jivesoftware.smackx.message_markup.element.CodeBlockElement;
032import org.jivesoftware.smackx.message_markup.element.ListElement;
033import org.jivesoftware.smackx.message_markup.element.MarkupElement;
034import org.jivesoftware.smackx.message_markup.element.SpanElement;
035
036import org.xmlpull.v1.XmlPullParser;
037
038public class MarkupElementProvider extends ExtensionElementProvider<MarkupElement> {
039
040    @Override
041    public MarkupElement parse(XmlPullParser parser, int initialDepth) throws Exception {
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            int tag = parser.next();
053            String name = parser.getName();
054            int start, end;
055            switch (tag) {
056                case START_TAG:
057                    switch (name) {
058                        case BlockQuoteElement.ELEMENT:
059                            start = ParserUtils.getIntegerAttributeOrThrow(parser, BlockQuoteElement.ATTR_START,
060                                    "Message Markup BlockQuoteElement MUST contain a 'start' attribute.");
061                            end = ParserUtils.getIntegerAttributeOrThrow(parser, BlockQuoteElement.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, CodeBlockElement.ATTR_START,
068                                    "Message Markup CodeBlockElement MUST contain a 'start' attribute.");
069                            end = ParserUtils.getIntegerAttributeOrThrow(parser, CodeBlockElement.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, SpanElement.ATTR_START,
077                                    "Message Markup SpanElement MUST contain a 'start' attribute.");
078                            spanEnd = ParserUtils.getIntegerAttributeOrThrow(parser, SpanElement.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, ListElement.ATTR_START,
097                                    "Message Markup ListElement MUST contain a 'start' attribute.");
098                            listEnd = ParserUtils.getIntegerAttributeOrThrow(parser, ListElement.ATTR_END,
099                                    "Message Markup ListElement MUST contain a 'end' attribute.");
100                            break;
101
102                        case ListElement.ELEM_LI:
103                            start = ParserUtils.getIntegerAttributeOrThrow(parser, ListElement.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_TAG:
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                                throw new SmackException("Error while parsing incoming MessageMarkup ListElement: " +
121                                        "'start' attribute of first 'li' element must equal 'start' attribute of list.");
122                            }
123                            for (int i = 0; i < lis.size(); i++) {
124                                int elemStart = lis.get(i).getStart();
125                                int elemEnd = i < lis.size() - 1 ? lis.get(i + 1).getStart() : listEnd;
126                                listBuilder.addEntry(elemStart, elemEnd);
127                            }
128                            listBuilder.endList();
129                            break;
130
131                        case MarkupElement.ELEMENT:
132                            return markup.build();
133                    }
134
135            }
136        }
137    }
138
139}