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