/**
   * 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. 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");
    }
  }
 /**
  * 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);
  }
  /**
   * 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);
 }