/**
   * 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;
  }
  /**
   * Reserved for internal use. Perform a merge operation on the specified table, using the
   * specified {@link TableRequestOptions} and {@link OperationContext}.
   *
   * <p>This method will invoke the Merge Entity REST API to execute this table operation, using the
   * Table service endpoint and storage account credentials in the {@link CloudTableClient} object.
   *
   * @param client A {@link CloudTableClient} instance specifying the Table service endpoint,
   *     storage account credentials, and any additional query parameters.
   * @param tableName A <code>String</code> containing the name of the table.
   * @param options A {@link TableRequestOptions} object that specifies execution options such as
   *     retry policy and timeout settings for the operation.
   * @param opContext An {@link OperationContext} object for tracking the current operation.
   * @return A {@link TableResult} containing the results of executing the operation.
   * @throws StorageException if an error occurs in the storage operation.
   */
  private TableResult performMerge(
      final CloudTableClient client,
      final String tableName,
      final TableRequestOptions options,
      final OperationContext opContext)
      throws StorageException {
    Utility.assertNotNullOrEmpty("Merge requires a valid ETag", this.getEntity().getEtag());
    Utility.assertNotNullOrEmpty(
        "Merge requires a valid PartitionKey", this.getEntity().getPartitionKey());
    Utility.assertNotNullOrEmpty("Merge requires a valid RowKey", this.getEntity().getRowKey());

    final StorageOperation<CloudTableClient, TableOperation, TableResult> impl =
        new StorageOperation<CloudTableClient, TableOperation, TableResult>(options) {
          @Override
          public TableResult execute(
              final CloudTableClient client,
              final TableOperation operation,
              final OperationContext opContext)
              throws Exception {

            final HttpURLConnection request =
                TableRequest.merge(
                    client.getTransformedEndPoint(opContext),
                    tableName,
                    generateRequestIdentity(false, null, false),
                    operation.getEntity().getEtag(),
                    options.getTimeoutIntervalInMs(),
                    null,
                    options,
                    opContext);

            client.getCredentials().signRequestLite(request, -1L, opContext);

            AtomPubParser.writeSingleEntityToStream(
                operation.getEntity(), false, request.getOutputStream(), opContext);

            this.setResult(ExecutionEngine.processRequest(request, opContext));

            if (this.getResult().getStatusCode() == HttpURLConnection.HTTP_NOT_FOUND
                || this.getResult().getStatusCode() == HttpURLConnection.HTTP_CONFLICT) {
              throw TableServiceException.generateTableServiceException(
                  false, this.getResult(), operation, request.getErrorStream());
            }

            if (this.getResult().getStatusCode() == HttpURLConnection.HTTP_NO_CONTENT) {
              return operation.parseResponse(
                  null,
                  this.getResult().getStatusCode(),
                  request.getHeaderField(TableConstants.HeaderConstants.ETAG),
                  opContext);
            } else {
              throw TableServiceException.generateTableServiceException(
                  true, this.getResult(), operation, request.getErrorStream());
            }
          }
        };

    return ExecutionEngine.executeWithRetry(
        client, this, impl, options.getRetryPolicyFactory(), opContext);
  }
  /**
   * Reserved for internal use. Generates the request identity, consisting of the specified entry
   * name, or the PartitionKey and RowKey pair from the operation, to identify the operation target.
   *
   * @param isSingleIndexEntry Pass <code>true</code> to use the specified <code>entryName</code>
   *     parameter, or <code>false</code> to use PartitionKey and RowKey values from the operation
   *     as the request identity.
   * @param entryName The entry name to use as the request identity if the <code>isSingleIndexEntry
   *     </code> parameter is <code>true</code>.
   * @param encodeKeys Pass <code>true</code> to url encode the partition & row keys
   * @return A <code>String</code> containing the formatted request identity string.
   * @throws StorageException If a storage service error occurred.
   */
  protected String generateRequestIdentity(
      boolean isSingleIndexEntry, final String entryName, boolean encodeKeys)
      throws StorageException {
    if (isSingleIndexEntry) {
      return String.format("'%s'", entryName);
    }

    if (this.opType == TableOperationType.INSERT) {
      return Constants.EMPTY_STRING;
    } else {
      String pk = null;
      String rk = null;

      if (this.opType == TableOperationType.RETRIEVE) {
        final QueryTableOperation qOp = (QueryTableOperation) this;
        pk = qOp.getPartitionKey();
        rk = qOp.getRowKey();
      } else {
        pk = this.getEntity().getPartitionKey();
        rk = this.getEntity().getRowKey();
      }

      return String.format(
          "%s='%s',%s='%s'",
          TableConstants.PARTITION_KEY,
          encodeKeys ? Utility.safeEncode(pk) : pk,
          TableConstants.ROW_KEY,
          encodeKeys ? Utility.safeEncode(rk) : rk);
    }
  }
  /**
   * Reads PageRanges from the XMLStreamReader, reader must be at Start element of PageRangeElement
   *
   * @param xmlr the XMLStreamReader to read from
   * @return the PageRange 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<PageRange> readPageRanges(final XMLStreamReader xmlr)
      throws XMLStreamException, StorageException {
    int eventType = xmlr.getEventType();
    final ArrayList<PageRange> retRanges = new ArrayList<PageRange>();

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

    // check if there are more events in the input stream
    while (xmlr.hasNext() && BlobConstants.PAGE_RANGE_ELEMENT.equals(xmlr.getName().toString())) {
      long startOffset = -1;
      long endOffset = -1;

      // Read a Page Range
      while (xmlr.hasNext()) {
        eventType = xmlr.next();
        final String name = xmlr.getName().toString();

        if (eventType == XMLStreamConstants.START_ELEMENT) {
          if (name.equals(BlobConstants.START_ELEMENT)) {
            final String sizeString =
                Utility.readElementFromXMLReader(xmlr, BlobConstants.START_ELEMENT);
            startOffset = Long.parseLong(sizeString);
          } else if (name.equals(Constants.END_ELEMENT)) {
            final String sizeString = Utility.readElementFromXMLReader(xmlr, Constants.END_ELEMENT);
            endOffset = Long.parseLong(sizeString);
          } else {
            throw new StorageException(
                StorageErrorCodeStrings.INVALID_XML_DOCUMENT,
                "The response received is invalid or improperly formatted.",
                Constants.HeaderConstants.HTTP_UNUSED_306,
                null,
                null);
          }
        } else if (eventType == XMLStreamConstants.END_ELEMENT) {
          if (startOffset == -1 || endOffset == -1) {
            throw new StorageException(
                StorageErrorCodeStrings.INVALID_XML_DOCUMENT,
                "The response received is invalid or improperly formatted.",
                Constants.HeaderConstants.HTTP_UNUSED_306,
                null,
                null);
          }

          final PageRange pageRef = new PageRange(startOffset, endOffset);
          retRanges.add(pageRef);
          break;
        }
      }

      eventType = xmlr.next();
    }

    return retRanges;
  }
  /**
   * Parses the input stream containing the response body of the list queues request result and
   * populates the class data.
   *
   * @param serviceClient A {@link CloudQueueClient} object associated with the storage service.
   * @throws XMLStreamException If the input stream cannot be read or parsed as a list queues
   *     response.
   * @throws StorageException
   */
  public void parseResponse(final CloudQueueClient serviceClient)
      throws XMLStreamException, StorageException {
    final XMLStreamReader xmlr = Utility.createXMLStreamReaderFromStream(this.streamRef);
    String tempParseString = null;

    // Start document
    int eventType = xmlr.getEventType();
    xmlr.require(XMLStreamConstants.START_DOCUMENT, null, null);

    // 1. get enumerationResults Header
    eventType = xmlr.next();
    xmlr.require(XMLStreamConstants.START_ELEMENT, null, "EnumerationResults");

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

      if (eventType == XMLStreamConstants.START_ELEMENT
          || eventType == XMLStreamConstants.END_ELEMENT) {
        final String name = xmlr.getName().toString();
        if (eventType == XMLStreamConstants.START_ELEMENT
            && name.equals(Constants.MAX_RESULTS_ELEMENT)) {
          tempParseString = Utility.readElementFromXMLReader(xmlr, Constants.MAX_RESULTS_ELEMENT);
          this.maxResults = Integer.parseInt(tempParseString);
        } else if (eventType == XMLStreamConstants.START_ELEMENT
            && name.equals(Constants.MARKER_ELEMENT)) {
          this.marker = Utility.readElementFromXMLReader(xmlr, Constants.MARKER_ELEMENT);
        } else if (eventType == XMLStreamConstants.START_ELEMENT
            && name.equals(Constants.NEXT_MARKER_ELEMENT)) {
          this.nextMarker = Utility.readElementFromXMLReader(xmlr, Constants.NEXT_MARKER_ELEMENT);
        } else if (eventType == XMLStreamConstants.START_ELEMENT
            && name.equals(Constants.PREFIX_ELEMENT)) {
          this.prefix = Utility.readElementFromXMLReader(xmlr, Constants.PREFIX_ELEMENT);
        } else if (eventType == XMLStreamConstants.START_ELEMENT
            && name.equals(QueueConstants.QUEUES_ELEMENT)) {
          try {
            this.queues = QueueDeserializationHelper.readQueues(xmlr, serviceClient);
          } catch (final URISyntaxException e) {
            throw new XMLStreamException(e);
          } catch (final ParseException e) {
            throw new XMLStreamException(e);
          }

          xmlr.require(XMLStreamConstants.END_ELEMENT, null, QueueConstants.QUEUES_ELEMENT);
          // eventType = xmlr.next();
        } else if (eventType == XMLStreamConstants.END_ELEMENT
            && "EnumerationResults".equals(name)) {
          break;
        }
      }
    }

    this.isParsed = true;
  }
  /**
   * Reserved for internal use. Execute this table operation on the specified table, using the
   * specified {@link TableRequestOptions} and {@link OperationContext}.
   *
   * <p>This method will invoke the Storage Service REST API to execute this table operation, using
   * the Table service endpoint and storage account credentials in the {@link CloudTableClient}
   * object.
   *
   * @param client A {@link CloudTableClient} instance specifying the Table service endpoint,
   *     storage account credentials, and any additional query parameters.
   * @param tableName A <code>String</code> containing the name of the table.
   * @param options A {@link TableRequestOptions} object that specifies execution options such as
   *     retry policy and timeout settings for the operation.
   * @param opContext An {@link OperationContext} object for tracking the current operation.
   * @return A {@link TableResult} containing the results of executing the operation.
   * @throws StorageException if an error occurs in the storage operation.
   */
  protected TableResult execute(
      final CloudTableClient client,
      final String tableName,
      TableRequestOptions options,
      OperationContext opContext)
      throws StorageException {
    if (opContext == null) {
      opContext = new OperationContext();
    }

    if (options == null) {
      options = new TableRequestOptions();
    }

    opContext.initialize();
    options.applyDefaults(client);
    Utility.assertNotNullOrEmpty("TableName", tableName);

    if (this.getOperationType() == TableOperationType.INSERT
        || this.getOperationType() == TableOperationType.INSERT_OR_MERGE
        || this.getOperationType() == TableOperationType.INSERT_OR_REPLACE) {
      return this.performInsert(client, tableName, options, opContext);
    } else if (this.getOperationType() == TableOperationType.DELETE) {
      return this.performDelete(client, tableName, options, opContext);
    } else if (this.getOperationType() == TableOperationType.MERGE) {
      return this.performMerge(client, tableName, options, opContext);
    } else if (this.getOperationType() == TableOperationType.REPLACE) {
      return this.performUpdate(client, tableName, options, opContext);
    } else if (this.getOperationType() == TableOperationType.RETRIEVE) {
      return ((QueryTableOperation) this).performRetrieve(client, tableName, options, opContext);
    } else {
      throw new IllegalArgumentException("Unknown table operation");
    }
  }
  /**
   * Reads BlobItems from the XMLStreamReader, reader must be at Start element of BlobsElement
   *
   * @param xmlr the XMLStreamReader to read from
   * @param searchMode the block search mode
   * @return BlockEntry 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<BlockEntry> readBlobBlocks(
      final XMLStreamReader xmlr, final BlockSearchMode searchMode)
      throws XMLStreamException, StorageException {
    int eventType = xmlr.getEventType();
    final ArrayList<BlockEntry> retBlocks = new ArrayList<BlockEntry>();

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

    // check if there are more events in the input stream
    while (xmlr.hasNext() && BlobConstants.BLOCK_ELEMENT.equals(xmlr.getName().toString())) {
      String blockName = null;
      long blockSize = -1;

      // Read a block
      while (xmlr.hasNext()) {
        eventType = xmlr.next();
        final String name = xmlr.getName().toString();

        if (eventType == XMLStreamConstants.START_ELEMENT) {
          if (name.equals(Constants.NAME_ELEMENT)) {
            blockName = Utility.readElementFromXMLReader(xmlr, Constants.NAME_ELEMENT);
          } else if (name.equals(BlobConstants.SIZE_ELEMENT)) {
            final String sizeString =
                Utility.readElementFromXMLReader(xmlr, BlobConstants.SIZE_ELEMENT);
            blockSize = Long.parseLong(sizeString);
          } else {
            throw new StorageException(
                StorageErrorCodeStrings.INVALID_XML_DOCUMENT,
                "The response received is invalid or improperly formatted.",
                Constants.HeaderConstants.HTTP_UNUSED_306,
                null,
                null);
          }
        } else if (eventType == XMLStreamConstants.END_ELEMENT) {
          final BlockEntry newBlock = new BlockEntry(blockName, searchMode);
          newBlock.setSize(blockSize);
          retBlocks.add(newBlock);
          break;
        }
      }

      eventType = xmlr.next();
    }

    return retBlocks;
  }
 /**
  * Uploads a new configuration of service properties to the storage service, using the specified
  * request options and operation context. This includes Metrics and Logging Configuration.
  *
  * @param properties The {@link ServiceProperties} to upload.
  * @param options A {@link RequestOptions} object that specifies any additional options for the
  *     request. Specifying <code>null</code> will use the default request options from the
  *     associated service client ( {@link CloudBlobClient}{@link CloudQueueClient}).
  * @param opContext An {@link OperationContext} object that represents the context for the current
  *     operation. This object is used to track requests to the storage service, and to provide
  *     additional runtime information about the operation.
  * @throws StorageException If a storage service error occurred during the operation.
  */
 @DoesServiceRequest
 @Override
 public void uploadServiceProperties(
     final ServiceProperties properties,
     final RequestOptions options,
     final OperationContext opContext)
     throws StorageException {
   if (!Utility.isNullOrEmpty(properties.getDefaultServiceVersion())) {
     throw new IllegalArgumentException(
         "DefaultServiceVersion can only be set for the Blob service and the request must be made using the 2011-08-18 version");
   }
   super.uploadServiceProperties(properties, options, opContext);
 }
  /**
   * Gets a result segment containing a collection of queues from the storage service with the
   * specified parameters.
   *
   * @param prefix A <code>String</code> containing the queue name prefix to filter the results
   *     with.
   * @param detailsIncluded A {@link QueueListingDetails} value that indicates whether queue
   *     metadata will be returned.
   * @param maxResults The maximum number of queue results to retrieve.
   * @param continuationToken A {@link ResultContinuation} object that represents a continuation
   *     token returned by a previous listing operation.
   * @param options A {@link QueueRequestOptions} object that specifies any additional options for
   *     the request. Specifying <code>null</code> will use the default request options from the
   *     associated service client ( {@link CloudQueue}).
   * @param taskReference A {@link StorageOperation} reference to the encapsulating task.
   * @param opContext An {@link OperationContext} object that represents the context for the current
   *     operation. This object is used to track requests to the storage service, and to provide
   *     additional runtime information about the operation.
   * @return A {@link ResultSegment} of {@link CloudQueue} objects that contains a segment of the
   *     iterable collection of {@link CloudQueue} objects that represent the requested queues in
   *     the storage service.
   * @throws IOException
   * @throws URISyntaxException If the URI is not valid.
   * @throws XMLStreamException
   * @throws InvalidKeyException
   * @throws StorageException If a storage service error occurred during the operation.
   */
  @DoesServiceRequest
  ResultSegment<CloudQueue> listQueuesCore(
      final String prefix,
      final QueueListingDetails detailsIncluded,
      final int maxResults,
      final ResultContinuation continuationToken,
      final RequestOptions options,
      final StorageOperation<CloudQueueClient, Void, ResultSegment<CloudQueue>> taskReference,
      final OperationContext opContext)
      throws IOException, URISyntaxException, XMLStreamException, InvalidKeyException,
          StorageException {

    Utility.assertContinuationType(continuationToken, ResultContinuationType.QUEUE);

    final ListingContext listingContext = new ListingContext(prefix, maxResults);
    listingContext.setMarker(continuationToken != null ? continuationToken.getNextMarker() : null);

    final HttpURLConnection listQueueRequest =
        QueueRequest.list(
            this.getEndpoint(),
            options.getTimeoutIntervalInMs(),
            listingContext,
            detailsIncluded,
            opContext);

    this.getCredentials().signRequest(listQueueRequest, -1L);

    taskReference.setResult(ExecutionEngine.processRequest(listQueueRequest, opContext));

    if (taskReference.getResult().getStatusCode() != HttpURLConnection.HTTP_OK) {
      taskReference.setNonExceptionedRetryableFailure(true);
      return null;
    }

    final ListQueuesResponse response = new ListQueuesResponse(listQueueRequest.getInputStream());
    response.parseResponse(this);

    ResultContinuation newToken = null;

    if (response.getNextMarker() != null) {
      newToken = new ResultContinuation();
      newToken.setNextMarker(response.getNextMarker());
      newToken.setContinuationType(ResultContinuationType.QUEUE);
    }

    final ResultSegment<CloudQueue> resSegment =
        new ResultSegment<CloudQueue>(response.getQueues(this), maxResults, newToken);

    return resSegment;
  }
  /**
   * Populates the CloudBlobDirectory from an XMLStreamReader, reader must be at Start element of
   * BlobPrefix
   *
   * @param xmlr the XMLStreamReader to read from
   * @param serviceClient the CloudBlobClient associated with the objects.
   * @param container the container associated with the objects.
   * @return a CloudBlobDirectory parsed 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
   */
  protected static CloudBlobDirectory readDirectory(
      final XMLStreamReader xmlr,
      final CloudBlobClient serviceClient,
      final CloudBlobContainer container)
      throws XMLStreamException, ParseException, URISyntaxException, StorageException {
    xmlr.require(XMLStreamConstants.START_ELEMENT, null, BlobConstants.BLOB_PREFIX_ELEMENT);

    // Move to Name element
    xmlr.next();
    xmlr.require(XMLStreamConstants.START_ELEMENT, null, Constants.NAME_ELEMENT);

    final String prefixName = Utility.readElementFromXMLReader(xmlr, Constants.NAME_ELEMENT);

    // Move from End name element to end prefix element
    xmlr.next();
    xmlr.require(XMLStreamConstants.END_ELEMENT, null, BlobConstants.BLOB_PREFIX_ELEMENT);

    return container.getDirectoryReference(prefixName);
  }
  /**
   * Gets a result segment of an iterable collection of queues whose names begin with the specified
   * prefix for this queue, using the specified listing details options, request options, and
   * operation context.
   *
   * @param prefix A <code>String</code> that represents the prefix of the queue name to match.
   * @param detailsIncluded A {@link QueueListingDetails} value that indicates whether queue
   *     metadata will be returned.
   * @param maxResults The maximum number of queue results to retrieve.
   * @param continuationToken A {@link ResultContinuation} object that represents a continuation
   *     token returned by a previous listing operation.
   * @param options A {@link QueueRequestOptions} object that specifies any additional options for
   *     the request. Specifying <code>null</code> will use the default request options from the
   *     associated service client ( {@link CloudQueue}).
   * @param opContext An {@link OperationContext} object that represents the context for the current
   *     operation. This object is used to track requests to the storage service, and to provide
   *     additional runtime information about the operation.
   * @return A {@link ResultSegment} of {@link CloudQueue} objects that contains a segment of the
   *     iterable collection of {@link CloudQueue} objects that represent the requested queues in
   *     the storage service.
   * @throws StorageException If a storage service error occurred during the operation.
   */
  @DoesServiceRequest
  public ResultSegment<CloudQueue> listQueuesSegmented(
      final String prefix,
      final QueueListingDetails detailsIncluded,
      final int maxResults,
      final ResultContinuation continuationToken,
      QueueRequestOptions options,
      OperationContext opContext)
      throws StorageException {

    if (opContext == null) {
      opContext = new OperationContext();
    }

    if (options == null) {
      options = new QueueRequestOptions();
    }

    opContext.initialize();
    options.applyDefaults(this);

    Utility.assertContinuationType(continuationToken, ResultContinuationType.QUEUE);

    final StorageOperation<CloudQueueClient, Void, ResultSegment<CloudQueue>> impl =
        new StorageOperation<CloudQueueClient, Void, ResultSegment<CloudQueue>>(options) {
          @Override
          public ResultSegment<CloudQueue> execute(
              final CloudQueueClient client, final Void dontCare, final OperationContext opContext)
              throws Exception {
            return CloudQueueClient.this.listQueuesCore(
                prefix,
                detailsIncluded,
                maxResults,
                continuationToken,
                this.getRequestOptions(),
                this,
                opContext);
          }
        };

    return ExecutionEngine.executeWithRetry(
        this, null, impl, options.getRetryPolicyFactory(), opContext);
  }
  /**
   * Writes a Block List and returns the corresponding UTF8 bytes.
   *
   * @param blockList the Iterable of BlockEntry to write
   * @param opContext a tracking object for the request
   * @return a byte array of the UTF8 bytes representing the serialized block list.
   * @throws XMLStreamException if there is an error writing the block list.
   * @throws StorageException
   */
  public static byte[] writeBlockListToStream(
      final Iterable<BlockEntry> blockList, final OperationContext opContext)
      throws XMLStreamException, StorageException {

    final StringWriter outWriter = new StringWriter();
    final XMLOutputFactory xmlOutFactoryInst = XMLOutputFactory.newInstance();
    final XMLStreamWriter xmlw = xmlOutFactoryInst.createXMLStreamWriter(outWriter);

    // default is UTF8
    xmlw.writeStartDocument();
    xmlw.writeStartElement(BlobConstants.BLOCK_LIST_ELEMENT);

    for (final BlockEntry block : blockList) {
      if (block.searchMode == BlockSearchMode.COMMITTED) {
        xmlw.writeStartElement(BlobConstants.COMMITTED_ELEMENT);
      } else if (block.searchMode == BlockSearchMode.UNCOMMITTED) {
        xmlw.writeStartElement(BlobConstants.UNCOMMITTED_ELEMENT);
      } else if (block.searchMode == BlockSearchMode.LATEST) {
        xmlw.writeStartElement(BlobConstants.LATEST_ELEMENT);
      }

      xmlw.writeCharacters(block.getId());
      xmlw.writeEndElement();
    }

    // end BlockListElement
    xmlw.writeEndElement();

    // end doc
    xmlw.writeEndDocument();
    try {
      return outWriter.toString().getBytes("UTF8");
    } catch (final UnsupportedEncodingException e) {
      throw Utility.generateNewUnexpectedStorageException(e);
    }
  }
  /**
   * Populates the object from the XMLStreamReader, reader must be at Start element of Properties
   *
   * @param xmlr the XMLStreamReader object
   * @throws XMLStreamException if there is a parsing exception
   * @throws ParseException if a date value is not correctly encoded
   */
  protected static BlobContainerProperties readBlobContainerProperties(final XMLStreamReader xmlr)
      throws XMLStreamException, ParseException {
    xmlr.require(XMLStreamConstants.START_ELEMENT, null, BlobConstants.PROPERTIES);
    int eventType = xmlr.getEventType();
    final BlobContainerProperties properties = new BlobContainerProperties();

    while (xmlr.hasNext()) {
      eventType = xmlr.next();
      final String name = xmlr.getName().toString();
      if (eventType == XMLStreamConstants.START_ELEMENT) {
        if (name.equals(Constants.LAST_MODIFIED_ELEMENT)) {
          properties.setLastModified(
              Utility.parseRFC1123DateFromStringInGMT(
                  Utility.readElementFromXMLReader(xmlr, Constants.LAST_MODIFIED_ELEMENT)));
        } else if (name.equals(Constants.ETAG_ELEMENT)) {
          properties.setEtag(Utility.readElementFromXMLReader(xmlr, Constants.ETAG_ELEMENT));
        } else if (name.equals(Constants.LEASE_STATUS_ELEMENT)) {
          properties.setLeaseStatus(
              LeaseStatus.parse(
                  Utility.readElementFromXMLReader(xmlr, Constants.LEASE_STATUS_ELEMENT)));
        } else if (name.equals(Constants.LEASE_STATE_ELEMENT)) {
          properties.setLeaseState(
              LeaseState.parse(
                  Utility.readElementFromXMLReader(xmlr, Constants.LEASE_STATE_ELEMENT)));
        } else if (name.equals(Constants.LEASE_DURATION_ELEMENT)) {
          properties.setLeaseDuration(
              LeaseDuration.parse(
                  Utility.readElementFromXMLReader(xmlr, Constants.LEASE_DURATION_ELEMENT)));
        }
      } else {
        // expect end of properties
        xmlr.require(XMLStreamConstants.END_ELEMENT, null, BlobConstants.PROPERTIES);
        break;
      }
    }

    return properties;
  }
 /**
  * A static factory method returning a {@link TableOperation} instance to replace the specified
  * entity in Windows Azure storage, or insert it if it does not exist. To execute this {@link
  * TableOperation} on a given table, call the {@link CloudTableClient#execute(String,
  * TableOperation)} method on a {@link CloudTableClient} instance with the table name and the
  * {@link TableOperation} as arguments.
  *
  * @param entity The object instance implementing {@link TableEntity} to associate with the
  *     operation.
  * @return A new {@link TableOperation} instance for inserting or replacing the table entity.
  */
 public static TableOperation insertOrReplace(final TableEntity entity) {
   Utility.assertNotNull("Entity", entity);
   return new TableOperation(entity, TableOperationType.INSERT_OR_REPLACE);
 }
 /**
  * A static factory method returning a {@link TableOperation} instance to merge the specified
  * entity into Windows Azure storage, or insert it if it does not exist. To execute this {@link
  * TableOperation} on a given table, call the {@link CloudTableClient#execute(String,
  * TableOperation)} method on a {@link CloudTableClient} instance with the table name and the
  * {@link TableOperation} as arguments.
  *
  * @param entity The object instance implementing {@link TableEntity} to associate with the
  *     operation.
  * @return A new {@link TableOperation} instance for inserting or merging the table entity.
  */
 public static TableOperation insertOrMerge(final TableEntity entity) {
   Utility.assertNotNull("Entity", entity);
   return new TableOperation(entity, TableOperationType.INSERT_OR_MERGE);
 }
 /**
  * A static factory method returning a {@link TableOperation} instance to delete the specified
  * entity from Windows Azure storage. To execute this {@link TableOperation} on a given table,
  * call the {@link CloudTableClient#execute(String, TableOperation)} method on a {@link
  * CloudTableClient} instance with the table name and the {@link TableOperation} as arguments.
  *
  * @param entity The object instance implementing {@link TableEntity} to associate with the
  *     operation.
  * @return A new {@link TableOperation} instance to insert the table entity.
  */
 public static TableOperation delete(final TableEntity entity) {
   Utility.assertNotNull("Entity", entity);
   Utility.assertNotNullOrEmpty("Entity Etag", entity.getEtag());
   return new TableOperation(entity, TableOperationType.DELETE);
 }
  /**
   * Reserved for internal use. Performs an insert operation on the specified table, using the
   * specified {@link TableRequestOptions} and {@link OperationContext}.
   *
   * <p>This method will invoke the Insert Entity REST API to execute this table operation, using
   * the Table service endpoint and storage account credentials in the {@link CloudTableClient}
   * object.
   *
   * @param client A {@link CloudTableClient} instance specifying the Table service endpoint,
   *     storage account credentials, and any additional query parameters.
   * @param tableName A <code>String</code> containing the name of the table.
   * @param options A {@link TableRequestOptions} object that specifies execution options such as
   *     retry policy and timeout settings for the operation.
   * @param opContext An {@link OperationContext} object for tracking the current operation.
   * @return A {@link TableResult} containing the results of executing the operation.
   * @throws StorageException if an error occurs in the storage operation.
   */
  private TableResult performInsert(
      final CloudTableClient client,
      final String tableName,
      final TableRequestOptions options,
      final OperationContext opContext)
      throws StorageException {
    final boolean isTableEntry = TableConstants.TABLES_SERVICE_TABLES_NAME.equals(tableName);
    final String tableIdentity =
        isTableEntry
            ? this.getEntity()
                .writeEntity(opContext)
                .get(TableConstants.TABLE_NAME)
                .getValueAsString()
            : null;

    // Upserts need row key and partition key
    if (!isTableEntry && this.opType != TableOperationType.INSERT) {
      Utility.assertNotNullOrEmpty(
          "Upserts require a valid PartitionKey", this.getEntity().getPartitionKey());
      Utility.assertNotNullOrEmpty("Upserts require a valid RowKey", this.getEntity().getRowKey());
    }

    final StorageOperation<CloudTableClient, TableOperation, TableResult> impl =
        new StorageOperation<CloudTableClient, TableOperation, TableResult>(options) {
          @Override
          public TableResult execute(
              final CloudTableClient client,
              final TableOperation operation,
              final OperationContext opContext)
              throws Exception {
            final HttpURLConnection request =
                TableRequest.insert(
                    client.getTransformedEndPoint(opContext),
                    tableName,
                    generateRequestIdentity(isTableEntry, tableIdentity, false),
                    operation.opType != TableOperationType.INSERT
                        ? operation.getEntity().getEtag()
                        : null,
                    operation.opType.getUpdateType(),
                    options.getTimeoutIntervalInMs(),
                    null,
                    options,
                    opContext);

            client.getCredentials().signRequestLite(request, -1L, opContext);

            AtomPubParser.writeSingleEntityToStream(
                operation.getEntity(), isTableEntry, request.getOutputStream(), opContext);

            this.setResult(ExecutionEngine.processRequest(request, opContext));
            if (operation.opType == TableOperationType.INSERT) {
              if (this.getResult().getStatusCode() == HttpURLConnection.HTTP_CONFLICT) {
                throw TableServiceException.generateTableServiceException(
                    false, this.getResult(), operation, request.getErrorStream());
              }

              if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_CREATED) {
                throw TableServiceException.generateTableServiceException(
                    true, this.getResult(), operation, request.getErrorStream());
              }

              InputStream inStream = request.getInputStream();
              TableResult res = null;

              try {
                final XMLStreamReader xmlr = Utility.createXMLStreamReaderFromStream(inStream);
                res =
                    operation.parseResponse(
                        xmlr, this.getResult().getStatusCode(), null, opContext);
              } finally {
                inStream.close();
              }

              return res;
            } else {
              if (this.getResult().getStatusCode() == HttpURLConnection.HTTP_NO_CONTENT) {
                return operation.parseResponse(
                    null,
                    this.getResult().getStatusCode(),
                    request.getHeaderField(TableConstants.HeaderConstants.ETAG),
                    opContext);
              } else {
                throw TableServiceException.generateTableServiceException(
                    true, this.getResult(), operation, request.getErrorStream());
              }
            }
          }
        };

    return ExecutionEngine.executeWithRetry(
        client, this, impl, options.getRetryPolicyFactory(), opContext);
  }
  /**
   * Populates the object from the XMLStreamReader, reader must be at Start element of Properties
   *
   * @param xmlr the XMLStreamReader object
   * @return the BlobProperties that was read.
   * @throws XMLStreamException if there is a parsing exception
   * @throws ParseException if a date value is not correctly encoded
   * @throws StorageException
   * @throws URISyntaxException
   */
  protected static BlobProperties readBlobProperties(final XMLStreamReader xmlr)
      throws XMLStreamException, ParseException, StorageException, URISyntaxException {
    xmlr.require(XMLStreamConstants.START_ELEMENT, null, BlobConstants.PROPERTIES);
    int eventType = xmlr.getEventType();
    final BlobProperties properties = new BlobProperties();

    while (xmlr.hasNext()) {
      eventType = xmlr.next();
      final String name = xmlr.getName().toString();

      if (eventType == XMLStreamConstants.START_ELEMENT) {
        if (name.equals(Constants.LAST_MODIFIED_ELEMENT)) {
          properties.setLastModified(
              Utility.parseRFC1123DateFromStringInGMT(
                  Utility.readElementFromXMLReader(xmlr, Constants.LAST_MODIFIED_ELEMENT)));
        } else if (name.equals(Constants.ETAG_ELEMENT)) {
          properties.setEtag(Utility.readElementFromXMLReader(xmlr, Constants.ETAG_ELEMENT));
        } else if (name.equals(Constants.HeaderConstants.CONTENT_LENGTH)) {
          final String tempString =
              Utility.readElementFromXMLReader(xmlr, Constants.HeaderConstants.CONTENT_LENGTH);
          properties.setLength(Long.parseLong(tempString));
        } else if (name.equals(Constants.HeaderConstants.CONTENT_TYPE)) {
          properties.setContentType(
              Utility.readElementFromXMLReader(xmlr, Constants.HeaderConstants.CONTENT_TYPE));
        } else if (name.equals(Constants.HeaderConstants.CONTENT_ENCODING)) {
          properties.setContentEncoding(
              Utility.readElementFromXMLReader(xmlr, Constants.HeaderConstants.CONTENT_ENCODING));
        } else if (name.equals(Constants.HeaderConstants.CONTENT_LANGUAGE)) {
          properties.setContentLanguage(
              Utility.readElementFromXMLReader(xmlr, Constants.HeaderConstants.CONTENT_LANGUAGE));
        } else if (name.equals(Constants.HeaderConstants.CONTENT_MD5)) {
          properties.setContentMD5(
              Utility.readElementFromXMLReader(xmlr, Constants.HeaderConstants.CONTENT_MD5));
        } else if (name.equals(Constants.HeaderConstants.CACHE_CONTROL)) {
          properties.setCacheControl(
              Utility.readElementFromXMLReader(xmlr, Constants.HeaderConstants.CACHE_CONTROL));
        } else if (name.equals(Constants.HeaderConstants.CACHE_CONTROL)) {
          properties.setCacheControl(
              Utility.readElementFromXMLReader(xmlr, Constants.HeaderConstants.CACHE_CONTROL));
        } else if (name.equals(BlobConstants.SEQUENCE_NUMBER)) {
          Utility.readElementFromXMLReader(xmlr, BlobConstants.SEQUENCE_NUMBER);
        } else if (name.equals(BlobConstants.BLOB_TYPE_ELEMENT)) {
          final String tempString =
              Utility.readElementFromXMLReader(xmlr, BlobConstants.BLOB_TYPE_ELEMENT);
          if (tempString.equals(BlobConstants.BLOCK_BLOB_VALUE)) {
            properties.setBlobType(BlobType.BLOCK_BLOB);
          } else if (tempString.equals(BlobConstants.PAGE_BLOB_VALUE)) {
            properties.setBlobType(BlobType.PAGE_BLOB);
          } else {
            throw new StorageException(
                StorageErrorCodeStrings.INVALID_XML_DOCUMENT,
                "The response received is invalid or improperly formatted.",
                Constants.HeaderConstants.HTTP_UNUSED_306,
                null,
                null);
          }
        } else if (name.equals(Constants.LEASE_STATUS_ELEMENT)) {
          final String tempString =
              Utility.readElementFromXMLReader(xmlr, Constants.LEASE_STATUS_ELEMENT);
          if (tempString.equals(Constants.LOCKED_VALUE.toLowerCase())) {
            properties.setLeaseStatus(LeaseStatus.LOCKED);
          } else if (tempString.equals(Constants.UNLOCKED_VALUE.toLowerCase())) {
            properties.setLeaseStatus(LeaseStatus.UNLOCKED);
          } else {
            throw new StorageException(
                StorageErrorCodeStrings.INVALID_XML_DOCUMENT,
                "The response received is invalid or improperly formatted.",
                Constants.HeaderConstants.HTTP_UNUSED_306,
                null,
                null);
          }
        } else if (name.equals(Constants.LEASE_STATE_ELEMENT)) {
          properties.setLeaseState(
              LeaseState.parse(
                  Utility.readElementFromXMLReader(xmlr, Constants.LEASE_STATE_ELEMENT)));
        } else if (name.equals(Constants.LEASE_DURATION_ELEMENT)) {
          properties.setLeaseDuration(
              LeaseDuration.parse(
                  Utility.readElementFromXMLReader(xmlr, Constants.LEASE_DURATION_ELEMENT)));
        }
      } else if (eventType == XMLStreamConstants.END_ELEMENT) {
        // expect end of properties
        xmlr.require(XMLStreamConstants.END_ELEMENT, null, BlobConstants.PROPERTIES);
        break;
      }
    }

    return properties;
  }
  /**
   * Reserved for internal use. Performs a delete operation on the specified table, using the
   * specified {@link TableRequestOptions} and {@link OperationContext}.
   *
   * <p>This method will invoke the <a
   * href="http://msdn.microsoft.com/en-us/library/windowsazure/dd135727.aspx">Delete Entity</a>
   * REST API to execute this table operation, using the Table service endpoint and storage account
   * credentials in the {@link CloudTableClient} object.
   *
   * @param client A {@link CloudTableClient} instance specifying the Table service endpoint,
   *     storage account credentials, and any additional query parameters.
   * @param tableName A <code>String</code> containing the name of the table.
   * @param options A {@link TableRequestOptions} object that specifies execution options such as
   *     retry policy and timeout settings for the operation.
   * @param opContext An {@link OperationContext} object for tracking the current operation.
   * @return A {@link TableResult} containing the results of executing the operation.
   * @throws StorageException if an error occurs in the storage operation.
   */
  private TableResult performDelete(
      final CloudTableClient client,
      final String tableName,
      final TableRequestOptions options,
      final OperationContext opContext)
      throws StorageException {
    final boolean isTableEntry = TableConstants.TABLES_SERVICE_TABLES_NAME.equals(tableName);
    final String tableIdentity =
        isTableEntry
            ? this.getEntity()
                .writeEntity(opContext)
                .get(TableConstants.TABLE_NAME)
                .getValueAsString()
            : null;

    if (!isTableEntry) {
      Utility.assertNotNullOrEmpty("Delete requires a valid ETag", this.getEntity().getEtag());
      Utility.assertNotNullOrEmpty(
          "Delete requires a valid PartitionKey", this.getEntity().getPartitionKey());
      Utility.assertNotNullOrEmpty("Delete requires a valid RowKey", this.getEntity().getRowKey());
    }

    final StorageOperation<CloudTableClient, TableOperation, TableResult> impl =
        new StorageOperation<CloudTableClient, TableOperation, TableResult>(options) {
          @Override
          public TableResult execute(
              final CloudTableClient client,
              final TableOperation operation,
              final OperationContext opContext)
              throws Exception {

            final HttpURLConnection request =
                TableRequest.delete(
                    client.getTransformedEndPoint(opContext),
                    tableName,
                    generateRequestIdentity(isTableEntry, tableIdentity, false),
                    operation.getEntity().getEtag(),
                    options.getTimeoutIntervalInMs(),
                    null,
                    options,
                    opContext);

            client.getCredentials().signRequestLite(request, -1L, opContext);

            this.setResult(ExecutionEngine.processRequest(request, opContext));

            if (this.getResult().getStatusCode() == HttpURLConnection.HTTP_NOT_FOUND
                || this.getResult().getStatusCode() == HttpURLConnection.HTTP_CONFLICT) {
              throw TableServiceException.generateTableServiceException(
                  false, this.getResult(), operation, request.getErrorStream());
            }

            if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_NO_CONTENT) {
              throw TableServiceException.generateTableServiceException(
                  true, this.getResult(), operation, request.getErrorStream());
            }

            return operation.parseResponse(null, this.getResult().getStatusCode(), null, opContext);
          }
        };

    return ExecutionEngine.executeWithRetry(
        client, this, impl, options.getRetryPolicyFactory(), opContext);
  }
 /**
  * A static factory method returning a {@link TableOperation} instance to replace the specified
  * table entity. To execute this {@link TableOperation} on a given table, call the {@link
  * CloudTableClient#execute(String, TableOperation)} method on a {@link CloudTableClient} instance
  * with the table name and the {@link TableOperation} as arguments.
  *
  * @param entity The object instance implementing {@link TableEntity} to associate with the
  *     operation.
  * @return A new {@link TableOperation} instance for replacing the table entity.
  */
 public static TableOperation replace(final TableEntity entity) {
   Utility.assertNotNullOrEmpty("Entity Etag", entity.getEtag());
   return new TableOperation(entity, TableOperationType.REPLACE);
 }
 /**
  * Gets a {@link CloudQueue} object that represents the storage service queue for the specified
  * address.
  *
  * @param queueAddress A <code>String</code> that represents the name of the queue, or the
  *     absolute URI to the queue.
  * @return A {@link CloudQueue} object that represents a reference to the queue.
  * @throws URISyntaxException If the resource URI is invalid.
  * @throws StorageException If a storage service error occurred during the operation.
  */
 public CloudQueue getQueueReference(final String queueAddress)
     throws URISyntaxException, StorageException {
   Utility.assertNotNullOrEmpty("queueAddress", queueAddress);
   return new CloudQueue(queueAddress, this);
 }
  /**
   * 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);
    }
  }
  /**
   * Constructs a HttpURLConnection to list blobs. Sign with no length specified.
   *
   * @param uri The absolute URI to the blob
   * @param timeout The server timeout interval
   * @param listingContext A set of parameters for the listing operation.
   * @param blobOptions the options to use for the request.
   * @param opContext a tracking object for the request
   * @return a HttpURLConnection configured for the operation.
   * @throws IOException if there is an error opening the connection
   * @throws URISyntaxException if the resource URI is invalid
   * @throws StorageException an exception representing any error which occurred during the
   *     operation.
   * @throws IllegalArgumentException
   */
  public static HttpURLConnection list(
      final URI uri,
      final int timeout,
      final BlobListingContext listingContext,
      final BlobRequestOptions blobOptions,
      final OperationContext opContext)
      throws URISyntaxException, IOException, StorageException {

    final UriQueryBuilder builder = ContainerRequest.getContainerUriQueryBuilder();
    builder.add("comp", "list");

    if (listingContext != null) {
      if (!Utility.isNullOrEmpty(listingContext.getPrefix())) {
        builder.add("prefix", listingContext.getPrefix());
      }

      if (!Utility.isNullOrEmpty(listingContext.getDelimiter())) {
        builder.add("delimiter", listingContext.getDelimiter());
      }

      if (!Utility.isNullOrEmpty(listingContext.getMarker())) {
        builder.add("marker", listingContext.getMarker());
      }

      if (listingContext.getMaxResults() != null && listingContext.getMaxResults() > 0) {
        builder.add("maxresults", listingContext.getMaxResults().toString());
      }

      if (listingContext.getListingDetails().size() > 0) {
        final StringBuilder sb = new StringBuilder();

        boolean started = false;

        if (listingContext.getListingDetails().contains(BlobListingDetails.SNAPSHOTS)) {
          if (!started) {
            started = true;
          } else {
            sb.append(",");
          }

          sb.append("snapshots");
        }

        if (listingContext.getListingDetails().contains(BlobListingDetails.UNCOMMITTED_BLOBS)) {
          if (!started) {
            started = true;
          } else {
            sb.append(",");
          }

          sb.append("uncommittedblobs");
        }

        if (listingContext.getListingDetails().contains(BlobListingDetails.METADATA)) {
          if (!started) {
            started = true;
          } else {
            sb.append(",");
          }

          sb.append("metadata");
        }

        builder.add("include", sb.toString());
      }
    }

    final HttpURLConnection request =
        BlobRequest.createURLConnection(uri, timeout, builder, blobOptions, opContext);

    request.setRequestMethod("GET");

    return request;
  }