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