001/** 002 * 003 * Copyright 2019-2022 Florian Schmaus 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.smack.provider; 018 019import java.io.IOException; 020import java.text.ParseException; 021 022import org.jivesoftware.smack.packet.IQ; 023import org.jivesoftware.smack.packet.IqData; 024import org.jivesoftware.smack.packet.XmlEnvironment; 025import org.jivesoftware.smack.parsing.SmackParsingException; 026import org.jivesoftware.smack.util.ParserUtils; 027import org.jivesoftware.smack.xml.XmlPullParser; 028import org.jivesoftware.smack.xml.XmlPullParserException; 029 030/** 031 * An abstract class for parsing custom {@link IQ} packets. Each IqProvider must be registered with the {@link 032 * ProviderManager} for it to be used. Every implementation of this abstract class <b>must</b> have a public, 033 * no-argument constructor. 034 * <h2>Custom IQ Provider Example</h2> 035 * <p> 036 * Let us assume you want to write a provider for a new, unsupported IQ in Smack. 037 * </p> 038 * <pre>{@code 039 * <iq type='set' from='juliet@capulet.example/balcony' to='romeo@montage.example'> 040 * <myiq xmlns='example:iq:foo' token='secret'> 041 * <user age='42'>John Doe</user> 042 * <location>New York</location> 043 * </myiq> 044 * </iq> 045 * }</pre> 046 * The custom IQ provider may look like the follows 047 * <pre>{@code 048 * public class MyIQProvider extends IQProvider<MyIQ> { 049 * 050 * {@literal @}Override 051 * public MyIQ parse(XmlPullParser parser, int initialDepth) throws XmlPullParserException, IOException { 052 * // Define the data we are trying to collect with sane defaults 053 * int age = -1; 054 * String user = null; 055 * String location = null; 056 * 057 * // Start parsing loop 058 * outerloop: while(true) { 059 * XmlPullParser.Event eventType = parser.next(); 060 * switch(eventType) { 061 * case START_ELEMENT: 062 * String elementName = parser.getName(); 063 * switch (elementName) { 064 * case "user": 065 * age = ParserUtils.getIntegerAttribute(parser, "age"); 066 * user = parser.nextText(); 067 * break; 068 * case "location" 069 * location = parser.nextText(); 070 * break; 071 * } 072 * break; 073 * case END_ELEMENT: 074 * // Abort condition: if the are on a end tag (closing element) of the same depth 075 * if (parser.getDepth() == initialDepth) { 076 * break outerloop; 077 * } 078 * break; 079 * default: 080 * // Catch all for incomplete switch (MissingCasesInEnumSwitch) statement. 081 * break; 082 * } 083 * } 084 * 085 * // Construct the IQ instance at the end of parsing, when all data has been collected 086 * return new MyIQ(user, age, location); 087 * } 088 * } 089 * }</pre> 090 * 091 * @param <I> the {@link IQ} that is parsed by implementations. 092 */ 093public abstract class IqProvider<I extends IQ> extends AbstractProvider<I> { 094 095 public final I parse(XmlPullParser parser, IqData iqCommon) 096 throws XmlPullParserException, IOException, SmackParsingException { 097 return parse(parser, iqCommon, null); 098 } 099 100 public final I parse(XmlPullParser parser, IqData iqData, XmlEnvironment outerXmlEnvironment) 101 throws XmlPullParserException, IOException, SmackParsingException { 102 final int initialDepth = parser.getDepth(); 103 final XmlEnvironment xmlEnvironment = XmlEnvironment.from(parser, outerXmlEnvironment); 104 105 I i = wrapExceptions(() -> parse(parser, initialDepth, iqData, xmlEnvironment)); 106 107 // Parser should be at end tag of the consumed/parsed element 108 ParserUtils.forwardToEndTagOfDepth(parser, initialDepth); 109 return i; 110 } 111 112 public abstract I parse(XmlPullParser parser, int initialDepth, IqData iqData, XmlEnvironment xmlEnvironment) 113 throws XmlPullParserException, IOException, SmackParsingException, ParseException; 114 115}