/**
   * Populates the object from the XMLStreamReader
   *
   * @param xmlr the XMLStreamReader to read from
   * @throws XMLStreamException if there is an error parsing the stream
   * @throws ParseException if there is an error in parsing a date
   * @throws URISyntaxException if the uri is invalid
   */
  protected static BlobContainerAttributes readBlobContainerAttributes(final XMLStreamReader xmlr)
      throws XMLStreamException, ParseException, URISyntaxException {
    int eventType = xmlr.getEventType();

    final BlobContainerAttributes attributes = new BlobContainerAttributes();

    while (xmlr.hasNext()) {
      eventType = xmlr.next();
      final String name = xmlr.getName().toString();
      if (eventType == XMLStreamConstants.START_ELEMENT) {
        if (name.equals(BlobConstants.PROPERTIES)) {
          attributes.setProperties(BlobDeserializationHelper.readBlobContainerProperties(xmlr));
          xmlr.require(XMLStreamConstants.END_ELEMENT, null, BlobConstants.PROPERTIES);
        } else if (name.equals(Constants.URL_ELEMENT)) {
          attributes.setUri(new URI(Utility.readElementFromXMLReader(xmlr, Constants.URL_ELEMENT)));
        } else if (name.equals(Constants.NAME_ELEMENT)) {
          attributes.setName(Utility.readElementFromXMLReader(xmlr, Constants.NAME_ELEMENT));
        } else if (name.equals(Constants.METADATA_ELEMENT)) {
          // parse metadata
          attributes.setMetadata(DeserializationHelper.parseMetadateFromXML(xmlr));
          xmlr.require(XMLStreamConstants.END_ELEMENT, null, Constants.METADATA_ELEMENT);
        }
      } else if (eventType == XMLStreamConstants.END_ELEMENT
          && name.equals(BlobConstants.CONTAINER_ELEMENT)) {
        break;
      }
    }

    return attributes;
  }
  /**
   * Reads BlobItems from the XMLStreamReader, reader must be at Start element of BlobsElement
   *
   * @param xmlr the XMLStreamReader to read from
   * @param serviceClient the CloudBlobClient associated with the objects.
   * @param container the container associated with the objects.
   * @return the BlobItems from the stream
   * @throws XMLStreamException if there is an error parsing the stream
   * @throws ParseException if there is an error in parsing a date
   * @throws URISyntaxException if the uri is invalid
   * @throws StorageException
   */
  public static ArrayList<ListBlobItem> readBlobItems(
      final XMLStreamReader xmlr,
      final CloudBlobClient serviceClient,
      final CloudBlobContainer container)
      throws XMLStreamException, ParseException, URISyntaxException, StorageException {
    int eventType = xmlr.getEventType();
    final ArrayList<ListBlobItem> retBlobs = new ArrayList<ListBlobItem>();

    xmlr.require(XMLStreamConstants.START_ELEMENT, null, BlobConstants.BLOBS_ELEMENT);

    // check if there are more events in the input stream
    while (xmlr.hasNext()) {
      eventType = xmlr.next();
      final String name = xmlr.getName().toString();

      if (eventType == XMLStreamConstants.START_ELEMENT) {
        if (name.equals(BlobConstants.BLOB_ELEMENT)) {
          retBlobs.add(BlobDeserializationHelper.readBlob(xmlr, serviceClient, container));
        } else if (name.equals(BlobConstants.BLOB_PREFIX_ELEMENT)) {
          retBlobs.add(BlobDeserializationHelper.readDirectory(xmlr, serviceClient, container));
        } else {
          throw new StorageException(
              StorageErrorCodeStrings.INVALID_XML_DOCUMENT,
              "The response received is invalid or improperly formatted.",
              Constants.HeaderConstants.HTTP_UNUSED_306,
              null,
              null);
        }
      } else {
        break;
      }
    }

    xmlr.require(XMLStreamConstants.END_ELEMENT, null, BlobConstants.BLOBS_ELEMENT);
    return retBlobs;
  }
  /**
   * Populates CloudBlobContainer objects from the XMLStreamReader, reader must be at Start element
   * of ContainersElement
   *
   * @param xmlr the XMLStreamReader object
   * @param serviceClient the CloudBlobClient associated with the objects.
   * @return an ArrayList of CloudBlobContainer from the stream.
   * @throws XMLStreamException if there is a parsing exception
   * @throws ParseException if a date value is not correctly encoded
   * @throws URISyntaxException
   * @throws StorageException
   */
  public static ArrayList<CloudBlobContainer> readContainers(
      final XMLStreamReader xmlr, final CloudBlobClient serviceClient)
      throws XMLStreamException, ParseException, URISyntaxException, StorageException {
    int eventType = xmlr.getEventType();
    xmlr.require(XMLStreamConstants.START_ELEMENT, null, BlobConstants.CONTAINERS_ELEMENT);

    final ArrayList<CloudBlobContainer> containers = new ArrayList<CloudBlobContainer>();

    eventType = xmlr.next();
    while (eventType == XMLStreamConstants.START_ELEMENT
        && xmlr.hasName()
        && BlobConstants.CONTAINER_ELEMENT.equals(xmlr.getName().toString())) {
      containers.add(BlobDeserializationHelper.readContainer(xmlr, serviceClient));
      eventType = xmlr.next();
    }

    xmlr.require(XMLStreamConstants.END_ELEMENT, null, BlobConstants.CONTAINERS_ELEMENT);
    return containers;
  }
  /**
   * Populates the container from an XMLStreamReader
   *
   * @param xmlr the XMLStreamReader to read from
   * @throws XMLStreamException if there is an error parsing the stream
   * @throws ParseException if there is an error in parsing a date
   * @throws URISyntaxException if the uri is invalid
   * @throws StorageException
   */
  protected static CloudBlobContainer readContainer(
      final XMLStreamReader xmlr, final CloudBlobClient serviceClient)
      throws XMLStreamException, ParseException, URISyntaxException, StorageException {

    xmlr.require(XMLStreamConstants.START_ELEMENT, null, BlobConstants.CONTAINER_ELEMENT);

    final BlobContainerAttributes attributes =
        BlobDeserializationHelper.readBlobContainerAttributes(xmlr);

    final CloudBlobContainer retContainer =
        new CloudBlobContainer(attributes.getUri(), serviceClient);
    retContainer.setMetadata(attributes.getMetadata());
    retContainer.setName(attributes.getName());
    retContainer.setProperties(attributes.getProperties());
    retContainer.setUri(attributes.getUri());

    xmlr.require(XMLStreamConstants.END_ELEMENT, null, BlobConstants.CONTAINER_ELEMENT);
    return retContainer;
  }
  /**
   * Reserved for internal use. Populates the blob from an XMLStreamReader, reader must be at Start
   * element of Blob
   *
   * @param xmlr the XMLStreamReader to read from
   * @param serviceClient the CloudBlobClient associated with the objects.
   * @throws XMLStreamException if there is an error parsing the stream
   * @throws ParseException if there is an error in parsing a date
   * @throws URISyntaxException if the uri is invalid
   * @throws StorageException
   */
  protected static CloudBlob readBlob(
      final XMLStreamReader xmlr,
      final CloudBlobClient serviceClient,
      final CloudBlobContainer container)
      throws XMLStreamException, ParseException, URISyntaxException, StorageException {
    xmlr.require(XMLStreamConstants.START_ELEMENT, null, BlobConstants.BLOB_ELEMENT);

    String blobName = Constants.EMPTY_STRING;

    String snapshotID = null;
    String urlString = null;
    HashMap<String, String> metadata = null;
    BlobProperties properties = null;
    CopyState copyState = null;

    int eventType = xmlr.getEventType();
    // check if there are more events in the input stream
    while (xmlr.hasNext()) {
      eventType = xmlr.next();
      final String name = xmlr.getName().toString();

      if (eventType == XMLStreamConstants.START_ELEMENT) {
        if (name.equals(Constants.URL_ELEMENT)) {
          urlString = Utility.readElementFromXMLReader(xmlr, Constants.URL_ELEMENT);
        } else if (name.equals(BlobConstants.SNAPSHOT_ELEMENT)) {
          snapshotID = Utility.readElementFromXMLReader(xmlr, BlobConstants.SNAPSHOT_ELEMENT);
        } else if (name.equals(Constants.NAME_ELEMENT)) {
          blobName = Utility.readElementFromXMLReader(xmlr, Constants.NAME_ELEMENT);
        } else if (name.equals(BlobConstants.PROPERTIES)) {
          properties = BlobDeserializationHelper.readBlobProperties(xmlr);
          xmlr.require(XMLStreamConstants.END_ELEMENT, null, BlobConstants.PROPERTIES);
        } else if (name.equals(Constants.METADATA_ELEMENT)) {
          metadata = DeserializationHelper.parseMetadateFromXML(xmlr);
          xmlr.require(XMLStreamConstants.END_ELEMENT, null, Constants.METADATA_ELEMENT);
        } else if (name.equals(Constants.COPY_ID_ELEMENT)) {
          if (copyState == null) {
            copyState = new CopyState();
          }
          copyState.setCopyId(Utility.readElementFromXMLReader(xmlr, Constants.COPY_ID_ELEMENT));
        } else if (name.equals(Constants.COPY_COMPLETION_TIME_ELEMENT)) {
          if (copyState == null) {
            copyState = new CopyState();
          }
          copyState.setCompletionTime(
              Utility.parseRFC1123DateFromStringInGMT(
                  Utility.readElementFromXMLReader(xmlr, Constants.COPY_COMPLETION_TIME_ELEMENT)));
        } else if (name.equals(Constants.COPY_STATUS_ELEMENT)) {
          if (copyState == null) {
            copyState = new CopyState();
          }
          copyState.setStatus(
              CopyStatus.parse(
                  Utility.readElementFromXMLReader(xmlr, Constants.COPY_STATUS_ELEMENT)));
        } else if (name.equals(Constants.COPY_SOURCE_ELEMENT)) {
          if (copyState == null) {
            copyState = new CopyState();
          }
          copyState.setSource(
              new URI(Utility.readElementFromXMLReader(xmlr, Constants.COPY_SOURCE_ELEMENT)));
        } else if (name.equals(Constants.COPY_PROGRESS_ELEMENT)) {
          if (copyState == null) {
            copyState = new CopyState();
          }

          final String tempString =
              Utility.readElementFromXMLReader(xmlr, Constants.COPY_PROGRESS_ELEMENT);
          String[] progressSequence = tempString.split("/");
          copyState.setBytesCopied(Long.parseLong(progressSequence[0]));
          copyState.setTotalBytes(Long.parseLong(progressSequence[1]));
        } else if (name.equals(Constants.COPY_STATUS_DESCRIPTION_ELEMENT)) {
          if (copyState == null) {
            copyState = new CopyState();
          }
          copyState.setStatusDescription(
              Utility.readElementFromXMLReader(xmlr, Constants.COPY_STATUS_DESCRIPTION_ELEMENT));
        }
      } else if (eventType == XMLStreamConstants.END_ELEMENT
          && name.equals(BlobConstants.BLOB_ELEMENT)) {
        break;
      }
    }

    xmlr.require(XMLStreamConstants.END_ELEMENT, null, BlobConstants.BLOB_ELEMENT);

    // Assemble and return
    if (properties != null) {
      CloudBlob retBlob = null;
      final int blobNameSectionIndex = urlString.lastIndexOf("/".concat(blobName));
      final URI baseUri = new URI(urlString.substring(0, blobNameSectionIndex + 1));
      String query = null;
      if (blobNameSectionIndex + 1 + blobName.length() < urlString.length()) {
        // Snapshot blob URI
        // example:http://<yourstorageaccount>.blob.core.windows.net/<yourcontainer>/<yourblobname>?snapshot=2009-12-03T15%3a26%3a19.4466877Z
        query = urlString.substring(blobNameSectionIndex + 1 + blobName.length() + 1);
      }

      final URI blobURI =
          new URI(
              baseUri.getScheme(),
              baseUri.getAuthority(),
              baseUri.getRawPath().concat(blobName),
              query,
              null);

      if (properties.getBlobType() == BlobType.BLOCK_BLOB) {
        retBlob = new CloudBlockBlob(blobURI, serviceClient, container);
      } else if (properties.getBlobType() == BlobType.PAGE_BLOB) {
        retBlob = new CloudPageBlob(blobURI, serviceClient, container);
      } else {
        throw new StorageException(
            StorageErrorCodeStrings.INVALID_XML_DOCUMENT,
            "The response received is invalid or improperly formatted.",
            Constants.HeaderConstants.HTTP_UNUSED_306,
            null,
            null);
      }

      retBlob.uri = blobURI;
      retBlob.snapshotID = snapshotID;
      retBlob.properties = properties;
      retBlob.metadata = metadata;
      retBlob.copyState = copyState;
      return retBlob;
    } else {
      throw new StorageException(
          StorageErrorCodeStrings.INVALID_XML_DOCUMENT,
          "The response received is invalid or improperly formatted.",
          Constants.HeaderConstants.HTTP_UNUSED_306,
          null,
          null);
    }
  }