001/** 002 * 003 * Copyright 2003-2007 Jive Software, 2014 Vyacheslav Blinov 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.xhtmlim.provider; 018 019import org.jivesoftware.smack.packet.PacketExtension; 020import org.jivesoftware.smack.provider.PacketExtensionProvider; 021import org.jivesoftware.smack.util.StringUtils; 022import org.jivesoftware.smackx.xhtmlim.packet.XHTMLExtension; 023import org.xmlpull.v1.XmlPullParser; 024import org.xmlpull.v1.XmlPullParserException; 025 026import java.io.IOException; 027 028/** 029 * The XHTMLExtensionProvider parses XHTML packets. 030 * 031 * @author Vyacheslav Blinov 032 */ 033public class XHTMLExtensionProvider implements PacketExtensionProvider { 034 public static final String BODY_ELEMENT = "body"; 035 036 @Override 037 public PacketExtension parseExtension(XmlPullParser parser) throws IOException, XmlPullParserException { 038 XHTMLExtension xhtmlExtension = new XHTMLExtension(); 039 final String XHTML_EXTENSION_ELEMENT_NAME = xhtmlExtension.getElementName(); 040 041 int startDepth = parser.getDepth(); 042 int tagDepth = parser.getDepth(); 043 boolean tagStarted = false; 044 StringBuilder buffer = new StringBuilder(); 045 046 while (true) { 047 int eventType = parser.next(); 048 if (eventType == XmlPullParser.START_TAG) { 049 boolean appendNamespace = false; 050 if (BODY_ELEMENT.equals(parser.getName())) { 051 buffer = new StringBuilder(); 052 tagDepth = parser.getDepth(); 053 appendNamespace = true; 054 } 055 maybeCloseTag(tagStarted, buffer); 056 appendStartTagPartial(buffer, parser, appendNamespace); 057 tagStarted = true; 058 } else if (eventType == XmlPullParser.TEXT) { 059 tagStarted = maybeCloseTag(tagStarted, buffer); 060 appendText(buffer, parser); 061 } else if (eventType == XmlPullParser.END_TAG) { 062 String name = parser.getName(); 063 if (XHTML_EXTENSION_ELEMENT_NAME.equals(name) && parser.getDepth() <= startDepth) { 064 return xhtmlExtension; 065 } else { 066 // xpp does not allows us to detect if tag is self-closing, so we have to 067 // handle self-closing tags by our own means 068 appendEndTag(buffer, parser, tagStarted); 069 tagStarted = false; 070 if (BODY_ELEMENT.equals(name) && parser.getDepth() <= tagDepth) { 071 xhtmlExtension.addBody(buffer.toString()); 072 } 073 } 074 } 075 } 076 } 077 078 private static void appendStartTagPartial(StringBuilder builder, XmlPullParser parser, boolean withNamespace) { 079 builder.append('<'); 080 081 String prefix = parser.getPrefix(); 082 if (StringUtils.isNotEmpty(prefix)) { 083 builder.append(prefix).append(':'); 084 } 085 builder.append(parser.getName()); 086 087 int attributesCount = parser.getAttributeCount(); 088 // handle namespace 089 if (withNamespace) { 090 String namespace = parser.getNamespace(); 091 if (StringUtils.isNotEmpty(namespace)) { 092 builder.append(" xmlns='").append(namespace).append('\''); 093 } 094 } 095 // handle attributes 096 for (int i = 0; i < attributesCount; ++i) { 097 builder.append(' '); 098 String attributeNamespace = parser.getAttributeNamespace(i); 099 if (StringUtils.isNotEmpty(attributeNamespace)) { 100 builder.append(attributeNamespace).append(':'); 101 } 102 builder.append(parser.getAttributeName(i)); 103 String value = parser.getAttributeValue(i); 104 if (value != null) { 105 // We need to return valid XML so any inner text needs to be re-escaped 106 builder.append("='").append(StringUtils.escapeForXML(value)).append('\''); 107 } 108 } 109 } 110 111 112 private static void appendEndTag(StringBuilder builder, XmlPullParser parser, boolean tagStarted) { 113 if (tagStarted) { 114 builder.append("/>"); 115 } else { 116 builder.append("</").append(parser.getName()).append('>'); 117 } 118 } 119 120 private static boolean appendText(StringBuilder builder, XmlPullParser parser) { 121 String text = parser.getText(); 122 if (text == null) { 123 return false; 124 } else { 125 // We need to return valid XML so any inner text needs to be re-escaped 126 builder.append(StringUtils.escapeForXML(parser.getText())); 127 return true; 128 } 129 } 130 131 private static boolean maybeCloseTag(boolean tagStarted, StringBuilder builder) { 132 if (tagStarted) { 133 builder.append('>'); 134 } 135 return false; 136 } 137}