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}