private static Registration parseRegistration(XmlPullParser parser) throws Exception {
    Registration registration = new Registration();
    Map<String, String> fields = null;
    boolean done = false;
    while (!done) {
      int eventType = parser.next();
      if (eventType == XmlPullParser.START_TAG) {
        // Any element that's in the jabber:iq:register namespace,
        // attempt to parse it if it's in the form <name>value</name>.
        if (parser.getNamespace().equals("jabber:iq:register")) {
          String name = parser.getName();
          String value = "";
          if (fields == null) {
            fields = new HashMap<String, String>();
          }

          if (parser.next() == XmlPullParser.TEXT) {
            value = parser.getText();
          }
          // Ignore instructions, but anything else should be added to the map.
          if (!name.equals("instructions")) {
            fields.put(name, value);
          } else {
            registration.setInstructions(value);
          }
        }
        // Otherwise, it must be a packet extension.
        else {
          registration.addExtension(
              PacketParserUtils.parsePacketExtension(
                  parser.getName(), parser.getNamespace(), parser));
        }
      } else if (eventType == XmlPullParser.END_TAG) {
        if (parser.getName().equals("query")) {
          done = true;
        }
      }
    }
    registration.setAttributes(fields);
    return registration;
  }
  /**
   * Parses a message packet.
   *
   * @param parser the XML parser, positioned at the start of a message packet.
   * @return a Message packet.
   * @throws Exception if an exception occurs while parsing the packet.
   */
  public static Packet parseMessage(XmlPullParser parser) throws Exception {
    Message message = new Message();
    String id = parser.getAttributeValue("", "id");
    message.setPacketID(id == null ? Packet.ID_NOT_AVAILABLE : id);
    message.setTo(parser.getAttributeValue("", "to"));
    message.setFrom(parser.getAttributeValue("", "from"));
    message.setType(Message.Type.fromString(parser.getAttributeValue("", "type")));
    String language = getLanguageAttribute(parser);

    // determine message's default language
    String defaultLanguage = null;
    if (language != null && !"".equals(language.trim())) {
      message.setLanguage(language);
      defaultLanguage = language;
    } else {
      defaultLanguage = Packet.getDefaultLanguage();
    }

    // Parse sub-elements. We include extra logic to make sure the values
    // are only read once. This is because it's possible for the names to appear
    // in arbitrary sub-elements.
    boolean done = false;
    String thread = null;
    Map<String, Object> properties = null;
    while (!done) {
      int eventType = parser.next();
      if (eventType == XmlPullParser.START_TAG) {
        String elementName = parser.getName();
        String namespace = parser.getNamespace();
        if (elementName.equals("subject")) {
          String xmlLang = getLanguageAttribute(parser);
          if (xmlLang == null) {
            xmlLang = defaultLanguage;
          }

          String subject = parseContent(parser);

          if (message.getSubject(xmlLang) == null) {
            message.addSubject(xmlLang, subject);
          }
        } else if (elementName.equals("body")) {
          String xmlLang = getLanguageAttribute(parser);
          if (xmlLang == null) {
            xmlLang = defaultLanguage;
          }

          String body = parseContent(parser);

          if (message.getBody(xmlLang) == null) {
            message.addBody(xmlLang, body);
          }
        } else if (elementName.equals("thread")) {
          if (thread == null) {
            thread = parser.nextText();
          }
        } else if (elementName.equals("error")) {
          message.setError(parseError(parser));
        } else if (elementName.equals("properties") && namespace.equals(PROPERTIES_NAMESPACE)) {
          properties = parseProperties(parser);
        }
        // Otherwise, it must be a packet extension.
        else {
          message.addExtension(
              PacketParserUtils.parsePacketExtension(elementName, namespace, parser));
        }
      } else if (eventType == XmlPullParser.END_TAG) {
        if (parser.getName().equals("message")) {
          done = true;
        }
      }
    }

    message.setThread(thread);
    // Set packet properties.
    if (properties != null) {
      for (String name : properties.keySet()) {
        message.setProperty(name, properties.get(name));
      }
    }
    return message;
  }
  /**
   * Parses an IQ packet.
   *
   * @param parser the XML parser, positioned at the start of an IQ packet.
   * @return an IQ object.
   * @throws Exception if an exception occurs while parsing the packet.
   */
  public static IQ parseIQ(XmlPullParser parser, Connection connection) throws Exception {
    IQ iqPacket = null;

    String id = parser.getAttributeValue("", "id");
    String to = parser.getAttributeValue("", "to");
    String from = parser.getAttributeValue("", "from");
    IQ.Type type = IQ.Type.fromString(parser.getAttributeValue("", "type"));
    XMPPError error = null;

    boolean done = false;
    while (!done) {
      int eventType = parser.next();

      if (eventType == XmlPullParser.START_TAG) {
        String elementName = parser.getName();
        String namespace = parser.getNamespace();
        if (elementName.equals("error")) {
          error = PacketParserUtils.parseError(parser);
        } else if (elementName.equals("query") && namespace.equals("jabber:iq:auth")) {
          iqPacket = parseAuthentication(parser);
        } else if (elementName.equals("query") && namespace.equals("jabber:iq:roster")) {
          iqPacket = parseRoster(parser);
        } else if (elementName.equals("query") && namespace.equals("jabber:iq:register")) {
          iqPacket = parseRegistration(parser);
        } else if (elementName.equals("bind")
            && namespace.equals("urn:ietf:params:xml:ns:xmpp-bind")) {
          iqPacket = parseResourceBinding(parser);
        }
        // Otherwise, see if there is a registered provider for
        // this element name and namespace.
        else {
          Object provider = ProviderManager.getInstance().getIQProvider(elementName, namespace);
          if (provider != null) {
            if (provider instanceof IQProvider) {
              iqPacket = ((IQProvider) provider).parseIQ(parser);
            } else if (provider instanceof Class) {
              iqPacket =
                  (IQ)
                      PacketParserUtils.parseWithIntrospection(
                          elementName, (Class) provider, parser);
            }
          }
        }
      } else if (eventType == XmlPullParser.END_TAG) {
        if (parser.getName().equals("iq")) {
          done = true;
        }
      }
    }
    // Decide what to do when an IQ packet was not understood
    if (iqPacket == null) {
      if (IQ.Type.GET == type || IQ.Type.SET == type) {
        // If the IQ stanza is of type "get" or "set" containing a child element
        // qualified by a namespace it does not understand, then answer an IQ of
        // type "error" with code 501 ("feature-not-implemented")
        iqPacket =
            new IQ() {
              public String getChildElementXML() {
                return null;
              }
            };
        iqPacket.setPacketID(id);
        iqPacket.setTo(from);
        iqPacket.setFrom(to);
        iqPacket.setType(IQ.Type.ERROR);
        iqPacket.setError(new XMPPError(XMPPError.Condition.service_unavailable));
        connection.sendPacket(iqPacket);
        return null;
      } else {
        // If an IQ packet wasn't created above, create an empty IQ packet.
        iqPacket =
            new IQ() {
              public String getChildElementXML() {
                return null;
              }
            };
      }
    }

    // Set basic values on the iq packet.
    iqPacket.setPacketID(id);
    iqPacket.setTo(to);
    iqPacket.setFrom(from);
    iqPacket.setType(type);
    iqPacket.setError(error);

    return iqPacket;
  }
  /**
   * Parses a presence packet.
   *
   * @param parser the XML parser, positioned at the start of a presence packet.
   * @return a Presence packet.
   * @throws Exception if an exception occurs while parsing the packet.
   */
  public static Presence parsePresence(XmlPullParser parser) throws Exception {
    Presence.Type type = Presence.Type.available;
    String typeString = parser.getAttributeValue("", "type");
    if (typeString != null && !typeString.equals("")) {
      try {
        type = Presence.Type.valueOf(typeString);
      } catch (IllegalArgumentException iae) {
        LOGGER.log(Level.SEVERE, "Found invalid presence type " + typeString);
      }
    }
    Presence presence = new Presence(type);
    presence.setTo(parser.getAttributeValue("", "to"));
    presence.setFrom(parser.getAttributeValue("", "from"));
    String id = parser.getAttributeValue("", "id");
    presence.setPacketID(id == null ? Packet.ID_NOT_AVAILABLE : id);

    String language = getLanguageAttribute(parser);
    if (language != null && !"".equals(language.trim())) {
      presence.setLanguage(language);
    }
    presence.setPacketID(id == null ? Packet.ID_NOT_AVAILABLE : id);

    // Parse sub-elements
    boolean done = false;
    while (!done) {
      int eventType = parser.next();
      if (eventType == XmlPullParser.START_TAG) {
        String elementName = parser.getName();
        String namespace = parser.getNamespace();
        if (elementName.equals("status")) {
          try {
            presence.setStatus(parser.nextText());
          } catch (Throwable t) {
            LOGGER.log(Level.SEVERE, "Error parsing presence status", t);
          }
        } else if (elementName.equals("priority")) {
          try {
            int priority = Integer.parseInt(parser.nextText());
            presence.setPriority(priority);
          } catch (NumberFormatException nfe) {
            // Ignore.
          } catch (IllegalArgumentException iae) {
            // Presence priority is out of range so assume priority to be zero
            presence.setPriority(0);
          }
        } else if (elementName.equals("show")) {
          String modeText = parser.nextText();
          try {
            presence.setMode(Presence.Mode.valueOf(modeText));
          } catch (IllegalArgumentException iae) {
            LOGGER.log(Level.SEVERE, "Found invalid presence mode " + modeText);
          }
        } else if (elementName.equals("error")) {
          presence.setError(parseError(parser));
        } else if (elementName.equals("properties") && namespace.equals(PROPERTIES_NAMESPACE)) {
          Map<String, Object> properties = parseProperties(parser);
          // Set packet properties.
          for (String name : properties.keySet()) {
            presence.setProperty(name, properties.get(name));
          }
        }
        // Otherwise, it must be a packet extension.
        else {
          presence.addExtension(
              PacketParserUtils.parsePacketExtension(elementName, namespace, parser));
        }
      } else if (eventType == XmlPullParser.END_TAG) {
        if (parser.getName().equals("presence")) {
          done = true;
        }
      }
    }
    return presence;
  }