blob: aacbad551573f6f577d25bbeb3f558324be38564 [file] [log] [blame]
Shuyi Chend7955ce2013-05-22 14:51:55 -07001/**
2 * $RCSfile$
3 * $Revision$
4 * $Date$
5 *
6 * Copyright 2003-2007 Jive Software.
7 *
8 * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20
21package org.jivesoftware.smack.util;
22
23import java.io.ByteArrayInputStream;
24import java.io.IOException;
25import java.io.ObjectInputStream;
26import java.util.ArrayList;
27import java.util.Collection;
28import java.util.HashMap;
29import java.util.List;
30import java.util.Map;
31
32import org.jivesoftware.smack.Connection;
33import org.jivesoftware.smack.packet.Authentication;
34import org.jivesoftware.smack.packet.Bind;
35import org.jivesoftware.smack.packet.DefaultPacketExtension;
36import org.jivesoftware.smack.packet.IQ;
37import org.jivesoftware.smack.packet.Message;
38import org.jivesoftware.smack.packet.Packet;
39import org.jivesoftware.smack.packet.PacketExtension;
40import org.jivesoftware.smack.packet.Presence;
41import org.jivesoftware.smack.packet.Registration;
42import org.jivesoftware.smack.packet.RosterPacket;
43import org.jivesoftware.smack.packet.StreamError;
44import org.jivesoftware.smack.packet.XMPPError;
45import org.jivesoftware.smack.provider.IQProvider;
46import org.jivesoftware.smack.provider.PacketExtensionProvider;
47import org.jivesoftware.smack.provider.ProviderManager;
48import org.jivesoftware.smack.sasl.SASLMechanism.Failure;
49import org.xmlpull.v1.XmlPullParser;
50import org.xmlpull.v1.XmlPullParserException;
51
52/**
53 * Utility class that helps to parse packets. Any parsing packets method that must be shared
54 * between many clients must be placed in this utility class.
55 *
56 * @author Gaston Dombiak
57 */
58public class PacketParserUtils {
59
60 /**
61 * Namespace used to store packet properties.
62 */
63 private static final String PROPERTIES_NAMESPACE =
64 "http://www.jivesoftware.com/xmlns/xmpp/properties";
65
66 /**
67 * Parses a message packet.
68 *
69 * @param parser the XML parser, positioned at the start of a message packet.
70 * @return a Message packet.
71 * @throws Exception if an exception occurs while parsing the packet.
72 */
73 public static Packet parseMessage(XmlPullParser parser) throws Exception {
74 Message message = new Message();
75 String id = parser.getAttributeValue("", "id");
76 message.setPacketID(id == null ? Packet.ID_NOT_AVAILABLE : id);
77 message.setTo(parser.getAttributeValue("", "to"));
78 message.setFrom(parser.getAttributeValue("", "from"));
79 message.setType(Message.Type.fromString(parser.getAttributeValue("", "type")));
80 String language = getLanguageAttribute(parser);
81
82 // determine message's default language
83 String defaultLanguage = null;
84 if (language != null && !"".equals(language.trim())) {
85 message.setLanguage(language);
86 defaultLanguage = language;
87 }
88 else {
89 defaultLanguage = Packet.getDefaultLanguage();
90 }
91
92 // Parse sub-elements. We include extra logic to make sure the values
93 // are only read once. This is because it's possible for the names to appear
94 // in arbitrary sub-elements.
95 boolean done = false;
96 String thread = null;
97 Map<String, Object> properties = null;
98 while (!done) {
99 int eventType = parser.next();
100 if (eventType == XmlPullParser.START_TAG) {
101 String elementName = parser.getName();
102 String namespace = parser.getNamespace();
103 if (elementName.equals("subject")) {
104 String xmlLang = getLanguageAttribute(parser);
105 if (xmlLang == null) {
106 xmlLang = defaultLanguage;
107 }
108
109 String subject = parseContent(parser);
110
111 if (message.getSubject(xmlLang) == null) {
112 message.addSubject(xmlLang, subject);
113 }
114 }
115 else if (elementName.equals("body")) {
116 String xmlLang = getLanguageAttribute(parser);
117 if (xmlLang == null) {
118 xmlLang = defaultLanguage;
119 }
120
121 String body = parseContent(parser);
122
123 if (message.getBody(xmlLang) == null) {
124 message.addBody(xmlLang, body);
125 }
126 }
127 else if (elementName.equals("thread")) {
128 if (thread == null) {
129 thread = parser.nextText();
130 }
131 }
132 else if (elementName.equals("error")) {
133 message.setError(parseError(parser));
134 }
135 else if (elementName.equals("properties") &&
136 namespace.equals(PROPERTIES_NAMESPACE))
137 {
138 properties = parseProperties(parser);
139 }
140 // Otherwise, it must be a packet extension.
141 else {
142 message.addExtension(
143 PacketParserUtils.parsePacketExtension(elementName, namespace, parser));
144 }
145 }
146 else if (eventType == XmlPullParser.END_TAG) {
147 if (parser.getName().equals("message")) {
148 done = true;
149 }
150 }
151 }
152
153 message.setThread(thread);
154 // Set packet properties.
155 if (properties != null) {
156 for (String name : properties.keySet()) {
157 message.setProperty(name, properties.get(name));
158 }
159 }
160 return message;
161 }
162
163 /**
164 * Returns the content of a tag as string regardless of any tags included.
165 *
166 * @param parser the XML pull parser
167 * @return the content of a tag as string
168 * @throws XmlPullParserException if parser encounters invalid XML
169 * @throws IOException if an IO error occurs
170 */
171 private static String parseContent(XmlPullParser parser)
172 throws XmlPullParserException, IOException {
173 StringBuffer content = new StringBuffer();
174 int parserDepth = parser.getDepth();
175 while (!(parser.next() == XmlPullParser.END_TAG && parser
176 .getDepth() == parserDepth)) {
177 content.append(parser.getText());
178 }
179 return content.toString();
180 }
181
182 /**
183 * Parses a presence packet.
184 *
185 * @param parser the XML parser, positioned at the start of a presence packet.
186 * @return a Presence packet.
187 * @throws Exception if an exception occurs while parsing the packet.
188 */
189 public static Presence parsePresence(XmlPullParser parser) throws Exception {
190 Presence.Type type = Presence.Type.available;
191 String typeString = parser.getAttributeValue("", "type");
192 if (typeString != null && !typeString.equals("")) {
193 try {
194 type = Presence.Type.valueOf(typeString);
195 }
196 catch (IllegalArgumentException iae) {
197 System.err.println("Found invalid presence type " + typeString);
198 }
199 }
200 Presence presence = new Presence(type);
201 presence.setTo(parser.getAttributeValue("", "to"));
202 presence.setFrom(parser.getAttributeValue("", "from"));
203 String id = parser.getAttributeValue("", "id");
204 presence.setPacketID(id == null ? Packet.ID_NOT_AVAILABLE : id);
205
206 String language = getLanguageAttribute(parser);
207 if (language != null && !"".equals(language.trim())) {
208 presence.setLanguage(language);
209 }
210 presence.setPacketID(id == null ? Packet.ID_NOT_AVAILABLE : id);
211
212 // Parse sub-elements
213 boolean done = false;
214 while (!done) {
215 int eventType = parser.next();
216 if (eventType == XmlPullParser.START_TAG) {
217 String elementName = parser.getName();
218 String namespace = parser.getNamespace();
219 if (elementName.equals("status")) {
220 presence.setStatus(parser.nextText());
221 }
222 else if (elementName.equals("priority")) {
223 try {
224 int priority = Integer.parseInt(parser.nextText());
225 presence.setPriority(priority);
226 }
227 catch (NumberFormatException nfe) {
228 // Ignore.
229 }
230 catch (IllegalArgumentException iae) {
231 // Presence priority is out of range so assume priority to be zero
232 presence.setPriority(0);
233 }
234 }
235 else if (elementName.equals("show")) {
236 String modeText = parser.nextText();
237 try {
238 presence.setMode(Presence.Mode.valueOf(modeText));
239 }
240 catch (IllegalArgumentException iae) {
241 System.err.println("Found invalid presence mode " + modeText);
242 }
243 }
244 else if (elementName.equals("error")) {
245 presence.setError(parseError(parser));
246 }
247 else if (elementName.equals("properties") &&
248 namespace.equals(PROPERTIES_NAMESPACE))
249 {
250 Map<String,Object> properties = parseProperties(parser);
251 // Set packet properties.
252 for (String name : properties.keySet()) {
253 presence.setProperty(name, properties.get(name));
254 }
255 }
256 // Otherwise, it must be a packet extension.
257 else {
258 try {
259 presence.addExtension(PacketParserUtils.parsePacketExtension(elementName, namespace, parser));
260 }
261 catch (Exception e) {
262 System.err.println("Failed to parse extension packet in Presence packet.");
263 }
264 }
265 }
266 else if (eventType == XmlPullParser.END_TAG) {
267 if (parser.getName().equals("presence")) {
268 done = true;
269 }
270 }
271 }
272 return presence;
273 }
274
275 /**
276 * Parses an IQ packet.
277 *
278 * @param parser the XML parser, positioned at the start of an IQ packet.
279 * @return an IQ object.
280 * @throws Exception if an exception occurs while parsing the packet.
281 */
282 public static IQ parseIQ(XmlPullParser parser, Connection connection) throws Exception {
283 IQ iqPacket = null;
284
285 String id = parser.getAttributeValue("", "id");
286 String to = parser.getAttributeValue("", "to");
287 String from = parser.getAttributeValue("", "from");
288 IQ.Type type = IQ.Type.fromString(parser.getAttributeValue("", "type"));
289 XMPPError error = null;
290
291 boolean done = false;
292 while (!done) {
293 int eventType = parser.next();
294
295 if (eventType == XmlPullParser.START_TAG) {
296 String elementName = parser.getName();
297 String namespace = parser.getNamespace();
298 if (elementName.equals("error")) {
299 error = PacketParserUtils.parseError(parser);
300 }
301 else if (elementName.equals("query") && namespace.equals("jabber:iq:auth")) {
302 iqPacket = parseAuthentication(parser);
303 }
304 else if (elementName.equals("query") && namespace.equals("jabber:iq:roster")) {
305 iqPacket = parseRoster(parser);
306 }
307 else if (elementName.equals("query") && namespace.equals("jabber:iq:register")) {
308 iqPacket = parseRegistration(parser);
309 }
310 else if (elementName.equals("bind") &&
311 namespace.equals("urn:ietf:params:xml:ns:xmpp-bind")) {
312 iqPacket = parseResourceBinding(parser);
313 }
314 // Otherwise, see if there is a registered provider for
315 // this element name and namespace.
316 else {
317 Object provider = ProviderManager.getInstance().getIQProvider(elementName, namespace);
318 if (provider != null) {
319 if (provider instanceof IQProvider) {
320 iqPacket = ((IQProvider)provider).parseIQ(parser);
321 }
322 else if (provider instanceof Class) {
323 iqPacket = (IQ)PacketParserUtils.parseWithIntrospection(elementName,
324 (Class<?>)provider, parser);
325 }
326 }
327 // Only handle unknown IQs of type result. Types of 'get' and 'set' which are not understood
328 // have to be answered with an IQ error response. See the code a few lines below
329 else if (IQ.Type.RESULT == type){
330 // No Provider found for the IQ stanza, parse it to an UnparsedIQ instance
331 // so that the content of the IQ can be examined later on
332 iqPacket = new UnparsedResultIQ(parseContent(parser));
333 }
334 }
335 }
336 else if (eventType == XmlPullParser.END_TAG) {
337 if (parser.getName().equals("iq")) {
338 done = true;
339 }
340 }
341 }
342 // Decide what to do when an IQ packet was not understood
343 if (iqPacket == null) {
344 if (IQ.Type.GET == type || IQ.Type.SET == type ) {
345 // If the IQ stanza is of type "get" or "set" containing a child element
346 // qualified by a namespace it does not understand, then answer an IQ of
347 // type "error" with code 501 ("feature-not-implemented")
348 iqPacket = new IQ() {
349 @Override
350 public String getChildElementXML() {
351 return null;
352 }
353 };
354 iqPacket.setPacketID(id);
355 iqPacket.setTo(from);
356 iqPacket.setFrom(to);
357 iqPacket.setType(IQ.Type.ERROR);
358 iqPacket.setError(new XMPPError(XMPPError.Condition.feature_not_implemented));
359 connection.sendPacket(iqPacket);
360 return null;
361 }
362 else {
363 // If an IQ packet wasn't created above, create an empty IQ packet.
364 iqPacket = new IQ() {
365 @Override
366 public String getChildElementXML() {
367 return null;
368 }
369 };
370 }
371 }
372
373 // Set basic values on the iq packet.
374 iqPacket.setPacketID(id);
375 iqPacket.setTo(to);
376 iqPacket.setFrom(from);
377 iqPacket.setType(type);
378 iqPacket.setError(error);
379
380 return iqPacket;
381 }
382
383 private static Authentication parseAuthentication(XmlPullParser parser) throws Exception {
384 Authentication authentication = new Authentication();
385 boolean done = false;
386 while (!done) {
387 int eventType = parser.next();
388 if (eventType == XmlPullParser.START_TAG) {
389 if (parser.getName().equals("username")) {
390 authentication.setUsername(parser.nextText());
391 }
392 else if (parser.getName().equals("password")) {
393 authentication.setPassword(parser.nextText());
394 }
395 else if (parser.getName().equals("digest")) {
396 authentication.setDigest(parser.nextText());
397 }
398 else if (parser.getName().equals("resource")) {
399 authentication.setResource(parser.nextText());
400 }
401 }
402 else if (eventType == XmlPullParser.END_TAG) {
403 if (parser.getName().equals("query")) {
404 done = true;
405 }
406 }
407 }
408 return authentication;
409 }
410
411 private static RosterPacket parseRoster(XmlPullParser parser) throws Exception {
412 RosterPacket roster = new RosterPacket();
413 boolean done = false;
414 RosterPacket.Item item = null;
415 while (!done) {
416 if(parser.getEventType()==XmlPullParser.START_TAG &&
417 parser.getName().equals("query")){
418 String version = parser.getAttributeValue(null, "ver");
419 roster.setVersion(version);
420 }
421 int eventType = parser.next();
422 if (eventType == XmlPullParser.START_TAG) {
423 if (parser.getName().equals("item")) {
424 String jid = parser.getAttributeValue("", "jid");
425 String name = parser.getAttributeValue("", "name");
426 // Create packet.
427 item = new RosterPacket.Item(jid, name);
428 // Set status.
429 String ask = parser.getAttributeValue("", "ask");
430 RosterPacket.ItemStatus status = RosterPacket.ItemStatus.fromString(ask);
431 item.setItemStatus(status);
432 // Set type.
433 String subscription = parser.getAttributeValue("", "subscription");
434 RosterPacket.ItemType type = RosterPacket.ItemType.valueOf(subscription != null ? subscription : "none");
435 item.setItemType(type);
436 }
437 if (parser.getName().equals("group") && item!= null) {
438 final String groupName = parser.nextText();
439 if (groupName != null && groupName.trim().length() > 0) {
440 item.addGroupName(groupName);
441 }
442 }
443 }
444 else if (eventType == XmlPullParser.END_TAG) {
445 if (parser.getName().equals("item")) {
446 roster.addRosterItem(item);
447 }
448 if (parser.getName().equals("query")) {
449 done = true;
450 }
451 }
452 }
453 return roster;
454 }
455
456 private static Registration parseRegistration(XmlPullParser parser) throws Exception {
457 Registration registration = new Registration();
458 Map<String, String> fields = null;
459 boolean done = false;
460 while (!done) {
461 int eventType = parser.next();
462 if (eventType == XmlPullParser.START_TAG) {
463 // Any element that's in the jabber:iq:register namespace,
464 // attempt to parse it if it's in the form <name>value</name>.
465 if (parser.getNamespace().equals("jabber:iq:register")) {
466 String name = parser.getName();
467 String value = "";
468 if (fields == null) {
469 fields = new HashMap<String, String>();
470 }
471
472 if (parser.next() == XmlPullParser.TEXT) {
473 value = parser.getText();
474 }
475 // Ignore instructions, but anything else should be added to the map.
476 if (!name.equals("instructions")) {
477 fields.put(name, value);
478 }
479 else {
480 registration.setInstructions(value);
481 }
482 }
483 // Otherwise, it must be a packet extension.
484 else {
485 registration.addExtension(
486 PacketParserUtils.parsePacketExtension(
487 parser.getName(),
488 parser.getNamespace(),
489 parser));
490 }
491 }
492 else if (eventType == XmlPullParser.END_TAG) {
493 if (parser.getName().equals("query")) {
494 done = true;
495 }
496 }
497 }
498 registration.setAttributes(fields);
499 return registration;
500 }
501
502 private static Bind parseResourceBinding(XmlPullParser parser) throws IOException,
503 XmlPullParserException {
504 Bind bind = new Bind();
505 boolean done = false;
506 while (!done) {
507 int eventType = parser.next();
508 if (eventType == XmlPullParser.START_TAG) {
509 if (parser.getName().equals("resource")) {
510 bind.setResource(parser.nextText());
511 }
512 else if (parser.getName().equals("jid")) {
513 bind.setJid(parser.nextText());
514 }
515 } else if (eventType == XmlPullParser.END_TAG) {
516 if (parser.getName().equals("bind")) {
517 done = true;
518 }
519 }
520 }
521
522 return bind;
523 }
524
525 /**
526 * Parse the available SASL mechanisms reported from the server.
527 *
528 * @param parser the XML parser, positioned at the start of the mechanisms stanza.
529 * @return a collection of Stings with the mechanisms included in the mechanisms stanza.
530 * @throws Exception if an exception occurs while parsing the stanza.
531 */
532 public static Collection<String> parseMechanisms(XmlPullParser parser) throws Exception {
533 List<String> mechanisms = new ArrayList<String>();
534 boolean done = false;
535 while (!done) {
536 int eventType = parser.next();
537
538 if (eventType == XmlPullParser.START_TAG) {
539 String elementName = parser.getName();
540 if (elementName.equals("mechanism")) {
541 mechanisms.add(parser.nextText());
542 }
543 }
544 else if (eventType == XmlPullParser.END_TAG) {
545 if (parser.getName().equals("mechanisms")) {
546 done = true;
547 }
548 }
549 }
550 return mechanisms;
551 }
552
553 /**
554 * Parse the available compression methods reported from the server.
555 *
556 * @param parser the XML parser, positioned at the start of the compression stanza.
557 * @return a collection of Stings with the methods included in the compression stanza.
558 * @throws Exception if an exception occurs while parsing the stanza.
559 */
560 public static Collection<String> parseCompressionMethods(XmlPullParser parser)
561 throws IOException, XmlPullParserException {
562 List<String> methods = new ArrayList<String>();
563 boolean done = false;
564 while (!done) {
565 int eventType = parser.next();
566
567 if (eventType == XmlPullParser.START_TAG) {
568 String elementName = parser.getName();
569 if (elementName.equals("method")) {
570 methods.add(parser.nextText());
571 }
572 }
573 else if (eventType == XmlPullParser.END_TAG) {
574 if (parser.getName().equals("compression")) {
575 done = true;
576 }
577 }
578 }
579 return methods;
580 }
581
582 /**
583 * Parse a properties sub-packet. If any errors occur while de-serializing Java object
584 * properties, an exception will be printed and not thrown since a thrown
585 * exception will shut down the entire connection. ClassCastExceptions will occur
586 * when both the sender and receiver of the packet don't have identical versions
587 * of the same class.
588 *
589 * @param parser the XML parser, positioned at the start of a properties sub-packet.
590 * @return a map of the properties.
591 * @throws Exception if an error occurs while parsing the properties.
592 */
593 public static Map<String, Object> parseProperties(XmlPullParser parser) throws Exception {
594 Map<String, Object> properties = new HashMap<String, Object>();
595 while (true) {
596 int eventType = parser.next();
597 if (eventType == XmlPullParser.START_TAG && parser.getName().equals("property")) {
598 // Parse a property
599 boolean done = false;
600 String name = null;
601 String type = null;
602 String valueText = null;
603 Object value = null;
604 while (!done) {
605 eventType = parser.next();
606 if (eventType == XmlPullParser.START_TAG) {
607 String elementName = parser.getName();
608 if (elementName.equals("name")) {
609 name = parser.nextText();
610 }
611 else if (elementName.equals("value")) {
612 type = parser.getAttributeValue("", "type");
613 valueText = parser.nextText();
614 }
615 }
616 else if (eventType == XmlPullParser.END_TAG) {
617 if (parser.getName().equals("property")) {
618 if ("integer".equals(type)) {
619 value = Integer.valueOf(valueText);
620 }
621 else if ("long".equals(type)) {
622 value = Long.valueOf(valueText);
623 }
624 else if ("float".equals(type)) {
625 value = Float.valueOf(valueText);
626 }
627 else if ("double".equals(type)) {
628 value = Double.valueOf(valueText);
629 }
630 else if ("boolean".equals(type)) {
631 value = Boolean.valueOf(valueText);
632 }
633 else if ("string".equals(type)) {
634 value = valueText;
635 }
636 else if ("java-object".equals(type)) {
637 try {
638 byte [] bytes = StringUtils.decodeBase64(valueText);
639 ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bytes));
640 value = in.readObject();
641 }
642 catch (Exception e) {
643 e.printStackTrace();
644 }
645 }
646 if (name != null && value != null) {
647 properties.put(name, value);
648 }
649 done = true;
650 }
651 }
652 }
653 }
654 else if (eventType == XmlPullParser.END_TAG) {
655 if (parser.getName().equals("properties")) {
656 break;
657 }
658 }
659 }
660 return properties;
661 }
662
663 /**
664 * Parses SASL authentication error packets.
665 *
666 * @param parser the XML parser.
667 * @return a SASL Failure packet.
668 * @throws Exception if an exception occurs while parsing the packet.
669 */
670 public static Failure parseSASLFailure(XmlPullParser parser) throws Exception {
671 String condition = null;
672 boolean done = false;
673 while (!done) {
674 int eventType = parser.next();
675
676 if (eventType == XmlPullParser.START_TAG) {
677 if (!parser.getName().equals("failure")) {
678 condition = parser.getName();
679 }
680 }
681 else if (eventType == XmlPullParser.END_TAG) {
682 if (parser.getName().equals("failure")) {
683 done = true;
684 }
685 }
686 }
687 return new Failure(condition);
688 }
689
690 /**
691 * Parses stream error packets.
692 *
693 * @param parser the XML parser.
694 * @return an stream error packet.
695 * @throws Exception if an exception occurs while parsing the packet.
696 */
697 public static StreamError parseStreamError(XmlPullParser parser) throws IOException,
698 XmlPullParserException {
699 StreamError streamError = null;
700 boolean done = false;
701 while (!done) {
702 int eventType = parser.next();
703
704 if (eventType == XmlPullParser.START_TAG) {
705 streamError = new StreamError(parser.getName());
706 }
707 else if (eventType == XmlPullParser.END_TAG) {
708 if (parser.getName().equals("error")) {
709 done = true;
710 }
711 }
712 }
713 return streamError;
714}
715
716 /**
717 * Parses error sub-packets.
718 *
719 * @param parser the XML parser.
720 * @return an error sub-packet.
721 * @throws Exception if an exception occurs while parsing the packet.
722 */
723 public static XMPPError parseError(XmlPullParser parser) throws Exception {
724 final String errorNamespace = "urn:ietf:params:xml:ns:xmpp-stanzas";
725 String errorCode = "-1";
726 String type = null;
727 String message = null;
728 String condition = null;
729 List<PacketExtension> extensions = new ArrayList<PacketExtension>();
730
731 // Parse the error header
732 for (int i=0; i<parser.getAttributeCount(); i++) {
733 if (parser.getAttributeName(i).equals("code")) {
734 errorCode = parser.getAttributeValue("", "code");
735 }
736 if (parser.getAttributeName(i).equals("type")) {
737 type = parser.getAttributeValue("", "type");
738 }
739 }
740 boolean done = false;
741 // Parse the text and condition tags
742 while (!done) {
743 int eventType = parser.next();
744 if (eventType == XmlPullParser.START_TAG) {
745 if (parser.getName().equals("text")) {
746 message = parser.nextText();
747 }
748 else {
749 // Condition tag, it can be xmpp error or an application defined error.
750 String elementName = parser.getName();
751 String namespace = parser.getNamespace();
752 if (errorNamespace.equals(namespace)) {
753 condition = elementName;
754 }
755 else {
756 extensions.add(parsePacketExtension(elementName, namespace, parser));
757 }
758 }
759 }
760 else if (eventType == XmlPullParser.END_TAG) {
761 if (parser.getName().equals("error")) {
762 done = true;
763 }
764 }
765 }
766 // Parse the error type.
767 XMPPError.Type errorType = XMPPError.Type.CANCEL;
768 try {
769 if (type != null) {
770 errorType = XMPPError.Type.valueOf(type.toUpperCase());
771 }
772 }
773 catch (IllegalArgumentException iae) {
774 // Print stack trace. We shouldn't be getting an illegal error type.
775 iae.printStackTrace();
776 }
777 return new XMPPError(Integer.parseInt(errorCode), errorType, condition, message, extensions);
778 }
779
780 /**
781 * Parses a packet extension sub-packet.
782 *
783 * @param elementName the XML element name of the packet extension.
784 * @param namespace the XML namespace of the packet extension.
785 * @param parser the XML parser, positioned at the starting element of the extension.
786 * @return a PacketExtension.
787 * @throws Exception if a parsing error occurs.
788 */
789 public static PacketExtension parsePacketExtension(String elementName, String namespace, XmlPullParser parser)
790 throws Exception
791 {
792 // See if a provider is registered to handle the extension.
793 Object provider = ProviderManager.getInstance().getExtensionProvider(elementName, namespace);
794 if (provider != null) {
795 if (provider instanceof PacketExtensionProvider) {
796 return ((PacketExtensionProvider)provider).parseExtension(parser);
797 }
798 else if (provider instanceof Class) {
799 return (PacketExtension)parseWithIntrospection(
800 elementName, (Class<?>)provider, parser);
801 }
802 }
803 // No providers registered, so use a default extension.
804 DefaultPacketExtension extension = new DefaultPacketExtension(elementName, namespace);
805 boolean done = false;
806 while (!done) {
807 int eventType = parser.next();
808 if (eventType == XmlPullParser.START_TAG) {
809 String name = parser.getName();
810 // If an empty element, set the value with the empty string.
811 if (parser.isEmptyElementTag()) {
812 extension.setValue(name,"");
813 }
814 // Otherwise, get the the element text.
815 else {
816 eventType = parser.next();
817 if (eventType == XmlPullParser.TEXT) {
818 String value = parser.getText();
819 extension.setValue(name, value);
820 }
821 }
822 }
823 else if (eventType == XmlPullParser.END_TAG) {
824 if (parser.getName().equals(elementName)) {
825 done = true;
826 }
827 }
828 }
829 return extension;
830 }
831
832 private static String getLanguageAttribute(XmlPullParser parser) {
833 for (int i = 0; i < parser.getAttributeCount(); i++) {
834 String attributeName = parser.getAttributeName(i);
835 if ( "xml:lang".equals(attributeName) ||
836 ("lang".equals(attributeName) &&
837 "xml".equals(parser.getAttributePrefix(i)))) {
838 return parser.getAttributeValue(i);
839 }
840 }
841 return null;
842 }
843
844 public static Object parseWithIntrospection(String elementName,
845 Class<?> objectClass, XmlPullParser parser) throws Exception
846 {
847 boolean done = false;
848 Object object = objectClass.newInstance();
849 while (!done) {
850 int eventType = parser.next();
851 if (eventType == XmlPullParser.START_TAG) {
852 String name = parser.getName();
853 String stringValue = parser.nextText();
854 Class propertyType = object.getClass().getMethod(
855 "get" + Character.toUpperCase(name.charAt(0)) + name.substring(1)).getReturnType();
856 // Get the value of the property by converting it from a
857 // String to the correct object type.
858 Object value = decode(propertyType, stringValue);
859 // Set the value of the bean.
860 object.getClass().getMethod("set" + Character.toUpperCase(name.charAt(0)) + name.substring(1), propertyType)
861 .invoke(object, value);
862 }
863 else if (eventType == XmlPullParser.END_TAG) {
864 if (parser.getName().equals(elementName)) {
865 done = true;
866 }
867 }
868 }
869 return object;
870 }
871
872 /**
873 * Decodes a String into an object of the specified type. If the object
874 * type is not supported, null will be returned.
875 *
876 * @param type the type of the property.
877 * @param value the encode String value to decode.
878 * @return the String value decoded into the specified type.
879 * @throws Exception If decoding failed due to an error.
880 */
881 private static Object decode(Class<?> type, String value) throws Exception {
882 if (type.getName().equals("java.lang.String")) {
883 return value;
884 }
885 if (type.getName().equals("boolean")) {
886 return Boolean.valueOf(value);
887 }
888 if (type.getName().equals("int")) {
889 return Integer.valueOf(value);
890 }
891 if (type.getName().equals("long")) {
892 return Long.valueOf(value);
893 }
894 if (type.getName().equals("float")) {
895 return Float.valueOf(value);
896 }
897 if (type.getName().equals("double")) {
898 return Double.valueOf(value);
899 }
900 if (type.getName().equals("java.lang.Class")) {
901 return Class.forName(value);
902 }
903 return null;
904 }
905
906 /**
907 * This class represents and unparsed IQ of the type 'result'. Usually it's created when no IQProvider
908 * was found for the IQ element.
909 *
910 * The child elements can be examined with the getChildElementXML() method.
911 *
912 */
913 public static class UnparsedResultIQ extends IQ {
914 public UnparsedResultIQ(String content) {
915 this.str = content;
916 }
917
918 private final String str;
919
920 @Override
921 public String getChildElementXML() {
922 return this.str;
923 }
924 }
925}