public interface CloudTests {
} public interface DevFabricTests {
} public interface DevStoreTests {
} public final class CloudAppendBlob extends CloudBlob {
    /**
     * Creates an instance of the <code>CloudAppendBlob</code> class using the specified absolute URI and storage service
     * client.
     * 
     * @param blobAbsoluteUri
     *            A <code>java.net.URI</code> object which represents the absolute URI to the blob.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    public CloudAppendBlob(final URI blobAbsoluteUri) throws StorageException {
        this(new StorageUri(blobAbsoluteUri));
    }

    /**
     * Creates an instance of the <code>CloudAppendBlob</code> class using the specified absolute URI and storage service
     * client.
     * 
     * @param blobAbsoluteUri
     *            A {@link StorageUri} object which represents the absolute URI to the blob.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    public CloudAppendBlob(final StorageUri blobAbsoluteUri) throws StorageException {
        this(blobAbsoluteUri, (StorageCredentials)null);
    }

    /**
     * Creates an instance of the <code>CloudAppendBlob</code> class by copying values from another append blob.
     * 
     * @param otherBlob
     *            A <code>CloudAppendBlob</code> object which represents the append blob to copy.
     */
    public CloudAppendBlob(final CloudAppendBlob otherBlob) {
        super(otherBlob);
    }
    
    /**
     * Creates an instance of the <code>CloudAppendBlob</code> class using the specified absolute URI and credentials.
     * 
     * @param blobAbsoluteUri
     *            A <code>java.net.URI</code> object that represents the absolute URI to the blob.
     * @param credentials
     *            A {@link StorageCredentials} object used to authenticate access.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    public CloudAppendBlob(final URI blobAbsoluteUri, final StorageCredentials credentials) throws StorageException {
        this(new StorageUri(blobAbsoluteUri), credentials);
    }

    /**
     * Creates an instance of the <code>CloudAppendBlob</code> class using the specified absolute URI, snapshot ID, and
     * credentials.
     * 
     * @param blobAbsoluteUri
     *            A <code>java.net.URI</code> object that represents the absolute URI to the blob.
     * @param snapshotID
     *            A <code>String</code> that represents the snapshot version, if applicable.
     * @param credentials
     *            A {@link StorageCredentials} object used to authenticate access.
     * @throws StorageException
     *             If a storage service error occurred.
     */
    public CloudAppendBlob(final URI blobAbsoluteUri, final String snapshotID, final StorageCredentials credentials)
            throws StorageException {
        this(new StorageUri(blobAbsoluteUri), snapshotID, credentials);
    }
    
    /**
     * Creates an instance of the <code>CloudAppendBlob</code> class using the specified absolute StorageUri 
     * and credentials.
     * 
     * @param blobAbsoluteUri
     *            A {@link StorageUri} object that represents the absolute URI to the blob.
     * @param credentials
     *            A {@link StorageCredentials} object used to authenticate access.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    public CloudAppendBlob(final StorageUri blobAbsoluteUri, final StorageCredentials credentials) throws StorageException {
        this(blobAbsoluteUri, null /* snapshotID */, credentials);
    }

    /**
     * Creates an instance of the <code>CloudAppendBlob</code> class using the specified absolute StorageUri, snapshot
     * ID, and credentials.
     * 
     * @param blobAbsoluteUri
     *            A {@link StorageUri} object that represents the absolute URI to the blob.
     * @param snapshotID
     *            A <code>String</code> that represents the snapshot version, if applicable.
     * @param credentials
     *            A {@link StorageCredentials} object used to authenticate access.
     * @throws StorageException
     *             If a storage service error occurred.
     */
    public CloudAppendBlob(final StorageUri blobAbsoluteUri, final String snapshotID, final StorageCredentials credentials)
            throws StorageException {
        super(BlobType.APPEND_BLOB, blobAbsoluteUri, snapshotID, credentials);
    }
    
    /**
     * Creates an instance of the <code>CloudAppendBlob</code> class using the specified type, name, snapshot ID, and
     * container.
     *
     * @param blobName
     *            Name of the blob.
     * @param snapshotID
     *            A <code>String</code> that represents the snapshot version, if applicable.
     * @param container
     *            The reference to the parent container.
     * @throws URISyntaxException
     *             If the resource URI is invalid.
     */
    protected CloudAppendBlob(String blobName, String snapshotID, CloudBlobContainer container)
            throws URISyntaxException {
        super(BlobType.APPEND_BLOB, blobName, snapshotID, container);
    }
    
    /**
     * Requests the service to start copying a append blob's contents, properties, and metadata to a new append blob.
     *
     * @param sourceBlob
     *            A <code>CloudAppendBlob</code> object that represents the source blob to copy.
     *
     * @return A <code>String</code> which represents the copy ID associated with the copy operation.
     *
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws URISyntaxException
     */
    @DoesServiceRequest
    public final String startCopy(final CloudAppendBlob sourceBlob) throws StorageException, URISyntaxException {
        return this.startCopy(sourceBlob, null /* sourceAccessCondition */,
                null /* destinationAccessCondition */, null /* options */, null /* opContext */);
    }

    /**
     * Requests the service to start copying a append blob's contents, properties, and metadata to a new append blob,
     * using the specified access conditions, lease ID, request options, and operation context.
     *
     * @param sourceBlob
     *            A <code>CloudAppendBlob</code> object that represents the source blob to copy.
     * @param sourceAccessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the source blob.
     * @param destinationAccessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the destination blob.
     * @param options
     *            A {@link BlobRequestOptions} 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}).
     * @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 <code>String</code> which represents the copy ID associated with the copy operation.
     *
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws URISyntaxException
     *
     */
    @DoesServiceRequest
    public final String startCopy(final CloudAppendBlob sourceBlob, final AccessCondition sourceAccessCondition,
            final AccessCondition destinationAccessCondition, BlobRequestOptions options, OperationContext opContext)
            throws StorageException, URISyntaxException {
        Utility.assertNotNull("sourceBlob", sourceBlob);

        URI source = sourceBlob.getSnapshotQualifiedUri();
        if (sourceBlob.getServiceClient() != null && sourceBlob.getServiceClient().getCredentials() != null)
        {
            source = sourceBlob.getServiceClient().getCredentials().transformUri(sourceBlob.getSnapshotQualifiedUri());
        }

        return this.startCopy(source, sourceAccessCondition, destinationAccessCondition, options, opContext);
    }

    /**
     * Creates an empty append blob. If the blob already exists, this will replace it. 
     * <p>
     * To avoid overwriting and instead throw an error, please use the 
     * {@link #createOrReplace(AccessCondition, BlobRequestOptions, OperationContext)} overload with the appropriate 
     * {@link AccessCondition}.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public void createOrReplace() throws StorageException {
        this.createOrReplace(null /* accessCondition */, null /* options */, null /* opContext */);
    }

    /**
     * Creates an append blob using the specified request options and operation context. If the blob already exists, 
     * this will replace it.
     * <p>
     * To avoid overwriting and instead throw an error, please pass in an {@link AccessCondition} generated using 
     * {@link AccessCondition#generateIfNotExistsCondition()}.
     * 
     * @param accessCondition
     *            An {@link AccessCondition} object which represents the access conditions for the blob.
     * @param options
     *            A {@link BlobRequestOptions} 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}).
     * @param opContext
     *            An {@link OperationContext} object which 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.
     */
    @DoesServiceRequest
    public void createOrReplace(final AccessCondition accessCondition, BlobRequestOptions options,
            OperationContext opContext) throws StorageException {
        assertNoWriteOperationForSnapshot();

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

        options = BlobRequestOptions.populateAndApplyDefaults(options, BlobType.APPEND_BLOB, this.blobServiceClient);

        ExecutionEngine.executeWithRetry(this.blobServiceClient, this,
                this.createImpl(accessCondition, options), options.getRetryPolicyFactory(), opContext);
    }

    private StorageRequest<CloudBlobClient, CloudBlob, Void> createImpl(final AccessCondition accessCondition, 
            final BlobRequestOptions options) {
        final StorageRequest<CloudBlobClient, CloudBlob, Void> putRequest = new StorageRequest<CloudBlobClient, CloudBlob, Void>(
                options, this.getStorageUri()) {

            @Override
            public HttpURLConnection buildRequest(CloudBlobClient client, CloudBlob blob, OperationContext context)
                    throws Exception {
                return BlobRequest.putBlob(blob.getTransformedAddress(context).getUri(this.getCurrentLocation()),
                        options, context, accessCondition, blob.properties, BlobType.APPEND_BLOB, 0);
            }

            @Override
            public void setHeaders(HttpURLConnection connection, CloudBlob blob, OperationContext context) {
                BlobRequest.addMetadata(connection, blob.metadata, context);
            }

            @Override
            public void signRequest(HttpURLConnection connection, CloudBlobClient client, OperationContext context)
                    throws Exception {
                StorageRequest.signBlobQueueAndFileRequest(connection, client, 0L, context);
            }

            @Override
            public Void preProcessResponse(CloudBlob blob, CloudBlobClient client, OperationContext context)
                    throws Exception {
                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_CREATED) {
                    this.setNonExceptionedRetryableFailure(true);
                    return null;
                }

                blob.updateEtagAndLastModifiedFromResponse(this.getConnection());
                this.getResult().setRequestServiceEncrypted(BaseResponse.isServerRequestEncrypted(this.getConnection()));
                blob.getProperties().setLength(0);
                return null;
            }

        };

        return putRequest;
    }
    
    /**
     * Commits a new block of data to the end of the blob.
     * 
     * @param sourceStream
     *            An {@link InputStream} object that represents the input stream to write to the append blob.
     * @param length
     *            A <code>long</code> which represents the length, in bytes, of the stream data, or -1 if unknown.
     *            
     * @return The offset at which the block was appended.
     * @throws IOException
     *             If an I/O exception occurred.
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public Long appendBlock(final InputStream sourceStream, final long length) throws IOException, StorageException
    {
        return this.appendBlock(sourceStream, length, null, null, null);
    }
    
    /**
     * Commits a new block of data to the end of the blob.
     * 
     * @param sourceStream
     *            An {@link InputStream} object that represents the input stream to write to the Append blob.
     * @param length
     *            A <code>long</code> which represents the length, in bytes, of the stream data, or -1 if unknown.
     * @param accessCondition
     *            An {@link AccessCondition} object which represents the access conditions for the blob.
     * @param options
     *            A {@link BlobRequestOptions} 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}).
     * @param opContext
     *            An {@link OperationContext} object which 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 The offset at which the block was appended.</returns>
     * @throws IOException
     *             If an I/O exception occurred.
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public Long appendBlock(final InputStream sourceStream, final long length, final AccessCondition accessCondition, 
            BlobRequestOptions options, OperationContext opContext) throws StorageException, IOException
    {
        if (length < -1) {
            throw new IllegalArgumentException(SR.STREAM_LENGTH_NEGATIVE);
        }

        assertNoWriteOperationForSnapshot();

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

        options = BlobRequestOptions.populateAndApplyDefaults(options, BlobType.APPEND_BLOB, this.blobServiceClient);

        if (sourceStream.markSupported()) {
            // Mark sourceStream for current position.
            sourceStream.mark(Constants.MAX_MARK_LENGTH);
        }

        InputStream bufferedStreamReference = sourceStream;
        StreamMd5AndLength descriptor = new StreamMd5AndLength();
        descriptor.setLength(length);

        if (!sourceStream.markSupported()) {
            // needs buffering
            final ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
            descriptor = Utility.writeToOutputStream(sourceStream, byteStream, length, false /* rewindSourceStream */,
                    options.getUseTransactionalContentMD5(), opContext, options);

            bufferedStreamReference = new ByteArrayInputStream(byteStream.toByteArray());
        }
        else if (length < 0 || options.getUseTransactionalContentMD5()) {
            // If the stream is of unknown length or we need to calculate the
            // MD5, then we we need to read the stream contents first
            descriptor = Utility.analyzeStream(sourceStream, length, -1L, true /* rewindSourceStream */,
                    options.getUseTransactionalContentMD5());
        }

        if (descriptor.getLength() > 4 * Constants.MB) {
            throw new IllegalArgumentException(SR.STREAM_LENGTH_GREATER_THAN_4MB);
        }

        StorageRequest<CloudBlobClient, CloudAppendBlob, Long> appendBlockImpl = appendBlockImpl(descriptor.getMd5(), bufferedStreamReference, 
                descriptor.getLength(), accessCondition, options, opContext);
        return ExecutionEngine.executeWithRetry(this.blobServiceClient, this, appendBlockImpl, options.getRetryPolicyFactory(), opContext);
    }

    private StorageRequest<CloudBlobClient, CloudAppendBlob, Long> appendBlockImpl(final String md5,
            final InputStream sourceStream, final long length, final AccessCondition accessCondition,
            final BlobRequestOptions options, final OperationContext opContext) {

        final StorageRequest<CloudBlobClient, CloudAppendBlob, Long> putRequest = new StorageRequest<CloudBlobClient, CloudAppendBlob, Long>(
                options, this.getStorageUri()) {

            @Override
            public HttpURLConnection buildRequest(CloudBlobClient client, CloudAppendBlob blob, OperationContext context)
                    throws Exception {
                this.setSendStream(sourceStream);
                this.setLength(length);
                return BlobRequest.appendBlock(blob.getTransformedAddress(opContext).getUri(this.getCurrentLocation()),
                        options, opContext, accessCondition);
            }

            @Override
            public void setHeaders(HttpURLConnection connection, CloudAppendBlob blob, OperationContext context) {
                if (options.getUseTransactionalContentMD5()) {
                    connection.setRequestProperty(Constants.HeaderConstants.CONTENT_MD5, md5);
                }
            }

            @Override
            public void signRequest(HttpURLConnection connection, CloudBlobClient client, OperationContext context)
                    throws Exception {
                StorageRequest.signBlobQueueAndFileRequest(connection, client, length, context);
            }

            @Override
            public Long preProcessResponse(CloudAppendBlob blob, CloudBlobClient client, OperationContext context)
                    throws Exception {
                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_CREATED) {
                    this.setNonExceptionedRetryableFailure(true);
                    return null;
                }

                Long appendOffset = null;
                if (this.getConnection().getHeaderField(Constants.HeaderConstants.BLOB_APPEND_OFFSET) != null)
                {
                    appendOffset = Long.parseLong(this.getConnection().getHeaderField(Constants.HeaderConstants.BLOB_APPEND_OFFSET));
                }

                blob.updateEtagAndLastModifiedFromResponse(this.getConnection());
                blob.updateCommittedBlockCountFromResponse(this.getConnection());

                this.getResult().setRequestServiceEncrypted(BaseResponse.isServerRequestEncrypted(this.getConnection()));
                return appendOffset;
            }

            @Override
            public void recoveryAction(OperationContext context) throws IOException {
                sourceStream.reset();
                sourceStream.mark(Constants.MAX_MARK_LENGTH);
            }
        };

        return putRequest;
    }
    
    /**
     * Updates the blob's committed block count from the web request.
     * 
     * @param request
     *            The web request from which to parse the committed block count.
     */
    private void updateCommittedBlockCountFromResponse(HttpURLConnection request) {
        final String comittedBlockCount = request.getHeaderField(Constants.HeaderConstants.BLOB_COMMITTED_BLOCK_COUNT);
        if (!Utility.isNullOrEmpty(comittedBlockCount))
        {
            this.getProperties().setAppendBlobCommittedBlockCount(Integer.parseInt(comittedBlockCount));
        }
    }
    
    /**
     * Appends a stream to an append blob. This API should be used strictly in a single writer scenario because the API 
     * internally uses the append-offset conditional header to avoid duplicate blocks which does not work in a multiple 
     * writer scenario.
     * <p>
     * If you are doing writes in a single writer scenario, please look at 
     * {@link BlobRequestOptions#setAbsorbConditionalErrorsOnRetry(Boolean)} and see if setting this flag 
     * to <code>true</code> is acceptable for you.
     * 
     * @param sourceStream
     *          A {@link InputStream} object providing the blob content to append.
     * @param length
     *            A <code>long</code> which represents the length, in bytes, of the stream data, or -1 if unknown.
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws IOException
     *          If an I/O exception occurred.
     */
    @DoesServiceRequest
    public void append(InputStream sourceStream, final long length) throws StorageException, IOException
    {
        this.append(sourceStream, length, null /* accessCondition */, null /* options */, null /* operationContext */);
    }
    
    /**
     * Appends a stream to an append blob. This API should be used strictly in a single writer scenario because the API 
     * internally uses the append-offset conditional header to avoid duplicate blocks which does not work in a multiple 
     * writer scenario.
     * <p>
     * If you are doing writes in a single writer scenario, please look at 
     * {@link BlobRequestOptions#setAbsorbConditionalErrorsOnRetry(Boolean)} and see if setting this flag 
     * to <code>true</code> is acceptable for you.
     * 
     * @param sourceStream
     *          A {@link InputStream} object providing the blob content to append.
     * @param length
     *            A <code>long</code> which represents the length, in bytes, of the stream data, or -1 if unknown.
     * @param accessCondition
     *            An {@link AccessCondition} object which represents the access conditions for the blob.
     * @param options
     *            A {@link BlobRequestOptions} 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}).
     * @param opContext
     *            An {@link OperationContext} object which 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.
     * @throws IOException
     *          If an I/O exception occurred.
     */
    @DoesServiceRequest
    public void append(InputStream sourceStream, final long length, AccessCondition accessCondition, 
            BlobRequestOptions options, OperationContext opContext) throws StorageException, IOException
    {
        assertNoWriteOperationForSnapshot();

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

        options = BlobRequestOptions.populateAndApplyDefaults(options, BlobType.APPEND_BLOB, this.blobServiceClient);

        if (sourceStream.markSupported()) {
            // Mark sourceStream for current position.
            sourceStream.mark(Constants.MAX_MARK_LENGTH);
        }

        final BlobOutputStream streamRef = this.openWriteExisting(accessCondition, options, opContext);
        try {
            streamRef.write(sourceStream, length);
        }
        finally {
            streamRef.close();
        }
    }
    
    /**
     * Appends the contents of a byte array to an append blob.This API should be used strictly in a single writer 
     * scenario because the API internally uses the append-offset conditional header to avoid duplicate blocks which 
     * does not work in a multiple writer scenario.
     * <p>
     * If you are doing writes in a single writer scenario, please look at 
     * {@link BlobRequestOptions#setAbsorbConditionalErrorsOnRetry(Boolean)} and see if setting this flag 
     * to <code>true</code> is acceptable for you.
     * 
     * @param buffer
     *            A <code>byte</code> array which represents the data to append to the blob.
     * @param offset
     *            A <code>int</code> which represents the offset of the byte array from which to start the data upload.
     * @param length
     *            An <code>int</code> which represents the number of bytes to upload from the input buffer.
     *
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws IOException
     *          If an I/O exception occurred.
     */
    public void appendFromByteArray(final byte[] buffer, final int offset, final int length) throws StorageException,
            IOException {
        appendFromByteArray(buffer, offset, length, null /* accessCondition */, null /* options */, null /* opContext */);
    }

    /**
     * Appends the contents of a byte array to an append blob.This API should be used strictly in a single writer 
     * scenario because the API internally uses the append-offset conditional header to avoid duplicate blocks which 
     * does not work in a multiple writer scenario.
     * <p>
     * If you are doing writes in a single writer scenario, please look at 
     * {@link BlobRequestOptions#setAbsorbConditionalErrorsOnRetry(Boolean)} and see if setting this flag 
     * to <code>true</code> is acceptable for you.
     * 
     * @param buffer
     *            A <code>byte</code> array which represents the data to append to the blob.
     * @param offset
     *            A <code>int</code> which represents the offset of the byte array from which to start the data upload.
     * @param length
     *            An <code>int</code> which represents the number of bytes to upload from the input buffer.
     * @param accessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the blob.
     * @param options
     *            A {@link BlobRequestOptions} 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}).
     * @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.
     * @throws IOException
     *          If an I/O exception occurred.
     */
    public void appendFromByteArray(final byte[] buffer, final int offset, final int length,
            final AccessCondition accessCondition, BlobRequestOptions options, OperationContext opContext)
            throws StorageException, IOException {
        ByteArrayInputStream inputStream = new ByteArrayInputStream(buffer, offset, length);
        this.append(inputStream, length, accessCondition, options, opContext);
        inputStream.close();
    }
    
    /**
     * Appends a file to an append blob. This API should be used strictly in a single writer scenario because the API 
     * internally uses the append-offset conditional header to avoid duplicate blocks which does not work in a multiple 
     * writer scenario.
     * <p>
     * If you are doing writes in a single writer scenario, please look at 
     * {@link BlobRequestOptions#setAbsorbConditionalErrorsOnRetry(Boolean)} and see if setting this flag 
     * to <code>true</code> is acceptable for you.
     * 
     * @param path
     *            A <code>String</code> which represents the path to the file to be appended.
     *
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws IOException
     *          If an I/O exception occurred.
     */
    public void appendFromFile(final String path) throws StorageException, IOException {
        appendFromFile(path, null /* accessCondition */, null /* options */, null /* opContext */);
    }

    /**
     * Appends a file to an append blob. This API should be used strictly in a single writer scenario because the API 
     * internally uses the append-offset conditional header to avoid duplicate blocks which does not work in a multiple 
     * writer scenario.
     * <p>
     * If you are doing writes in a single writer scenario, please look at 
     * {@link BlobRequestOptions#setAbsorbConditionalErrorsOnRetry(Boolean)} and see if setting this flag 
     * to <code>true</code> is acceptable for you.
     *
     * @param path
     *            A <code>String</code> which represents the path to the file to be appended.
     * @param accessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the blob.
     * @param options
     *            A {@link BlobRequestOptions} 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}).
     * @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.
     * @throws IOException
     *          If an I/O exception occurred.
     */
    public void appendFromFile(final String path, final AccessCondition accessCondition, BlobRequestOptions options,
            OperationContext opContext) throws StorageException, IOException {
        File file = new File(path);
        long fileLength = file.length();
        InputStream inputStream = new BufferedInputStream(new FileInputStream(file));
        this.append(inputStream, fileLength, accessCondition, options, opContext);
        inputStream.close();
    }
    
    /**
     * Appends a string of text to an append blob using the platform's default encoding. This API should be used 
     * strictly in a single writer scenario because the API internally uses the append-offset conditional header to 
     * avoid duplicate blocks which does not work in a multiple writer scenario.
     * <p>
     * If you are doing writes in a single writer scenario, please look at 
     * {@link BlobRequestOptions#setAbsorbConditionalErrorsOnRetry(Boolean)} and see if setting this flag 
     * to <code>true</code> is acceptable for you.
     * 
     * @param content
     *            A <code>String</code> which represents the content that will be appended to the blob.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws IOException
     *          If an I/O exception occurred.
     */
    public void appendText(final String content) throws StorageException, IOException {
        this.appendText(content, null /* charsetName */, null /* accessCondition */, null /* options */, 
                null /* opContext */);
    }

    /**
     * Appends a string of text to an append blob using the specified encoding. This API should be used strictly in a 
     * single writer scenario because the API internally uses the append-offset conditional header to avoid duplicate 
     * blocks which does not work in a multiple writer scenario.
     * <p>
     * If you are doing writes in a single writer scenario, please look at 
     * {@link BlobRequestOptions#setAbsorbConditionalErrorsOnRetry(Boolean)} and see if setting this flag 
     * to <code>true</code> is acceptable for you.
     * 
     * @param content
     *            A <code>String</code> which represents the content that will be appended to the blob.
     * @param charsetName
     *            A <code>String</code> which represents the name of the charset to use to encode the content.
     *            If null, the platform's default encoding is used.
     * @param accessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the blob.
     * @param options
     *            A {@link BlobRequestOptions} 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}).
     * @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.
     * @throws IOException
     *          If an I/O exception occurred.
     */
    public void appendText(final String content, final String charsetName, final AccessCondition accessCondition,
            BlobRequestOptions options, OperationContext opContext) throws StorageException, IOException {
        byte[] bytes = (charsetName == null) ? content.getBytes() : content.getBytes(charsetName);
        this.appendFromByteArray(bytes, 0, bytes.length, accessCondition, options, opContext);
    }
    
    /**
     * Opens an output stream object to write data to the append blob. The append blob must already exist and will be
     * appended to.
     * <p>
     * If you are doing writes in a single writer scenario, please look at 
     * {@link BlobRequestOptions#setAbsorbConditionalErrorsOnRetry(Boolean)} and see if setting this flag 
     * to <code>true</code> is acceptable for you.
     * 
     * @return A {@link BlobOutputStream} object used to write data to the blob.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public BlobOutputStream openWriteExisting() throws StorageException {
        return this.openWriteExisting(null /* accessCondition */, null /* options */, null /* opContext */);
    }

    /**
     * Opens an output stream object to write data to the append blob, using the specified lease ID, request options and
     * operation context. The append blob must already exist and will be appended to.
     * <p>
     * If you are doing writes in a single writer scenario, please look at 
     * {@link BlobRequestOptions#setAbsorbConditionalErrorsOnRetry(Boolean)} and see if setting this flag 
     * to <code>true</code> is acceptable for you.
     * 
     * @param accessCondition
     *            An {@link AccessCondition} object which represents the access conditions for the blob.
     * @param options
     *            A {@link BlobRequestOptions} 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}).
     * @param opContext
     *            An {@link OperationContext} object which 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 BlobOutputStream} object used to write data to the blob.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public BlobOutputStream openWriteExisting(AccessCondition accessCondition, BlobRequestOptions options,
            OperationContext opContext) throws StorageException {
        return this.openOutputStreamInternal(false, accessCondition, options, opContext);
    }

    /**
     * Opens an output stream object to write data to the append blob. The append blob does not need to yet exist. If 
     * the blob already exists, this will replace it. 
     * <p>
     * To avoid overwriting and instead throw an error, please use the 
     * {@link #openWriteNew(AccessCondition, BlobRequestOptions, OperationContext)} overload with the appropriate 
     * {@link AccessCondition}.
     * <p>
     * If you are doing writes in a single writer scenario, please look at 
     * {@link BlobRequestOptions#setAbsorbConditionalErrorsOnRetry(Boolean)} and see if setting this flag 
     * to <code>true</code> is acceptable for you.
     * 
     * @return A {@link BlobOutputStream} object used to write data to the blob.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public BlobOutputStream openWriteNew() throws StorageException {
        return this.openWriteNew(null /* accessCondition */, null /* options */, null /* opContext */);
    }

    /**
     * Opens an output stream object to write data to the append blob, using the specified lease ID, request options and
     * operation context. The append blob does not need to yet exist. If the blob already exists, this will replace it. 
     * <p>
     * To avoid overwriting and instead throw an error, please pass in an {@link AccessCondition} generated using 
     * {@link AccessCondition#generateIfNotExistsCondition()}.
     * <p>
     * If you are doing writes in a single writer scenario, please look at 
     * {@link BlobRequestOptions#setAbsorbConditionalErrorsOnRetry(Boolean)} and see if setting this flag 
     * to <code>true</code> is acceptable for you.
     * 
     * @param accessCondition
     *            An {@link AccessCondition} object which represents the access conditions for the blob.
     * @param options
     *            A {@link BlobRequestOptions} 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}).
     * @param opContext
     *            An {@link OperationContext} object which 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 BlobOutputStream} object used to write data to the blob.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public BlobOutputStream openWriteNew(AccessCondition accessCondition,
            BlobRequestOptions options, OperationContext opContext) throws StorageException {
        return this.openOutputStreamInternal(true, accessCondition, options, opContext);
    }

    /**
     * Opens an output stream object to write data to the append blob, using the specified lease ID, request options and
     * operation context.
     * 
     * @param accessCondition
     *            An {@link AccessCondition} object which represents the access conditions for the blob.
     * @param options
     *            A {@link BlobRequestOptions} 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}).
     * @param opContext
     *            An {@link OperationContext} object which 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 BlobOutputStream} object used to write data to the blob.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    private BlobOutputStream openOutputStreamInternal(boolean create, AccessCondition accessCondition,
            BlobRequestOptions options, OperationContext opContext) throws StorageException {
        assertNoWriteOperationForSnapshot();
        
        if (opContext == null) {
            opContext = new OperationContext();
        }
        
        BlobRequestOptions modifiedOptions = BlobRequestOptions.populateAndApplyDefaults(options, BlobType.APPEND_BLOB, 
                this.blobServiceClient, false /* setStartTime */);
        
        if (create) {
            this.createOrReplace(accessCondition, modifiedOptions, opContext);
        } else {
            if (modifiedOptions.getStoreBlobContentMD5()) {
                throw new IllegalArgumentException(SR.APPEND_BLOB_MD5_NOT_POSSIBLE);
            }
            
            // Download attributes to check the etag and date access conditions and
            // to get the blob length to verify the append position on the first write.
            this.downloadAttributes(accessCondition, modifiedOptions, opContext);
        }

        // Use an access condition with the etag and date conditions removed as we will be
        // appending to the blob and these properties will change each time we append a block.
        AccessCondition appendCondition = new AccessCondition();
        if (accessCondition != null) {
            appendCondition.setLeaseID(accessCondition.getLeaseID());
            appendCondition.setIfAppendPositionEqual(accessCondition.getIfAppendPositionEqual());
            appendCondition.setIfMaxSizeLessThanOrEqual(accessCondition.getIfMaxSizeLessThanOrEqual());
        }

        return new BlobOutputStream(this, appendCondition, modifiedOptions, opContext);
    }

    /**
     * Uploads the source stream data to the append blob.  If the blob already exists on the service, it will be 
     * overwritten.
     * <p>
     * If you want to append data to an already existing blob, please see {@link #appendBlock(InputStream, long)}.
     * <p>
     * If you are doing writes in a single writer scenario, please look at 
     * {@link BlobRequestOptions#setAbsorbConditionalErrorsOnRetry(Boolean)} and see if setting this flag 
     * to <code>true</code> is acceptable for you.
     * 
     * @param sourceStream
     *            An {@link InputStream} object to read from.
     * @param length
     *            A <code>long</code> which represents the length, in bytes, of the stream data, or -1 if unknown.
     * 
     * @throws IOException
     *             If an I/O exception occurred.
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @Override
    @DoesServiceRequest
    public void upload(final InputStream sourceStream, final long length) throws StorageException, IOException {
        this.upload(sourceStream, length, null /* accessCondition */, null /* options */, null /* opContext */);
    }

    /**
     * Uploads the source stream data to the append blob using the specified lease ID, request options, and operation
     * context. If the blob already exists on the service, it will be overwritten.
     * <p>
     * If you want to append data to an already existing blob, please see {@link #appendBlock(InputStream, long)}.
     * <p>
     * If you are doing writes in a single writer scenario, please look at 
     * {@link BlobRequestOptions#setAbsorbConditionalErrorsOnRetry(Boolean)} and see if setting this flag 
     * to <code>true</code> is acceptable for you.
     * 
     * @param sourceStream
     *            An {@link InputStream} object to read from.
     * @param length
     *            A <code>long</code> which represents the length, in bytes, of the stream data, or -1 if unknown.
     * @param accessCondition
     *            An {@link AccessCondition} object which represents the access conditions for the blob.
     * @param options
     *            A {@link BlobRequestOptions} 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}).
     * @param opContext
     *            An {@link OperationContext} object which 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 IOException
     *             If an I/O exception occurred.
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @Override
    @DoesServiceRequest
    public void upload(final InputStream sourceStream, final long length, final AccessCondition accessCondition,
            BlobRequestOptions options, OperationContext opContext) throws StorageException, IOException {
        assertNoWriteOperationForSnapshot();

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

        options = BlobRequestOptions.populateAndApplyDefaults(options, BlobType.APPEND_BLOB, this.blobServiceClient);

        if (sourceStream.markSupported()) {
            // Mark sourceStream for current position.
            sourceStream.mark(Constants.MAX_MARK_LENGTH);
        }

        final BlobOutputStream streamRef = this.openWriteNew(accessCondition, options, opContext);
        try {
            streamRef.write(sourceStream, length);
        }
        finally {
            streamRef.close();
        }
    }
    
    /**
     * Sets the number of bytes to buffer when writing to a {@link BlobOutputStream}.
     * 
     * @param streamWriteSizeInBytes
     *            An <code>int</code> which represents the maximum block size, in bytes, for writing to an append blob
     *            while using a {@link BlobOutputStream} object, ranging from 16 KB to 4 MB, inclusive.
     * 
     * @throws IllegalArgumentException
     *             If <code>streamWriteSizeInBytes</code> is less than 16 KB or greater than 4 MB.
     */
    @Override
    public void setStreamWriteSizeInBytes(final int streamWriteSizeInBytes) {
        if (streamWriteSizeInBytes > Constants.MAX_BLOCK_SIZE || streamWriteSizeInBytes < 16 * Constants.KB) {
            throw new IllegalArgumentException("StreamWriteSizeInBytes");
        }

        this.streamWriteSizeInBytes = streamWriteSizeInBytes;
    }
} public final class CloudBlobContainer {

    /**
     * Converts the ACL string to a BlobContainerPermissions object.
     * 
     * @param aclString
     *            A <code>String</code> which specifies the ACLs to convert.
     * 
     * @return A {@link BlobContainerPermissions} object which represents the ACLs.
     */
    static BlobContainerPermissions getContainerAcl(final String aclString) {
        BlobContainerPublicAccessType accessType = BlobContainerPublicAccessType.parse(aclString);

        final BlobContainerPermissions retVal = new BlobContainerPermissions();
        retVal.setPublicAccess(accessType);

        return retVal;
    }

    /**
     * Represents the container metadata.
     */
    protected HashMap<String, String> metadata = new HashMap<String, String>();

    /**
     * Holds the container properties.
     */
    BlobContainerProperties properties = new BlobContainerProperties();

    /**
     * Holds the name of the container.
     */
    String name;

    /**
     * Holds the list of URIs for all locations.
     */
    private StorageUri storageUri;

    /**
     * Holds a reference to the associated service client.
     */
    private CloudBlobClient blobServiceClient;
    
    /**
     * Creates an instance of the <code>CloudBlobContainer</code> class using the specified URI. The blob URI should
     * include a SAS token unless anonymous access is to be used.
     * 
     * @param uri
     *            A <code>java.net.URI</code> object which represents the URI of the container.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    public CloudBlobContainer(final URI uri) throws StorageException {
        this(new StorageUri(uri));
    }

    /**
     * Creates an instance of the <code>CloudBlobContainer</code> class using the specified URI. The blob URI should
     * include a SAS token unless anonymous access is to be used.
     * 
     * @param storageUri
     *            A {@link StorageUri} object which represents the URI of the container.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    public CloudBlobContainer(final StorageUri storageUri) throws StorageException {
        this(storageUri, (StorageCredentials) null);
    }
    
    /**
     * Creates an instance of the <code>CloudBlobContainer</code> class using the specified URI and credentials.
     * 
     * @param uri
     *            A <code>java.net.URI</code> object that represents the absolute URI of the container.
     * @param credentials
     *            A {@link StorageCredentials} object used to authenticate access.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    public CloudBlobContainer(final URI uri, final StorageCredentials credentials) throws StorageException {
        this(new StorageUri(uri), credentials);
    }

    /**
     * Creates an instance of the <code>CloudBlobContainer</code> class using the specified StorageUri and credentials.
     * 
     * @param storageUri
     *            A {@link StorageUri} object which represents the absolute StorageUri of the container.
     * @param credentials
     *            A {@link StorageCredentials} object used to authenticate access.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    public CloudBlobContainer(final StorageUri storageUri, final StorageCredentials credentials)
            throws StorageException {
        this.parseQueryAndVerify(storageUri, credentials);
    }

    /**
     * Creates an instance of the <code>CloudBlobContainer</code> class using the specified name and client.
     * 
     * @param containerName
     *            A <code>String</code> which represents the name of the container, which must adhere to container
     *            naming rules.
     *            The container name should not include any path separator characters (/).
     *            Container names must be lowercase, between 3-63 characters long and must start with a letter or
     *            number. Container names may contain only letters, numbers, and the dash (-) character.
     * @param client
     *            A {@link CloudBlobClient} object that represents the associated service client, and that specifies the
     *            endpoint for the Blob service. *
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws URISyntaxException
     *             If the resource URI constructed based on the containerName is invalid.
     * 
     * @see <a href="http://msdn.microsoft.com/library/azure/dd135715.aspx">Naming and Referencing Containers, Blobs,
     *      and Metadata</a>
     */
    protected CloudBlobContainer(final String containerName, final CloudBlobClient client) throws URISyntaxException,
            StorageException {
        Utility.assertNotNull("client", client);
        Utility.assertNotNull("containerName", containerName);

        this.storageUri = PathUtility.appendPathToUri(client.getStorageUri(), containerName);
        this.name = containerName;
        this.blobServiceClient = client;
    }

    /**
     * Creates the container.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public void create() throws StorageException {
        this.create(BlobContainerPublicAccessType.OFF, null /* options */, null /* opContext */);
    }

    /**
     * Creates the container using the specified options and operation context.
     *
     * @param options
     *            A {@link BlobRequestOptions} 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}).
     * @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.
     */
    @DoesServiceRequest
    public void create(BlobRequestOptions options, OperationContext opContext) throws StorageException {
        this.create(BlobContainerPublicAccessType.OFF, options, opContext);
    }

    /**
     * Creates the container using the specified options and operation context.
     *
     * @param accessType
     *            A {@link BlobContainerPublicAccessType} object that specifies whether data in the container may be
     *            accessed publicly and what level of access is to be allowed.
     * @param options
     *            A {@link BlobRequestOptions} 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}).
     * @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.
     */
    @DoesServiceRequest
    public void create(BlobContainerPublicAccessType accessType, BlobRequestOptions options, OperationContext opContext) throws StorageException {
        if (accessType == BlobContainerPublicAccessType.UNKNOWN) {
            throw new IllegalArgumentException(String.format(Utility.LOCALE_US, SR.ARGUMENT_OUT_OF_RANGE_ERROR, "accessType", accessType));
        }

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

        opContext.initialize();
        options = BlobRequestOptions.populateAndApplyDefaults(options, BlobType.UNSPECIFIED, this.blobServiceClient);

        ExecutionEngine.executeWithRetry(
                this.blobServiceClient, this, createImpl(options, accessType),
                options.getRetryPolicyFactory(), opContext);
    }

    private StorageRequest<CloudBlobClient, CloudBlobContainer, Void> createImpl(
            final BlobRequestOptions options, final BlobContainerPublicAccessType accessType) {
        final StorageRequest<CloudBlobClient, CloudBlobContainer, Void> putRequest = new StorageRequest<CloudBlobClient, CloudBlobContainer, Void>(
                options, this.getStorageUri()) {
            @Override
            public HttpURLConnection buildRequest(CloudBlobClient client, CloudBlobContainer container,
                    OperationContext context) throws Exception {
                final HttpURLConnection request = BlobRequest.createContainer(
                        container.getTransformedAddress().getUri(this.getCurrentLocation()), options, context, accessType);
                return request;
            }

            @Override
            public void setHeaders(HttpURLConnection connection, CloudBlobContainer container, OperationContext context) {
                BlobRequest.addMetadata(connection, container.metadata, context);
            }

            @Override
            public void signRequest(HttpURLConnection connection, CloudBlobClient client, OperationContext context)
                    throws Exception {
                StorageRequest.signBlobQueueAndFileRequest(connection, client, 0L, context);
            }

            @Override
            public Void preProcessResponse(CloudBlobContainer container, CloudBlobClient client,
                    OperationContext context) throws Exception {
                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_CREATED) {
                    this.setNonExceptionedRetryableFailure(true);
                    return null;
                }

                // Set attributes
                final BlobContainerAttributes attributes = BlobResponse.getBlobContainerAttributes(
                        this.getConnection(), client.isUsePathStyleUris());
                container.properties = attributes.getProperties();
                container.name = attributes.getName();
                if (accessType != null) {
                    container.properties.setPublicAccess(accessType);
                }
                else {
                    container.properties.setPublicAccess(BlobContainerPublicAccessType.OFF);
                }

                return null;
            }
        };

        return putRequest;
    }

    /**
     * Creates the container if it does not exist.
     * 
     * @return <code>true</code> if the container did not already exist and was created; otherwise, <code>false</code>.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public boolean createIfNotExists() throws StorageException {
        return this.createIfNotExists(BlobContainerPublicAccessType.OFF, null /* options */, null /* opContext */);
    }

    /**
     * Creates the container if it does not exist, using the specified request options and operation context.
     *
     * @param options
     *            A {@link BlobRequestOptions} 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}).
     * @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 <code>true</code> if the container did not already exist and was created; otherwise, <code>false</code>.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public boolean createIfNotExists(BlobRequestOptions options, OperationContext opContext) throws StorageException {
        return this.createIfNotExists(BlobContainerPublicAccessType.OFF, options, opContext);
    }

    /**
     * Creates the container if it does not exist, using the specified request options and operation context.
     * 
     * @param accessType
     *            A {@link BlobContainerPublicAccessType} object that specifies whether data in the container may be
     *            accessed publicly and what level of access is to be allowed.
     * @param options
     *            A {@link BlobRequestOptions} 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}).
     * @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 <code>true</code> if the container did not already exist and was created; otherwise, <code>false</code>.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public boolean createIfNotExists(BlobContainerPublicAccessType accessType, BlobRequestOptions options, OperationContext opContext) throws StorageException {
        if (accessType == BlobContainerPublicAccessType.UNKNOWN) {
            throw new IllegalArgumentException(String.format(Utility.LOCALE_US, SR.ARGUMENT_OUT_OF_RANGE_ERROR, "accessType", accessType));
        }

        options = BlobRequestOptions.populateAndApplyDefaults(options, BlobType.UNSPECIFIED, this.blobServiceClient);

        boolean exists = this.exists(true /* primaryOnly */, null /* accessCondition */, options, opContext);
        if (exists) {
            return false;
        }
        else {
            try {
                this.create(accessType, options, opContext);
                return true;
            }
            catch (StorageException e) {
                if (e.getHttpStatusCode() == HttpURLConnection.HTTP_CONFLICT
                        && StorageErrorCodeStrings.CONTAINER_ALREADY_EXISTS.equals(e.getErrorCode())) {
                    return false;
                }
                else {
                    throw e;
                }
            }
        }
    }

    /**
     * Deletes the container.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public void delete() throws StorageException {
        this.delete(null /* accessCondition */, null /* options */, null /* opContext */);
    }

    /**
     * Deletes the container using the specified request options and operation context.
     * 
     * @param accessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the container.
     * @param options
     *            A {@link BlobRequestOptions} 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}).
     * @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.
     */
    @DoesServiceRequest
    public void delete(AccessCondition accessCondition, BlobRequestOptions options, OperationContext opContext)
            throws StorageException {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        opContext.initialize();
        options = BlobRequestOptions.populateAndApplyDefaults(options, BlobType.UNSPECIFIED, this.blobServiceClient);

        ExecutionEngine.executeWithRetry(this.blobServiceClient, this, deleteImpl(accessCondition, options),
                options.getRetryPolicyFactory(), opContext);
    }

    private StorageRequest<CloudBlobClient, CloudBlobContainer, Void> deleteImpl(
            final AccessCondition accessCondition, final BlobRequestOptions options) {
        final StorageRequest<CloudBlobClient, CloudBlobContainer, Void> putRequest =
                new StorageRequest<CloudBlobClient, CloudBlobContainer, Void>(options, this.getStorageUri()) {
            @Override
            public HttpURLConnection buildRequest(
                    CloudBlobClient client, CloudBlobContainer container, OperationContext context) throws Exception {
                return BlobRequest.deleteContainer(
                        container.getTransformedAddress().getPrimaryUri(), options, context, accessCondition);
            }

            @Override
            public void signRequest(HttpURLConnection connection, CloudBlobClient client, OperationContext context)
                    throws Exception {
                StorageRequest.signBlobQueueAndFileRequest(connection, client, -1L, context);
            }

            @Override
            public Void preProcessResponse(CloudBlobContainer container, CloudBlobClient client, OperationContext context)
                    throws Exception {
                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_ACCEPTED) {
                    this.setNonExceptionedRetryableFailure(true);
                }

                return null;
            }
        };

        return putRequest;
    }

    /**
     * Deletes the container if it exists.
     * 
     * @return <code>true</code> if the container did not already exist and was created; otherwise, <code>false</code>.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public boolean deleteIfExists() throws StorageException {
        return this.deleteIfExists(null /* accessCondition */, null /* options */, null /* opContext */);
    }

    /**
     * Deletes the container if it exists using the specified request options and operation context.
     * 
     * @param accessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the container.
     * @param options
     *            A {@link BlobRequestOptions} 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}).
     * @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 <code>true</code> if the container existed and was deleted; otherwise, <code>false</code>.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public boolean deleteIfExists(AccessCondition accessCondition, BlobRequestOptions options,
            OperationContext opContext) throws StorageException {
        options = BlobRequestOptions.populateAndApplyDefaults(options, BlobType.UNSPECIFIED, this.blobServiceClient);

        boolean exists = this.exists(true /* primaryOnly */, accessCondition, options, opContext);
        if (exists) {
            try {
                this.delete(accessCondition, options, opContext);
                return true;
            }
            catch (StorageException e) {
                if (e.getHttpStatusCode() == HttpURLConnection.HTTP_NOT_FOUND
                        && StorageErrorCodeStrings.CONTAINER_NOT_FOUND.equals(e.getErrorCode())) {
                    return false;
                }
                else {
                    throw e;
                }
            }
        }
        else {
            return false;
        }
    }

    /**
     * Downloads the container's attributes, which consist of metadata and properties.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public void downloadAttributes() throws StorageException {
        this.downloadAttributes(null /* accessCondition */, null /* options */, null /* opContext */);
    }

    /**
     * Downloads the container's attributes, which consist of metadata and properties, using the specified request
     * options and operation context.
     * 
     * @param accessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the container.
     * @param options
     *            A {@link BlobRequestOptions} 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}).
     * @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.
     */
    @DoesServiceRequest
    public void downloadAttributes(AccessCondition accessCondition, BlobRequestOptions options,
            OperationContext opContext) throws StorageException {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        opContext.initialize();
        options = BlobRequestOptions.populateAndApplyDefaults(options, BlobType.UNSPECIFIED, this.blobServiceClient);

        ExecutionEngine.executeWithRetry(this.blobServiceClient, this,
                this.downloadAttributesImpl(accessCondition, options), options.getRetryPolicyFactory(), opContext);
    }

    private StorageRequest<CloudBlobClient, CloudBlobContainer, Void> downloadAttributesImpl(
            final AccessCondition accessCondition, final BlobRequestOptions options) {
        final StorageRequest<CloudBlobClient, CloudBlobContainer, Void> getRequest = new StorageRequest<CloudBlobClient, CloudBlobContainer, Void>(
                options, this.getStorageUri()) {

            @Override
            public void setRequestLocationMode() {
                this.setRequestLocationMode(RequestLocationMode.PRIMARY_OR_SECONDARY);
            }

            @Override
            public HttpURLConnection buildRequest(CloudBlobClient client, CloudBlobContainer container,
                    OperationContext context) throws Exception {
                return BlobRequest.getContainerProperties(
                        container.getTransformedAddress().getUri(this.getCurrentLocation()), options, context,
                        accessCondition);
            }

            @Override
            public void signRequest(HttpURLConnection connection, CloudBlobClient client, OperationContext context)
                    throws Exception {
                StorageRequest.signBlobQueueAndFileRequest(connection, client, -1L, context);
            }

            @Override
            public Void preProcessResponse(CloudBlobContainer container, CloudBlobClient client,
                    OperationContext context) throws Exception {
                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_OK) {
                    this.setNonExceptionedRetryableFailure(true);
                    return null;
                }

                // Set attributes
                final BlobContainerAttributes attributes = BlobResponse.getBlobContainerAttributes(
                        this.getConnection(), client.isUsePathStyleUris());
                container.metadata = attributes.getMetadata();
                container.properties = attributes.getProperties();
                container.name = attributes.getName();
                return null;
            }
        };

        return getRequest;
    }

    /**
     * Downloads the permission settings for the container.
     * 
     * @return A {@link BlobContainerPermissions} object that represents the container's permissions.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public BlobContainerPermissions downloadPermissions() throws StorageException {
        return this.downloadPermissions(null /* accessCondition */, null /* options */, null /* opContext */);
    }

    /**
     * Downloads the permissions settings for the container using the specified request options and operation context.
     * 
     * @param accessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the container.
     * @param options
     *            A {@link BlobRequestOptions} 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}).
     * @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 BlobContainerPermissions} object that represents the container's permissions.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public BlobContainerPermissions downloadPermissions(AccessCondition accessCondition, BlobRequestOptions options,
            OperationContext opContext) throws StorageException {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        opContext.initialize();
        options = BlobRequestOptions.populateAndApplyDefaults(options, BlobType.UNSPECIFIED, this.blobServiceClient);

        return ExecutionEngine.executeWithRetry(this.blobServiceClient, this,
                downloadPermissionsImpl(accessCondition, options), options.getRetryPolicyFactory(), opContext);
    }

    private StorageRequest<CloudBlobClient, CloudBlobContainer, BlobContainerPermissions> downloadPermissionsImpl(
            final AccessCondition accessCondition, final BlobRequestOptions options) {
        final StorageRequest<CloudBlobClient, CloudBlobContainer, BlobContainerPermissions> getRequest = new StorageRequest<CloudBlobClient, CloudBlobContainer, BlobContainerPermissions>(
                options, this.getStorageUri()) {

            @Override
            public void setRequestLocationMode() {
                this.setRequestLocationMode(RequestLocationMode.PRIMARY_OR_SECONDARY);
            }

            @Override
            public HttpURLConnection buildRequest(CloudBlobClient client, CloudBlobContainer container,
                    OperationContext context) throws Exception {
                return BlobRequest.getAcl(container.getTransformedAddress().getUri(this.getCurrentLocation()), options,
                        accessCondition, context);
            }

            @Override
            public void signRequest(HttpURLConnection connection, CloudBlobClient client, OperationContext context)
                    throws Exception {
                StorageRequest.signBlobQueueAndFileRequest(connection, client, -1L, context);
            }

            @Override
            public BlobContainerPermissions preProcessResponse(CloudBlobContainer container, CloudBlobClient client,
                    OperationContext context) throws Exception {
                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_OK) {
                    this.setNonExceptionedRetryableFailure(true);
                }

                container.updatePropertiesFromResponse(this.getConnection());
                final String aclString = BlobResponse.getAcl(this.getConnection());
                final BlobContainerPermissions containerAcl = getContainerAcl(aclString);
                container.properties.setPublicAccess(containerAcl.getPublicAccess());
                return containerAcl;
            }

            @Override
            public BlobContainerPermissions postProcessResponse(HttpURLConnection connection,
                    CloudBlobContainer container, CloudBlobClient client, OperationContext context,
                    BlobContainerPermissions containerAcl) throws Exception {
                HashMap<String, SharedAccessBlobPolicy> accessIds = SharedAccessPolicyHandler.getAccessIdentifiers(this
                        .getConnection().getInputStream(), SharedAccessBlobPolicy.class);

                for (final String key : accessIds.keySet()) {
                    containerAcl.getSharedAccessPolicies().put(key, accessIds.get(key));
                }

                return containerAcl;
            }
        };

        return getRequest;
    }

    /**
     * Returns a value that indicates whether the container exists.
     * 
     * @return <code>true</code> if the container exists, otherwise <code>false</code>.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public boolean exists() throws StorageException {
        return this.exists(null /* accessCondition */, null /* options */, null /* opContext */);
    }

    /**
     * Returns a value that indicates whether the container exists, using the specified request options and operation
     * context.
     * 
     * @param accessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the container.
     * @param options
     *            A {@link BlobRequestOptions} 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}).
     * @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 <code>true</code> if the container exists, otherwise <code>false</code>.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public boolean exists(final AccessCondition accessCondition, BlobRequestOptions options, OperationContext opContext)
            throws StorageException {
        return this.exists(false, accessCondition, options, opContext);
    }

    @DoesServiceRequest
    private boolean exists(final boolean primaryOnly, final AccessCondition accessCondition,
            BlobRequestOptions options, OperationContext opContext) throws StorageException {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        opContext.initialize();
        options = BlobRequestOptions.populateAndApplyDefaults(options, BlobType.UNSPECIFIED, this.blobServiceClient);

        return ExecutionEngine.executeWithRetry(this.blobServiceClient, this,
                this.existsImpl(primaryOnly, accessCondition, options), options.getRetryPolicyFactory(), opContext);
    }

    private StorageRequest<CloudBlobClient, CloudBlobContainer, Boolean> existsImpl(final boolean primaryOnly,
            final AccessCondition accessCondition, final BlobRequestOptions options) {
        final StorageRequest<CloudBlobClient, CloudBlobContainer, Boolean> getRequest = new StorageRequest<CloudBlobClient, CloudBlobContainer, Boolean>(
                options, this.getStorageUri()) {

            @Override
            public void setRequestLocationMode() {
                this.setRequestLocationMode(primaryOnly ? RequestLocationMode.PRIMARY_ONLY
                        : RequestLocationMode.PRIMARY_OR_SECONDARY);
            }

            @Override
            public HttpURLConnection buildRequest(CloudBlobClient client, CloudBlobContainer container,
                    OperationContext context) throws Exception {
                return BlobRequest.getContainerProperties(
                        container.getTransformedAddress().getUri(this.getCurrentLocation()), options, context,
                        accessCondition);
            }

            @Override
            public void signRequest(HttpURLConnection connection, CloudBlobClient client, OperationContext context)
                    throws Exception {
                StorageRequest.signBlobQueueAndFileRequest(connection, client, -1L, context);
            }

            @Override
            public Boolean preProcessResponse(CloudBlobContainer container, CloudBlobClient client,
                    OperationContext context) throws Exception {
                if (this.getResult().getStatusCode() == HttpURLConnection.HTTP_OK) {
                    // Set attributes
                    final BlobContainerAttributes attributes = BlobResponse.getBlobContainerAttributes(
                            this.getConnection(), client.isUsePathStyleUris());
                    container.metadata = attributes.getMetadata();
                    container.properties = attributes.getProperties();
                    container.name = attributes.getName();
                    return Boolean.valueOf(true);
                }
                else if (this.getResult().getStatusCode() == HttpURLConnection.HTTP_NOT_FOUND) {
                    return Boolean.valueOf(false);
                }
                else {
                    this.setNonExceptionedRetryableFailure(true);
                    // return false instead of null to avoid SCA issues
                    return false;
                }
            }
        };

        return getRequest;
    }

    /**
     * Returns a shared access signature for the container. Note this does not contain the leading "?".
     * 
     * @param policy
     *            An {@link SharedAccessBlobPolicy} object that represents the access policy for the shared access
     *            signature.
     * @param groupPolicyIdentifier
     *            A <code>String</code> which represents the container-level access policy.
     * 
     * @return A <code>String</code> which represents a shared access signature for the container.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws InvalidKeyException
     *             If the key is invalid.
     */
    public String generateSharedAccessSignature(final SharedAccessBlobPolicy policy, final String groupPolicyIdentifier)
            throws InvalidKeyException, StorageException {

        return this.generateSharedAccessSignature(
                policy, groupPolicyIdentifier, null /* IP range */, null /* protocols */);
    }
    
    /**
     * Returns a shared access signature for the container. Note this does not contain the leading "?".
     * 
     * @param policy
     *            An {@link SharedAccessBlobPolicy} object that represents the access policy for the shared access
     *            signature.
     * @param groupPolicyIdentifier
     *            A <code>String</code> which represents the container-level access policy.
     * @param ipRange
     *            A {@link IPRange} object containing the range of allowed IP addresses.
     * @param protocols
     *            A {@link SharedAccessProtocols} representing the allowed Internet protocols.
     * 
     * @return A <code>String</code> which represents a shared access signature for the container.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws InvalidKeyException
     *             If the key is invalid.
     */
    public String generateSharedAccessSignature(final SharedAccessBlobPolicy policy,
            final String groupPolicyIdentifier, final IPRange ipRange, final SharedAccessProtocols protocols)
            throws InvalidKeyException, StorageException {
        if (!StorageCredentialsHelper.canCredentialsSignRequest(this.blobServiceClient.getCredentials())) {
            final String errorMessage = SR.CANNOT_CREATE_SAS_WITHOUT_ACCOUNT_KEY;
            throw new IllegalArgumentException(errorMessage);
        }
        
        final String resourceName = this.getSharedAccessCanonicalName();

        final String signature = SharedAccessSignatureHelper.generateSharedAccessSignatureHashForBlobAndFile(
                policy, null /* SharedAccessBlobHeaders */, groupPolicyIdentifier, resourceName,
                ipRange, protocols, this.blobServiceClient);

        final UriQueryBuilder builder = SharedAccessSignatureHelper.generateSharedAccessSignatureForBlobAndFile(policy,
                null /* SharedAccessBlobHeaders */, groupPolicyIdentifier, "c", ipRange, protocols, signature);

        return builder.toString();
    }

    /**
     * Returns a reference to a {@link CloudAppendBlob} object that represents an append blob in this container.
     * 
     * @param blobName
     *            A <code>String</code> that represents the name of the blob.
     * 
     * @return A {@link CloudAppendBlob} object that represents a reference to the specified append blob.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws URISyntaxException
     *             If the resource URI is invalid.
     */
    public CloudAppendBlob getAppendBlobReference(final String blobName) throws URISyntaxException, StorageException {
        return this.getAppendBlobReference(blobName, null);
    }

    /**
     * Returns a reference to a {@link CloudAppendBlob} object that represents an append blob in the container, using the
     * specified snapshot ID.
     * 
     * @param blobName
     *            A <code>String</code> that represents the name of the blob.
     * @param snapshotID
     *            A <code>String</code> that represents the snapshot ID of the blob.
     * 
     * @return A {@link CloudAppendBlob} object that represents a reference to the specified append blob.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws URISyntaxException
     *             If the resource URI is invalid.
     */
    public CloudAppendBlob getAppendBlobReference(final String blobName, final String snapshotID)
            throws URISyntaxException, StorageException {
        return new CloudAppendBlob(blobName, snapshotID, this);
    }
    
    /**
     * Returns a reference to a {@link CloudBlockBlob} object that represents a block blob in this container.
     * 
     * @param blobName
     *            A <code>String</code> that represents the name of the blob.
     * 
     * @return A {@link CloudBlockBlob} object that represents a reference to the specified block blob.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws URISyntaxException
     *             If the resource URI is invalid.
     */
    public CloudBlockBlob getBlockBlobReference(final String blobName) throws URISyntaxException, StorageException {
        return this.getBlockBlobReference(blobName, null);
    }

    /**
     * Returns a reference to a {@link CloudBlockBlob} object that represents a block blob in this container, using the
     * specified snapshot ID.
     * 
     * @param blobName
     *            A <code>String</code> that represents the name of the blob.
     * @param snapshotID
     *            A <code>String</code> that represents the snapshot ID of the blob.
     * 
     * @return A {@link CloudBlockBlob} object that represents a reference to the specified block blob.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws URISyntaxException
     *             If the resource URI is invalid.
     */
    public CloudBlockBlob getBlockBlobReference(final String blobName, final String snapshotID)
            throws URISyntaxException, StorageException {
        return new CloudBlockBlob(blobName, snapshotID, this);
    }

    /**
     * Returns a reference to a {@link CloudBlobDirectory} object that represents a virtual blob directory within this
     * container.
     * 
     * @param directoryName
     *            A <code>String</code> that represents the name of the virtual blob directory. If the root directory
     *            (the directory representing the container itself) is desired, use an empty string.
     * @return A {@link CloudBlobDirectory} that represents a virtual blob directory within this container.
     * @throws URISyntaxException
     *             If the resource URI is invalid.
     */
    public CloudBlobDirectory getDirectoryReference(String directoryName) throws URISyntaxException {
        Utility.assertNotNull("directoryName", directoryName);

        // if the directory name does not end in the delimiter, add the delimiter
        if (!directoryName.isEmpty() && !directoryName.endsWith(this.blobServiceClient.getDirectoryDelimiter())) {
            directoryName = directoryName.concat(this.blobServiceClient.getDirectoryDelimiter());
        }

        final StorageUri address = PathUtility.appendPathToUri(this.storageUri, directoryName);

        return new CloudBlobDirectory(address, directoryName, this.blobServiceClient, this);
    }
    
    
    /**
     * Gets a reference to a blob in this container. The blob must already exist on the service.
     * 
     * Unlike the other get*Reference methods, this method does a service request to retrieve the blob's metadata and 
     * properties. The returned blob may be used directly as a CloudBlob or cast using either instanceof or 
     * getProperties().getBlobType() to determine its subtype.
     *
     * @param blobName
     *            A <code>String</code> that represents the name of the blob.
     *
     * @return A {@link CloudBlob} object that represents a reference to the specified blob.
     *
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws URISyntaxException
     *             If the resource URI is invalid.
     */
    @DoesServiceRequest
    public final CloudBlob getBlobReferenceFromServer(final String blobName) throws URISyntaxException, StorageException {
        return this.getBlobReferenceFromServer(blobName, null, null, null, null);
    }

    /**
     * Gets a reference to a blob in this container. The blob must already exist on the service.
     * 
     * Unlike the other get*Reference methods, this method does a service request to retrieve the blob's metadata and
     * properties. The returned blob may be used directly as a CloudBlob or cast using either instanceof or
     * getProperties().getBlobType() to determine its subtype.
     * 
     * @param blobName
     *            A <code>String</code> that represents the name of the blob.
     * @param snapshotID
     *            A <code>String</code> that represents the snapshot ID of the blob.
     * @param accessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the blob.
     * @param options
     *            A {@link BlobRequestOptions} 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}).
     * @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 CloudBlob} object that represents a reference to the specified blob.
     *
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws URISyntaxException
     *             If the resource URI is invalid.
     */
    @DoesServiceRequest
    public final CloudBlob getBlobReferenceFromServer(final String blobName, final String snapshotID,
            AccessCondition accessCondition, BlobRequestOptions options, OperationContext opContext)
            throws URISyntaxException, StorageException {
        if (opContext == null) {
            opContext = new OperationContext();
        }
        options = BlobRequestOptions.populateAndApplyDefaults(options, BlobType.UNSPECIFIED, this.blobServiceClient);
        
        StorageUri blobUri = PathUtility.appendPathToUri(this.getStorageUri(), blobName);
        return ExecutionEngine.executeWithRetry(this.blobServiceClient, this,
                this.getBlobReferenceFromServerImpl(blobName, blobUri, snapshotID, accessCondition, options), options.getRetryPolicyFactory(), opContext);
    }
    
    private StorageRequest<CloudBlobClient, CloudBlobContainer, CloudBlob> getBlobReferenceFromServerImpl(
            final String blobName, final StorageUri blobUri, final String snapshotID, final AccessCondition accessCondition,
            final BlobRequestOptions options) {
        final StorageRequest<CloudBlobClient, CloudBlobContainer, CloudBlob> getRequest = new StorageRequest<CloudBlobClient, CloudBlobContainer, CloudBlob>(
                options, this.getStorageUri()) {

            @Override
            public void setRequestLocationMode() {
                this.setRequestLocationMode(RequestLocationMode.PRIMARY_OR_SECONDARY);
            }

            @Override
            public HttpURLConnection buildRequest(CloudBlobClient client, CloudBlobContainer container,
                    OperationContext context) throws Exception {
                StorageUri transformedblobUri = CloudBlobContainer.this.getServiceClient().getCredentials().transformUri(blobUri, context);                 
                return BlobRequest.getBlobProperties(transformedblobUri.getUri(this.getCurrentLocation()), options, context, accessCondition, snapshotID);
            }

            @Override
            public void signRequest(HttpURLConnection connection, CloudBlobClient client, OperationContext context)
                    throws Exception {
                StorageRequest.signBlobQueueAndFileRequest(connection, client, -1L, context);
            }

            @Override
            public CloudBlob preProcessResponse(CloudBlobContainer container, CloudBlobClient client, OperationContext context)
                    throws Exception {
                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_OK) {
                    this.setNonExceptionedRetryableFailure(true);
                    return null;
                }

                // Set attributes
                final BlobAttributes retrievedAttributes = BlobResponse.getBlobAttributes(this.getConnection(),
                        blobUri, snapshotID);

                CloudBlob blob;
                switch (retrievedAttributes.getProperties().getBlobType()) {
                    case APPEND_BLOB:
                        blob = container.getAppendBlobReference(blobName, snapshotID);
                        break;
    
                    case BLOCK_BLOB:
                        blob = container.getBlockBlobReference(blobName, snapshotID);
                        break;
    
                    case PAGE_BLOB:
                        blob = container.getPageBlobReference(blobName, snapshotID);
                        break;
    
                    default:
                        throw new StorageException(StorageErrorCodeStrings.INCORRECT_BLOB_TYPE,
                                SR.INVALID_RESPONSE_RECEIVED, Constants.HeaderConstants.HTTP_UNUSED_306, null, null);
                }

                blob.properties = retrievedAttributes.getProperties();
                blob.metadata = retrievedAttributes.getMetadata();

                return blob;
            }
        };

        return getRequest;
    }
    
    /**
     * Returns the metadata for the container. This value is initialized with the metadata from the queue by a call to
     * {@link #downloadAttributes}, and is set on the queue with a call to {@link #uploadMetadata}.
     * 
     * @return A <code>java.util.HashMap</code> object that represents the metadata for the container.
     */
    public HashMap<String, String> getMetadata() {
        return this.metadata;
    }

    /**
     * Returns the name of the container.
     * 
     * @return A <code>String</code> that represents the name of the container.
     */
    public String getName() {
        return this.name;
    }

    /**
     * Returns the list of URIs for all locations.
     * 
     * @return A {@link StorageUri} object which represents the list of URIs for all locations..
     */
    public StorageUri getStorageUri() {
        return this.storageUri;
    }

    /**
     * Returns a reference to a {@link CloudPageBlob} object that represents a page blob in this container.
     * 
     * @param blobName
     *            A <code>String</code> that represents the name of the blob.
     * 
     * @return A {@link CloudPageBlob} object that represents a reference to the specified page blob.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws URISyntaxException
     *             If the resource URI is invalid.
     */
    public CloudPageBlob getPageBlobReference(final String blobName) throws URISyntaxException, StorageException {
        return this.getPageBlobReference(blobName, null);
    }

    /**
     * Returns a reference to a {@link CloudPageBlob} object that represents a page blob in the container, using the
     * specified snapshot ID.
     * 
     * @param blobName
     *            A <code>String</code> that represents the name of the blob.
     * @param snapshotID
     *            A <code>String</code> that represents the snapshot ID of the blob.
     * 
     * @return A {@link CloudPageBlob} object that represents a reference to the specified page blob.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws URISyntaxException
     *             If the resource URI is invalid.
     */
    public CloudPageBlob getPageBlobReference(final String blobName, final String snapshotID)
            throws URISyntaxException, StorageException {
        return new CloudPageBlob(blobName, snapshotID, this);
    }

    /**
     * Returns the properties for the container.
     * 
     * @return A {@link BlobContainerProperties} object that represents the properties for the container.
     */
    public BlobContainerProperties getProperties() {
        return this.properties;
    }

    /**
     * Returns the Blob service client associated with this container.
     * 
     * @return A {@link CloudBlobClient} object that represents the service client associated with this container.
     */
    public CloudBlobClient getServiceClient() {
        return this.blobServiceClient;
    }

    /**
     * Returns the canonical name for shared access.
     * 
     * @return the canonical name for shared access.
     */
    private String getSharedAccessCanonicalName() {
        String accountName = this.getServiceClient().getCredentials().getAccountName();
        String containerName = this.getName();

        return String.format("/%s/%s/%s", SR.BLOB, accountName, containerName);
    }

    /**
     * Returns the URI after applying the authentication transformation.
     * 
     * @return A <code>java.net.URI</code> object that represents the URI after applying the authentication
     *         transformation.
     * 
     * @throws IllegalArgumentException
     *             If an unexpected value is passed.
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws URISyntaxException
     *             If the resource URI is invalid.
     */
    private StorageUri getTransformedAddress() throws URISyntaxException, StorageException {
        return this.blobServiceClient.getCredentials().transformUri(this.storageUri);
    }

    /**
     * Returns the URI for this container.
     * 
     * @return The absolute URI to the container.
     */
    public URI getUri() {
        return this.storageUri.getPrimaryUri();
    }

    /**
     * Returns an enumerable collection of blob items for the container.
     * 
     * @return An enumerable collection of {@link ListBlobItem} objects retrieved lazily that represents the items in
     *         this container.
     */
    @DoesServiceRequest
    public Iterable<ListBlobItem> listBlobs() {
        return this.listBlobs(null, false, EnumSet.noneOf(BlobListingDetails.class), null, null);
    }

    /**
     * Returns an enumerable collection of blob items for the container whose names begin with the specified prefix.
     * 
     * @param prefix
     *            A <code>String</code> that represents the blob name prefix. This value must be preceded either by the
     *            name of the container or by the absolute path to the container.
     * 
     * @return An enumerable collection of {@link ListBlobItem} objects retrieved lazily that represents the
     *         items whose names begin with the specified prefix in this container.
     */
    @DoesServiceRequest
    public Iterable<ListBlobItem> listBlobs(final String prefix) {
        return this.listBlobs(prefix, false);
    }
    
    /**
     * Returns an enumerable collection of blob items for the container whose names begin with the specified prefix
     * using the specified flat or hierarchical option.
     * 
     * @param prefix
     *            A <code>String</code> that represents the blob name prefix. This value must be preceded either by the
     *            name of the container or by the absolute path to the container.
     * @param useFlatBlobListing
     *            <code>true</code> to indicate that the returned list will be flat; <code>false</code> to indicate that
     *            the returned list will be hierarchical.
     * @return An enumerable collection of {@link ListBlobItem} objects retrieved lazily that represents the
     *         items whose names begin with the specified prefix in this container.
     */
    @DoesServiceRequest
    public Iterable<ListBlobItem> listBlobs(final String prefix, final boolean useFlatBlobListing) {
        return this.listBlobs(prefix, useFlatBlobListing, EnumSet.noneOf(BlobListingDetails.class), null, null);
    }

    /**
     * Returns an enumerable collection of blob items for the container whose names begin with the specified prefix, 
     * using the specified flat or hierarchical option, listing details options, request options, and operation context.
     * 
     * @param prefix
     *            A <code>String</code> that represents the blob name prefix. This value must be preceded either by the
     *            name of the container or by the absolute path to the container.
     * @param useFlatBlobListing
     *            <code>true</code> to indicate that the returned list will be flat; <code>false</code> to indicate that
     *            the returned list will be hierarchical.
     * @param listingDetails
     *            A <code>java.util.EnumSet</code> object that contains {@link BlobListingDetails} values that indicate
     *            whether snapshots, metadata, and/or uncommitted blocks are returned. Committed blocks are always
     *            returned.
     * @param options
     *            A {@link BlobRequestOptions} 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}).
     * @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 An enumerable collection of {@link ListBlobItem} objects retrieved lazily that represents the
     *         items whose names begin with the specified prefix in this container.
     */
    @DoesServiceRequest
    public Iterable<ListBlobItem> listBlobs(final String prefix, final boolean useFlatBlobListing,
            final EnumSet<BlobListingDetails> listingDetails, BlobRequestOptions options, OperationContext opContext) {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        opContext.initialize();
        options = BlobRequestOptions.populateAndApplyDefaults(options, BlobType.UNSPECIFIED, this.blobServiceClient);

        if (!useFlatBlobListing && listingDetails != null && listingDetails.contains(BlobListingDetails.SNAPSHOTS)) {
            throw new IllegalArgumentException(SR.SNAPSHOT_LISTING_ERROR);
        }

        SegmentedStorageRequest segmentedRequest = new SegmentedStorageRequest();

        return new LazySegmentedIterable<CloudBlobClient, CloudBlobContainer, ListBlobItem>(
                this.listBlobsSegmentedImpl(prefix, useFlatBlobListing, listingDetails, null, options, segmentedRequest),
                this.blobServiceClient, this, options.getRetryPolicyFactory(), opContext);
    }

    /**
     * Returns a result segment of an enumerable collection of blob items in the container.
     * 
     * @return A {@link ResultSegment} object that contains a segment of the enumerable collection of
     *         {@link ListBlobItem} objects that represent the blob items in the container.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public ResultSegment<ListBlobItem> listBlobsSegmented() throws StorageException {
        return this.listBlobsSegmented(null, false, EnumSet.noneOf(BlobListingDetails.class), null, null, null, null);
    }

    /**
     * Returns a result segment containing a collection of blob items whose names begin with the specified prefix.
     * 
     * @param prefix
     *            A <code>String</code> that represents the prefix of the blob name.
     * 
     * @return A {@link ResultSegment} object that contains a segment of the enumerable collection of
     *         {@link ListBlobItem} objects that represent the blob items whose names begin with the specified prefix in
     *         the container.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public ResultSegment<ListBlobItem> listBlobsSegmented(final String prefix) throws StorageException {
        return this.listBlobsSegmented(prefix, false, EnumSet.noneOf(BlobListingDetails.class), null, null, null, null);
    }

    /**
     * Returns a result segment containing a collection of blob items whose names begin with the
     * specified prefix, using the specified flat or hierarchical option, listing details options,
     * request options, and operation context.
     * 
     * @param prefix
     *            A <code>String</code> that represents the prefix of the blob name.
     * @param useFlatBlobListing
     *            <code>true</code> to indicate that the returned list will be flat;
     *            <code>false</code> to indicate that the returned list will be hierarchical.
     * @param listingDetails
     *            A <code>java.util.EnumSet</code> object that contains {@link BlobListingDetails} 
     *            values that indicate whether snapshots, metadata, and/or uncommitted blocks
     *            are returned. Committed blocks are always returned.
     * @param maxResults
     *            The maximum number of results to retrieve.  If <code>null</code> or greater
     *            than 5000, the server will return up to 5,000 items.  Must be at least 1.
     * @param continuationToken
     *            A {@link ResultContinuation} object that represents a continuation token returned
     *            by a previous listing operation.
     * @param options
     *            A {@link BlobRequestOptions} 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}).
     * @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} object that contains a segment of the enumerable collection
     *         of {@link ListBlobItem} objects that represent the block items whose names begin
     *         with the specified prefix in the container.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public ResultSegment<ListBlobItem> listBlobsSegmented(final String prefix, final boolean useFlatBlobListing,
            final EnumSet<BlobListingDetails> listingDetails, final Integer maxResults,
            final ResultContinuation continuationToken, BlobRequestOptions options, OperationContext opContext)
            throws StorageException {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        opContext.initialize();
        options = BlobRequestOptions.populateAndApplyDefaults(options, BlobType.UNSPECIFIED, this.blobServiceClient);

        Utility.assertContinuationType(continuationToken, ResultContinuationType.BLOB);

        if (!useFlatBlobListing && listingDetails != null && listingDetails.contains(BlobListingDetails.SNAPSHOTS)) {
            throw new IllegalArgumentException(SR.SNAPSHOT_LISTING_ERROR);
        }

        SegmentedStorageRequest segmentedRequest = new SegmentedStorageRequest();
        segmentedRequest.setToken(continuationToken);

        return ExecutionEngine.executeWithRetry(this.blobServiceClient, this, this.listBlobsSegmentedImpl(prefix,
                useFlatBlobListing, listingDetails, maxResults, options, segmentedRequest), options
                .getRetryPolicyFactory(), opContext);
    }

    private StorageRequest<CloudBlobClient, CloudBlobContainer, ResultSegment<ListBlobItem>> listBlobsSegmentedImpl(
            final String prefix, final boolean useFlatBlobListing, final EnumSet<BlobListingDetails> listingDetails,
            final Integer maxResults, final BlobRequestOptions options, final SegmentedStorageRequest segmentedRequest) {

        Utility.assertContinuationType(segmentedRequest.getToken(), ResultContinuationType.BLOB);
        Utility.assertNotNull("options", options);

        final String delimiter = useFlatBlobListing ? null : this.blobServiceClient.getDirectoryDelimiter();

        final BlobListingContext listingContext = new BlobListingContext(prefix, maxResults, delimiter, listingDetails);

        final StorageRequest<CloudBlobClient, CloudBlobContainer, ResultSegment<ListBlobItem>> getRequest = new StorageRequest<CloudBlobClient, CloudBlobContainer, ResultSegment<ListBlobItem>>(
                options, this.getStorageUri()) {

            @Override
            public void setRequestLocationMode() {
                this.setRequestLocationMode(Utility.getListingLocationMode(segmentedRequest.getToken()));
            }

            @Override
            public HttpURLConnection buildRequest(CloudBlobClient client, CloudBlobContainer container,
                    OperationContext context) throws Exception {
                listingContext.setMarker(segmentedRequest.getToken() != null ? segmentedRequest.getToken()
                        .getNextMarker() : null);
                return BlobRequest.listBlobs(container.getTransformedAddress().getUri(this.getCurrentLocation()),
                        options, context, listingContext);
            }

            @Override
            public void signRequest(HttpURLConnection connection, CloudBlobClient client, OperationContext context)
                    throws Exception {
                StorageRequest.signBlobQueueAndFileRequest(connection, client, -1L, context);
            }

            @Override
            public ResultSegment<ListBlobItem> preProcessResponse(CloudBlobContainer container, CloudBlobClient client,
                    OperationContext context) throws Exception {
                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_OK) {
                    this.setNonExceptionedRetryableFailure(true);
                }

                return null;
            }

            @Override
            public ResultSegment<ListBlobItem> postProcessResponse(HttpURLConnection connection,
                    CloudBlobContainer container, CloudBlobClient client, OperationContext context,
                    ResultSegment<ListBlobItem> storageObject) throws Exception {
                final ListBlobsResponse response = BlobListHandler.getBlobList(connection.getInputStream(), container);

                ResultContinuation newToken = null;

                if (response.getNextMarker() != null) {
                    newToken = new ResultContinuation();
                    newToken.setNextMarker(response.getNextMarker());
                    newToken.setContinuationType(ResultContinuationType.BLOB);
                    newToken.setTargetLocation(this.getResult().getTargetLocation());
                }

                final ResultSegment<ListBlobItem> resSegment = new ResultSegment<ListBlobItem>(response.getResults(),
                        response.getMaxResults(), newToken);

                // Important for listBlobs because this is required by the lazy iterator between executions.
                segmentedRequest.setToken(resSegment.getContinuationToken());

                return resSegment;
            }
        };

        return getRequest;
    }

    /**
     * Returns an enumerable collection of containers for the service client associated with this container.
     * 
     * @return An enumerable collection of {@link CloudBlobContainer} objects retrieved lazily that represent the
     *         containers for the service client associated with this container.
     */
    @DoesServiceRequest
    public Iterable<CloudBlobContainer> listContainers() {
        return this.blobServiceClient.listContainers();
    }

    /**
     * Returns an enumerable collection of containers whose names begin with the specified prefix for the service client
     * associated with this container.
     * 
     * @param prefix
     *            A <code>String</code> that represents the container name prefix.
     * 
     * @return An enumerable collection of {@link CloudBlobContainer} objects retrieved lazily that represent the
     *         containers whose names begin with the specified prefix for the service client associated with this
     *         container.
     */
    @DoesServiceRequest
    public Iterable<CloudBlobContainer> listContainers(final String prefix) {
        return this.blobServiceClient.listContainers(prefix);
    }

    /**
     * Returns an enumerable collection of containers whose names begin with the specified prefix for the service client
     * associated with this container, using the specified details setting, request options, and operation context.
     * 
     * @param prefix
     *            A <code>String</code> that represents the container name prefix.
     * @param detailsIncluded
     *            A {@link ContainerListingDetails} value that indicates whether container metadata will be returned.
     * @param options
     *            A {@link BlobRequestOptions} 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}).
     * @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 An enumerable collection of {@link CloudBlobContainer} objects retrieved lazily that represents the
     *         containers for the
     *         service client associated with this container.
     */
    @DoesServiceRequest
    public Iterable<CloudBlobContainer> listContainers(final String prefix,
            final ContainerListingDetails detailsIncluded, final BlobRequestOptions options,
            final OperationContext opContext) {
        return this.blobServiceClient.listContainers(prefix, detailsIncluded, options, opContext);
    }

    /**
     * Returns a result segment of an enumerable collection of containers for the service client associated with this
     * container.
     * 
     * @return A {@link ResultSegment} object that contains a segment of the enumerable collection of
     *         {@link CloudBlobContainer} objects that represent the containers for the service client associated with
     *         this container.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public ResultSegment<CloudBlobContainer> listContainersSegmented() throws StorageException {
        return this.blobServiceClient.listContainersSegmented();
    }

    /**
     * Returns a result segment of an enumerable collection of containers whose names begin with the specified prefix
     * for the service client associated with this container.
     * 
     * @param prefix
     *            A <code>String</code> that represents the blob name prefix.
     * 
     * @return A {@link ResultSegment} object that contains a segment of the enumerable collection of
     *         {@link CloudBlobContainer} objects that represent the containers whose names begin with the specified
     *         prefix for the service client associated with this container.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public ResultSegment<CloudBlobContainer> listContainersSegmented(final String prefix) throws StorageException {
        return this.blobServiceClient.listContainersSegmented(prefix);
    }

    /**
     * Returns a result segment containing a collection of containers whose names begin with
     * the specified prefix for the service client associated with this container,
     * using the specified listing details options, request options, and operation context.
     * 
     * @param prefix
     *            A <code>String</code> that represents the prefix of the container name.
     * @param detailsIncluded
     *            A {@link ContainerListingDetails} object that indicates whether metadata is included.
     * @param maxResults
     *            The maximum number of results to retrieve.  If <code>null</code> or greater
     *            than 5000, the server will return up to 5,000 items.  Must be at least 1.
     * @param continuationToken
     *            A {@link ResultContinuation} object that represents a continuation token
     *            returned by a previous listing operation.
     * @param options
     *            A {@link BlobRequestOptions} 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}).
     * @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} object that contains a segment of the enumerable collection
     *         of {@link CloudBlobContainer} objects that represent the containers whose names
     *         begin with the specified prefix for the service client associated with this container.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     * 
     */
    @DoesServiceRequest
    public ResultSegment<CloudBlobContainer> listContainersSegmented(final String prefix,
            final ContainerListingDetails detailsIncluded, final Integer maxResults,
            final ResultContinuation continuationToken, final BlobRequestOptions options,
            final OperationContext opContext) throws StorageException {
        return this.blobServiceClient.listContainersSegmented(prefix, detailsIncluded, maxResults, continuationToken,
                options, opContext);
    }

    /**
     * Verifies the passed in URI. Then parses it and uses its components to populate this resource's properties.
     * 
     * @param completeUri
     *            A {@link StorageUri} object which represents the complete URI.
     * @param credentials
     *            A {@link StorageCredentials} object used to authenticate access.
     * @throws StorageException
     *             If a storage service error occurred.
     */
    private void parseQueryAndVerify(final StorageUri completeUri, final StorageCredentials credentials)
            throws StorageException {
       Utility.assertNotNull("completeUri", completeUri);

        if (!completeUri.isAbsolute()) {
            throw new IllegalArgumentException(String.format(SR.RELATIVE_ADDRESS_NOT_PERMITTED, completeUri.toString()));
        }

        this.storageUri = PathUtility.stripURIQueryAndFragment(completeUri);
        
        final StorageCredentialsSharedAccessSignature parsedCredentials = 
                SharedAccessSignatureHelper.parseQuery(completeUri);

        if (credentials != null && parsedCredentials != null) {
            throw new IllegalArgumentException(SR.MULTIPLE_CREDENTIALS_PROVIDED);
        }

        try {
            final boolean usePathStyleUris = Utility.determinePathStyleFromUri(this.storageUri.getPrimaryUri());
            this.blobServiceClient = new CloudBlobClient(PathUtility.getServiceClientBaseAddress(
                    this.getStorageUri(), usePathStyleUris), credentials != null ? credentials : parsedCredentials);
            this.name = PathUtility.getContainerNameFromUri(this.storageUri.getPrimaryUri(), usePathStyleUris);
        }
        catch (final URISyntaxException e) {
            throw Utility.generateNewUnexpectedStorageException(e);
        }
    }

    void updatePropertiesFromResponse(HttpURLConnection request) {
        // ETag
        this.getProperties().setEtag(request.getHeaderField(Constants.HeaderConstants.ETAG));

        // Last Modified
        if (0 != request.getLastModified()) {
            final Calendar lastModifiedCalendar = Calendar.getInstance(Utility.LOCALE_US);
            lastModifiedCalendar.setTimeZone(Utility.UTC_ZONE);
            lastModifiedCalendar.setTime(new Date(request.getLastModified()));
            this.getProperties().setLastModified(lastModifiedCalendar.getTime());
        }
    }

    /**
     * Sets the metadata collection of name-value pairs to be set on the container with an {@link #uploadMetadata} call.
     * This collection will overwrite any existing container metadata. If this is set to an empty collection, the
     * container metadata will be cleared on an {@link #uploadMetadata} call.
     * 
     * @param metadata
     *            A <code>java.util.HashMap</code> object that represents the metadata being assigned to the container.
     */
    public void setMetadata(final HashMap<String, String> metadata) {
        this.metadata = metadata;
    }

    /**
     * Sets the list of URIs for all locations.
     * 
     * @param storageUri
     *            A {@link StorageUri} object which represents the list of URIs for all locations.
     */
    protected void setStorageUri(final StorageUri storageUri) {
        this.storageUri = storageUri;
    }

    /**
     * Sets the properties for the container.
     * 
     * @param properties
     *            A {@link BlobContainerProperties} object that represents the properties being assigned to the
     *            container.
     */
    protected void setProperties(final BlobContainerProperties properties) {
        this.properties = properties;
    }

    /**
     * Uploads the container's metadata.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public void uploadMetadata() throws StorageException {
        this.uploadMetadata(null /* accessCondition */, null /* options */, null /* opContext */);
    }

    /**
     * Uploads the container's metadata using the specified request options and operation context.
     * 
     * @param accessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the container.
     * @param options
     *            A {@link BlobRequestOptions} 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}).
     * @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.
     */
    @DoesServiceRequest
    public void uploadMetadata(AccessCondition accessCondition, BlobRequestOptions options, OperationContext opContext)
            throws StorageException {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        opContext.initialize();
        options = BlobRequestOptions.populateAndApplyDefaults(options, BlobType.UNSPECIFIED, this.blobServiceClient);

        ExecutionEngine.executeWithRetry(this.blobServiceClient, this,
                this.uploadMetadataImpl(accessCondition, options), options.getRetryPolicyFactory(), opContext);

    }

    @DoesServiceRequest
    private StorageRequest<CloudBlobClient, CloudBlobContainer, Void> uploadMetadataImpl(
            final AccessCondition accessCondition, final BlobRequestOptions options) {
        final StorageRequest<CloudBlobClient, CloudBlobContainer, Void> putRequest = new StorageRequest<CloudBlobClient, CloudBlobContainer, Void>(
                options, this.getStorageUri()) {

            @Override
            public HttpURLConnection buildRequest(CloudBlobClient client, CloudBlobContainer container,
                    OperationContext context) throws Exception {
                return BlobRequest.setContainerMetadata(
                        container.getTransformedAddress().getUri(this.getCurrentLocation()),
                        options, context, accessCondition);
            }

            @Override
            public void setHeaders(HttpURLConnection connection, CloudBlobContainer container, OperationContext context) {
                BlobRequest.addMetadata(connection, container.metadata, context);
            }

            @Override
            public void signRequest(HttpURLConnection connection, CloudBlobClient client, OperationContext context)
                    throws Exception {
                StorageRequest.signBlobQueueAndFileRequest(connection, client, 0L, context);
            }

            @Override
            public Void preProcessResponse(CloudBlobContainer container, CloudBlobClient client,
                    OperationContext context) throws Exception {
                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_OK) {
                    this.setNonExceptionedRetryableFailure(true);
                }

                container.updatePropertiesFromResponse(this.getConnection());
                return null;
            }
        };

        return putRequest;
    }

    /**
     * Uploads the container's permissions.
     * 
     * @param permissions
     *            A {@link BlobContainerPermissions} object that represents the permissions to upload.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public void uploadPermissions(final BlobContainerPermissions permissions) throws StorageException {
        this.uploadPermissions(permissions, null /* accessCondition */, null /* options */, null /* opContext */);
    }

    /**
     * Uploads the container's permissions using the specified request options and operation context.
     * 
     * @param permissions
     *            A {@link BlobContainerPermissions} object that represents the permissions to upload.
     * @param accessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the container.
     * @param options
     *            A {@link BlobRequestOptions} 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}).
     * @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.
     */
    @DoesServiceRequest
    public void uploadPermissions(final BlobContainerPermissions permissions, final AccessCondition accessCondition,
            BlobRequestOptions options, OperationContext opContext) throws StorageException {
        if (permissions.getPublicAccess() == BlobContainerPublicAccessType.UNKNOWN) {
            throw new IllegalArgumentException(String.format(Utility.LOCALE_US, SR.ARGUMENT_OUT_OF_RANGE_ERROR, "accessType", permissions.getPublicAccess()));
        }

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

        opContext.initialize();
        options = BlobRequestOptions.populateAndApplyDefaults(options, BlobType.UNSPECIFIED, this.blobServiceClient);

        ExecutionEngine.executeWithRetry(this.blobServiceClient, this,
                uploadPermissionsImpl(permissions, accessCondition, options), options.getRetryPolicyFactory(),
                opContext);
    }

    private StorageRequest<CloudBlobClient, CloudBlobContainer, Void> uploadPermissionsImpl(
            final BlobContainerPermissions permissions, final AccessCondition accessCondition,
            final BlobRequestOptions options) throws StorageException {
        try {
            final StringWriter outBuffer = new StringWriter();
            SharedAccessPolicySerializer.writeSharedAccessIdentifiersToStream(permissions.getSharedAccessPolicies(),
                    outBuffer);
            final byte[] aclBytes = outBuffer.toString().getBytes(Constants.UTF8_CHARSET);
            final StorageRequest<CloudBlobClient, CloudBlobContainer, Void> putRequest = new StorageRequest<CloudBlobClient, CloudBlobContainer, Void>(
                    options, this.getStorageUri()) {
                @Override
                public HttpURLConnection buildRequest(CloudBlobClient client, CloudBlobContainer container,
                        OperationContext context) throws Exception {
                    this.setSendStream(new ByteArrayInputStream(aclBytes));
                    this.setLength((long) aclBytes.length);
                    return BlobRequest.setAcl(container.getTransformedAddress().getUri(this.getCurrentLocation()),
                            options, context, accessCondition, permissions.getPublicAccess());
                }

                @Override
                public void signRequest(HttpURLConnection connection, CloudBlobClient client, OperationContext context)
                        throws Exception {
                    StorageRequest.signBlobQueueAndFileRequest(connection, client, aclBytes.length, context);
                }

                @Override
                public Void preProcessResponse(CloudBlobContainer container, CloudBlobClient client,
                        OperationContext context) throws Exception {
                    if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_OK) {
                        this.setNonExceptionedRetryableFailure(true);
                        return null;
                    }

                    container.updatePropertiesFromResponse(this.getConnection());
                    container.getProperties().setPublicAccess(permissions.getPublicAccess());
                    return null;
                }
            };

            return putRequest;
        }
        catch (IllegalArgumentException e) {
            // The request was not even made. There was an error while trying to write the permissions. Just throw.
            StorageException translatedException = StorageException.translateClientException(e);
            throw translatedException;
        }
        catch (IllegalStateException e) {
            // The request was not even made. There was an error while trying to write the permissions. Just throw.
            StorageException translatedException = StorageException.translateClientException(e);
            throw translatedException;
        }
        catch (IOException e) {
            // The request was not even made. There was an error while trying to write the permissions. Just throw.
            StorageException translatedException = StorageException.translateClientException(e);
            throw translatedException;
        }
    }

    /**
     * Acquires a new infinite lease on the container.
     *
     * @return A <code>String</code> that represents the lease ID.
     *
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public final String acquireLease() throws StorageException {
        return this.acquireLease(null /* leaseTimeInSeconds */, null /* proposedLeaseId */);
    }

    /**
     * Acquires a new lease on the container with the specified lease time and proposed lease ID.
     * 
     * @param leaseTimeInSeconds
     *            An <code>Integer</code> which specifies the span of time for which to acquire the lease, in seconds.
     *            If null, an infinite lease will be acquired. If not null, the value must be greater than
     *            zero.
     * 
     * @param proposedLeaseId
     *            A <code>String</code> that represents the proposed lease ID for the new lease,
     *            or null if no lease ID is proposed.
     * 
     * @return A <code>String</code> that represents the lease ID.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public final String acquireLease(final Integer leaseTimeInSeconds, final String proposedLeaseId)
            throws StorageException {
        return this.acquireLease(leaseTimeInSeconds, proposedLeaseId, null /* accessCondition */, null /* options */,
                null /* opContext */);
    }

    /**
     * Acquires a new lease on the container with the specified lease time, proposed lease ID, request
     * options, and operation context.
     * 
     * @param leaseTimeInSeconds
     *            An <code>Integer</code> which specifies the span of time for which to acquire the lease, in seconds.
     *            If null, an infinite lease will be acquired. If not null, the value must be greater than
     *            zero.
     * 
     * @param proposedLeaseId
     *            A <code>String</code> that represents the proposed lease ID for the new lease,
     *            or null if no lease ID is proposed.
     * 
     * @param accessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the container.
     * 
     * @param options
     *            A {@link BlobRequestOptions} 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}).
     * 
     * @param opContext
     *            An {@link OperationContext} object that represents the context for the current operation. The context
     *            is used to track requests to the storage service, and to provide additional runtime information about
     *            the operation.
     * 
     * @return A <code>String</code> that represents the lease ID.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public final String acquireLease(final Integer leaseTimeInSeconds, final String proposedLeaseId,
            final AccessCondition accessCondition, BlobRequestOptions options, OperationContext opContext)
            throws StorageException {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        opContext.initialize();
        options = BlobRequestOptions.populateAndApplyDefaults(options, BlobType.UNSPECIFIED, this.blobServiceClient);

        return ExecutionEngine.executeWithRetry(this.blobServiceClient, this,
                this.acquireLeaseImpl(leaseTimeInSeconds, proposedLeaseId, accessCondition, options),
                options.getRetryPolicyFactory(), opContext);
    }

    private StorageRequest<CloudBlobClient, CloudBlobContainer, String> acquireLeaseImpl(
            final Integer leaseTimeInSeconds, final String proposedLeaseId, final AccessCondition accessCondition,
            final BlobRequestOptions options) {
        final StorageRequest<CloudBlobClient, CloudBlobContainer, String> putRequest = new StorageRequest<CloudBlobClient, CloudBlobContainer, String>(
                options, this.getStorageUri()) {

            @Override
            public HttpURLConnection buildRequest(CloudBlobClient client, CloudBlobContainer container,
                    OperationContext context) throws Exception {
                return BlobRequest.leaseContainer(container.getTransformedAddress().getUri(this.getCurrentLocation()),
                        options, context, accessCondition, LeaseAction.ACQUIRE, leaseTimeInSeconds, proposedLeaseId,
                        null /* breakPeriodInSeconds */);
            }

            @Override
            public void signRequest(HttpURLConnection connection, CloudBlobClient client, OperationContext context)
                    throws Exception {
                StorageRequest.signBlobQueueAndFileRequest(connection, client, 0L, context);
            }

            @Override
            public String preProcessResponse(CloudBlobContainer container, CloudBlobClient client,
                    OperationContext context) throws Exception {
                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_CREATED) {
                    this.setNonExceptionedRetryableFailure(true);
                    return null;
                }

                container.updatePropertiesFromResponse(this.getConnection());

                return BlobResponse.getLeaseID(this.getConnection());
            }

        };

        return putRequest;
    }

    /**
     * Renews an existing lease with the specified access conditions.
     * 
     * @param accessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the container. The lease
     *            ID is required to be set with an access condition.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public final void renewLease(final AccessCondition accessCondition) throws StorageException {
        this.renewLease(accessCondition, null /* options */, null /* opContext */);
    }

    /**
     * Renews an existing lease with the specified access conditions, request options, and operation context.
     * 
     * @param accessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the blob. The lease ID is
     *            required to be set with an access condition.
     * 
     * @param options
     *            A {@link BlobRequestOptions} 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}).
     * 
     * @param opContext
     *            An {@link OperationContext} object that represents the context for the current operation. The context
     *            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.
     */
    @DoesServiceRequest
    public final void renewLease(final AccessCondition accessCondition, BlobRequestOptions options,
            OperationContext opContext) throws StorageException {
        Utility.assertNotNull("accessCondition", accessCondition);
        Utility.assertNotNullOrEmpty("leaseID", accessCondition.getLeaseID());

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

        opContext.initialize();
        options = BlobRequestOptions.populateAndApplyDefaults(options, BlobType.UNSPECIFIED, this.blobServiceClient);

        ExecutionEngine.executeWithRetry(this.blobServiceClient, this, this.renewLeaseImpl(accessCondition, options),
                options.getRetryPolicyFactory(), opContext);
    }

    private StorageRequest<CloudBlobClient, CloudBlobContainer, Void> renewLeaseImpl(
            final AccessCondition accessCondition, final BlobRequestOptions options) {
        final StorageRequest<CloudBlobClient, CloudBlobContainer, Void> putRequest = new StorageRequest<CloudBlobClient, CloudBlobContainer, Void>(
                options, this.getStorageUri()) {

            @Override
            public HttpURLConnection buildRequest(CloudBlobClient client, CloudBlobContainer container,
                    OperationContext context) throws Exception {
                return BlobRequest.leaseContainer(container.getTransformedAddress().getUri(this.getCurrentLocation()),
                        options, context, accessCondition, LeaseAction.RENEW, null /* leaseTimeInSeconds */,
                        null /* proposedLeaseId */, null /* breakPeriodInseconds */);
            }

            @Override
            public void signRequest(HttpURLConnection connection, CloudBlobClient client, OperationContext context)
                    throws Exception {
                StorageRequest.signBlobQueueAndFileRequest(connection, client, 0L, context);
            }

            @Override
            public Void preProcessResponse(CloudBlobContainer container, CloudBlobClient client,
                    OperationContext context) throws Exception {
                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_OK) {
                    this.setNonExceptionedRetryableFailure(true);
                    return null;
                }

                container.updatePropertiesFromResponse(this.getConnection());
                return null;
            }
        };

        return putRequest;
    }

    /**
     * Releases the lease on the container.
     * 
     * @param accessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the blob. The lease ID is
     *            required to be set with an access condition.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public final void releaseLease(final AccessCondition accessCondition) throws StorageException {
        this.releaseLease(accessCondition, null /* options */, null /* opContext */);
    }

    /**
     * Releases the lease on the container using the specified access conditions, request options, and operation
     * context.
     * 
     * @param accessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the blob. The lease ID is
     *            required to be set with an access condition.
     * 
     * @param options
     *            A {@link BlobRequestOptions} 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}).
     * 
     * @param opContext
     *            An {@link OperationContext} object that represents the context for the current operation. The context
     *            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.
     */
    @DoesServiceRequest
    public final void releaseLease(final AccessCondition accessCondition, BlobRequestOptions options,
            OperationContext opContext) throws StorageException {
        Utility.assertNotNull("accessCondition", accessCondition);
        Utility.assertNotNullOrEmpty("leaseID", accessCondition.getLeaseID());

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

        opContext.initialize();
        options = BlobRequestOptions.populateAndApplyDefaults(options, BlobType.UNSPECIFIED, this.blobServiceClient);

        ExecutionEngine.executeWithRetry(this.blobServiceClient, this, this.releaseLeaseImpl(accessCondition, options),
                options.getRetryPolicyFactory(), opContext);
    }

    private StorageRequest<CloudBlobClient, CloudBlobContainer, Void> releaseLeaseImpl(
            final AccessCondition accessCondition, final BlobRequestOptions options) {
        final StorageRequest<CloudBlobClient, CloudBlobContainer, Void> putRequest = new StorageRequest<CloudBlobClient, CloudBlobContainer, Void>(
                options, this.getStorageUri()) {

            @Override
            public HttpURLConnection buildRequest(CloudBlobClient client, CloudBlobContainer container,
                    OperationContext context) throws Exception {
                return BlobRequest.leaseContainer(container.getTransformedAddress().getUri(this.getCurrentLocation()),
                        options, context, accessCondition, LeaseAction.RELEASE, null, null, null);
            }

            @Override
            public void signRequest(HttpURLConnection connection, CloudBlobClient client, OperationContext context)
                    throws Exception {
                StorageRequest.signBlobQueueAndFileRequest(connection, client, 0L, context);
            }

            @Override
            public Void preProcessResponse(CloudBlobContainer container, CloudBlobClient client,
                    OperationContext context) throws Exception {
                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_OK) {
                    this.setNonExceptionedRetryableFailure(true);
                    return null;
                }

                container.updatePropertiesFromResponse(this.getConnection());
                return null;
            }
        };

        return putRequest;
    }

    /**
     * Breaks the lease and ensures that another client cannot acquire a new lease until the current lease
     * period has expired.
     * 
     * @param breakPeriodInSeconds
     *            An <code>Integer</code> which specifies the time to wait, in seconds, until the current lease is
     *            broken.
     *            If null, the break period is the remainder of the current lease, or zero for infinite leases.
     * 
     * @return The time, in seconds, remaining in the lease period.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public final long breakLease(final Integer breakPeriodInSeconds) throws StorageException {
        return this.breakLease(breakPeriodInSeconds, null /* accessCondition */, null, null);
    }

    /**
     * Breaks the existing lease, using the specified request options and operation context, and ensures that
     * another client cannot acquire a new lease until the current lease period has expired.
     * 
     * @param breakPeriodInSeconds
     *            An <code>Integer</code> which specifies the time to wait, in seconds, until the current lease is
     *            broken.
     *            If null, the break period is the remainder of the current lease, or zero for infinite leases.
     * 
     * @param accessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the blob.
     * @param options
     *            A {@link BlobRequestOptions} 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}).
     * @param opContext
     *            An {@link OperationContext} object that represents the context for the current operation. The context
     *            is used to track requests to the storage service, and to provide additional runtime information about
     *            the operation.
     * 
     * @return The time, in seconds, remaining in the lease period.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public final long breakLease(final Integer breakPeriodInSeconds, final AccessCondition accessCondition,
            BlobRequestOptions options, OperationContext opContext) throws StorageException {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        if (breakPeriodInSeconds != null) {
            Utility.assertGreaterThanOrEqual("breakPeriodInSeconds", breakPeriodInSeconds, 0);
        }

        opContext.initialize();
        options = BlobRequestOptions.populateAndApplyDefaults(options, BlobType.UNSPECIFIED, this.blobServiceClient);

        return ExecutionEngine.executeWithRetry(this.blobServiceClient, this,
                this.breakLeaseImpl(breakPeriodInSeconds, accessCondition, options), options.getRetryPolicyFactory(),
                opContext);
    }

    private final StorageRequest<CloudBlobClient, CloudBlobContainer, Long> breakLeaseImpl(
            final Integer breakPeriodInSeconds, final AccessCondition accessCondition, final BlobRequestOptions options) {
        final StorageRequest<CloudBlobClient, CloudBlobContainer, Long> putCmd = new StorageRequest<CloudBlobClient, CloudBlobContainer, Long>(
                options, this.getStorageUri()) {

            @Override
            public HttpURLConnection buildRequest(CloudBlobClient client, CloudBlobContainer container,
                    OperationContext context) throws Exception {
                return BlobRequest.leaseContainer(container.getTransformedAddress().getUri(this.getCurrentLocation()),
                        options, context, accessCondition, LeaseAction.BREAK, null, null, breakPeriodInSeconds);
            }

            @Override
            public void signRequest(HttpURLConnection connection, CloudBlobClient client, OperationContext context)
                    throws Exception {
                StorageRequest.signBlobQueueAndFileRequest(connection, client, 0L, context);
            }

            @Override
            public Long preProcessResponse(CloudBlobContainer container, CloudBlobClient client,
                    OperationContext context) throws Exception {
                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_ACCEPTED) {
                    this.setNonExceptionedRetryableFailure(true);
                    return -1L;
                }

                container.updatePropertiesFromResponse(this.getConnection());
                final String leaseTime = BlobResponse.getLeaseTime(this.getConnection());
                return Utility.isNullOrEmpty(leaseTime) ? -1L : Long.parseLong(leaseTime);
            }
        };

        return putCmd;
    }

    /**
     * Changes the existing lease ID to the proposed lease ID.
     * 
     * @param proposedLeaseId
     *            A <code>String</code> that represents the proposed lease ID for the new lease,
     *            or null if no lease ID is proposed.
     * 
     * @param accessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the blob. The lease ID is
     *            required to be set with an access condition.
     * @return A <code>String</code> that represents the new lease ID.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public final String changeLease(final String proposedLeaseId, final AccessCondition accessCondition)
            throws StorageException {
        return this.changeLease(proposedLeaseId, accessCondition, null, null);
    }

    /**
     * Changes the existing lease ID to the proposed lease Id with the specified access conditions, request options,
     * and operation context.
     * 
     * @param proposedLeaseId
     *            A <code>String</code> that represents the proposed lease ID for the new lease. This cannot be null.
     * 
     * @param accessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the blob. The lease ID is
     *            required to be set with an access condition.
     * 
     * @param options
     *            A {@link BlobRequestOptions} 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}).
     * 
     * @param opContext
     *            An {@link OperationContext} object that represents the context for the current operation. The context
     *            is used to track requests to the storage service, and to provide additional runtime information about
     *            the operation.
     * @return A <code>String</code> that represents the new lease ID.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public final String changeLease(final String proposedLeaseId, final AccessCondition accessCondition,
            BlobRequestOptions options, OperationContext opContext) throws StorageException {
        Utility.assertNotNull("proposedLeaseId", proposedLeaseId);
        Utility.assertNotNull("accessCondition", accessCondition);
        Utility.assertNotNullOrEmpty("leaseID", accessCondition.getLeaseID());

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

        opContext.initialize();
        options = BlobRequestOptions.populateAndApplyDefaults(options, BlobType.UNSPECIFIED, this.blobServiceClient);

        return ExecutionEngine.executeWithRetry(this.blobServiceClient, this,
                this.changeLeaseImpl(proposedLeaseId, accessCondition, options), options.getRetryPolicyFactory(),
                opContext);
    }

    private final StorageRequest<CloudBlobClient, CloudBlobContainer, String> changeLeaseImpl(
            final String proposedLeaseId, final AccessCondition accessCondition, final BlobRequestOptions options) {
        final StorageRequest<CloudBlobClient, CloudBlobContainer, String> putRequest = new StorageRequest<CloudBlobClient, CloudBlobContainer, String>(
                options, this.getStorageUri()) {

            @Override
            public HttpURLConnection buildRequest(CloudBlobClient client, CloudBlobContainer container,
                    OperationContext context) throws Exception {
                return BlobRequest.leaseContainer(container.getTransformedAddress().getUri(this.getCurrentLocation()),
                        options, context, accessCondition, LeaseAction.CHANGE, null, proposedLeaseId, null);
            }

            @Override
            public void signRequest(HttpURLConnection connection, CloudBlobClient client, OperationContext context)
                    throws Exception {
                StorageRequest.signBlobQueueAndFileRequest(connection, client, 0L, context);
            }

            @Override
            public String preProcessResponse(CloudBlobContainer container, CloudBlobClient client,
                    OperationContext context) throws Exception {
                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_OK) {
                    this.setNonExceptionedRetryableFailure(true);
                    return null;
                }

                container.updatePropertiesFromResponse(this.getConnection());
                return BlobResponse.getLeaseID(this.getConnection());
            }
        };

        return putRequest;
    }
} public class SR {
    public static final String ACCOUNT_NAME_NULL_OR_EMPTY = "The account name is null or empty.";
    public static final String ACCOUNT_NAME_MISMATCH = "The account name does not match the existing account name on the credentials.";
    public static final String APPEND_BLOB_MD5_NOT_POSSIBLE = "MD5 cannot be calculated for an existing append blob because it would require reading the existing data. Please disable StoreFileContentMD5.";
    public static final String ARGUMENT_NULL_OR_EMPTY = "The argument must not be null or an empty string. Argument name: %s.";
    public static final String ARGUMENT_OUT_OF_RANGE_ERROR = "The argument is out of range. Argument name: %s, Value passed: %s.";
    public static final String ATTEMPTED_TO_SERIALIZE_INACCESSIBLE_PROPERTY = "An attempt was made to access an inaccessible member of the entity during serialization.";
    public static final String BLOB = "blob";
    public static final String BLOB_DATA_CORRUPTED = "Blob data corrupted (integrity check failed), Expected value is %s, retrieved %s";
    public static final String BLOB_ENDPOINT_NOT_CONFIGURED = "No blob endpoint configured.";
    public static final String BLOB_HASH_MISMATCH = "Blob hash mismatch (integrity check failed), Expected value is %s, retrieved %s.";
    public static final String BLOB_MD5_NOT_SUPPORTED_FOR_PAGE_BLOBS = "Blob level MD5 is not supported for page blobs.";
    public static final String BLOB_TYPE_NOT_DEFINED = "The blob type is not defined.  Allowed types are BlobType.BLOCK_BLOB and BlobType.Page_BLOB.";
    public static final String CANNOT_CREATE_SAS_FOR_GIVEN_CREDENTIALS = "Cannot create Shared Access Signature as the credentials does not have account name information. Please check that the credentials provided support creating Shared Access Signature.";
    public static final String CANNOT_CREATE_SAS_FOR_SNAPSHOTS = "Cannot create Shared Access Signature via references to blob snapshots. Please perform the given operation on the root blob instead.";
    public static final String CANNOT_CREATE_SAS_WITHOUT_ACCOUNT_KEY = "Cannot create Shared Access Signature unless the Account Key credentials are used by the ServiceClient.";
    public static final String CANNOT_TRANSFORM_NON_HTTPS_URI_WITH_HTTPS_ONLY_CREDENTIALS = "Cannot use HTTP with credentials that only support HTTPS.";
    public static final String CONTAINER = "container";
    public static final String CONTENT_LENGTH_MISMATCH = "An incorrect number of bytes was read from the connection. The connection may have been closed.";
    public static final String CREATING_NETWORK_STREAM = "Creating a NetworkInputStream and expecting to read %s bytes.";
    public static final String CREDENTIALS_CANNOT_SIGN_REQUEST = "CloudBlobClient, CloudQueueClient and CloudTableClient require credentials that can sign a request.";
    public static final String CUSTOM_RESOLVER_THREW = "The custom property resolver delegate threw an exception. Check the inner exception for more details.";
    public static final String DEFAULT_SERVICE_VERSION_ONLY_SET_FOR_BLOB_SERVICE = "DefaultServiceVersion can only be set for the Blob service.";
    public static final String DELETE_SNAPSHOT_NOT_VALID_ERROR = "The option '%s' must be 'None' to delete a specific snapshot specified by '%s'.";
    public static final String DIRECTORY = "directory";
    public static final String EDMTYPE_WAS_NULL = "EdmType cannot be null.";
    public static final String ENUMERATION_ERROR = "An error occurred while enumerating the result, check the original exception for details.";
    public static final String EMPTY_BATCH_NOT_ALLOWED = "Cannot execute an empty batch operation.";
    public static final String ENDPOINT_INFORMATION_UNAVAILABLE = "Endpoint information not available for Account using Shared Access Credentials.";
    public static final String ENTITY_PROPERTY_CANNOT_BE_NULL_FOR_PRIMITIVES = "EntityProperty cannot be set to null for primitive value types.";
    public static final String ETAG_INVALID_FOR_DELETE = "Delete requires a valid ETag (which may be the '*' wildcard).";
    public static final String ETAG_INVALID_FOR_MERGE = "Merge requires a valid ETag (which may be the '*' wildcard).";
    public static final String ETAG_INVALID_FOR_UPDATE = "Replace requires a valid ETag (which may be the '*' wildcard).";
    public static final String ENUM_COULD_NOT_BE_PARSED = "%s could not be parsed from '%s'.";
    public static final String EXCEPTION_THROWN_DURING_DESERIALIZATION = "The entity threw an exception during deserialization.";
    public static final String EXCEPTION_THROWN_DURING_SERIALIZATION = "The entity threw an exception during serialization.";
    public static final String EXPECTED_A_FIELD_NAME = "Expected a field name.";
    public static final String EXPECTED_END_ARRAY = "Expected the end of a JSON Array.";
    public static final String EXPECTED_END_OBJECT = "Expected the end of a JSON Object.";
    public static final String EXPECTED_START_ARRAY = "Expected the start of a JSON Array.";
    public static final String EXPECTED_START_ELEMENT_TO_EQUAL_ERROR = "Expected START_ELEMENT to equal error.";
    public static final String EXPECTED_START_OBJECT = "Expected the start of a JSON Object.";
    public static final String FAILED_TO_PARSE_PROPERTY = "Failed to parse property '%s' with value '%s' as type '%s'";
    public static final String FILE = "file";
    public static final String FILE_ENDPOINT_NOT_CONFIGURED = "No file endpoint configured.";
    public static final String FILE_HASH_MISMATCH = "File hash mismatch (integrity check failed), Expected value is %s, retrieved %s.";
    public static final String FILE_MD5_NOT_POSSIBLE = "MD5 cannot be calculated for an existing file because it would require reading the existing data. Please disable StoreFileContentMD5.";
    public static final String INCORRECT_STREAM_LENGTH = "An incorrect stream length was specified, resulting in an authentication failure. Please specify correct length, or -1.";
    public static final String INPUT_STREAM_SHOULD_BE_MARKABLE = "Input stream must be markable.";
    public static final String INVALID_ACCOUNT_NAME = "Invalid account name.";
    public static final String INVALID_ACL_ACCESS_TYPE = "Invalid acl public access type returned '%s'. Expected blob or container.";
    public static final String INVALID_BLOB_TYPE = "Incorrect Blob type, please use the correct Blob type to access a blob on the server. Expected %s, actual %s.";
    public static final String INVALID_BLOCK_ID = "Invalid blockID, blockID must be a valid Base64 String.";
    public static final String INVALID_BLOCK_SIZE = "Append block data should not exceed the maximum blob size condition value.";
    public static final String INVALID_CONDITIONAL_HEADERS = "The conditionals specified for this operation did not match server.";
    public static final String INVALID_CONNECTION_STRING = "Invalid connection string.";
    public static final String INVALID_CONNECTION_STRING_DEV_STORE_NOT_TRUE = "Invalid connection string, the UseDevelopmentStorage key must always be paired with 'true'.  Remove the flag entirely otherwise.";
    public static final String INVALID_CONTENT_LENGTH = "ContentLength must be set to -1 or positive Long value.";
    public static final String INVALID_CONTENT_TYPE = "An incorrect Content-Type was returned from the server.";
    public static final String INVALID_CORS_RULE = "A CORS rule must contain at least one allowed origin and allowed method, and MaxAgeInSeconds cannot have a value less than zero.";
    public static final String INVALID_DATE_STRING = "Invalid Date String: %s.";
    public static final String INVALID_EDMTYPE_VALUE = "Invalid value '%s' for EdmType.";
    public static final String INVALID_FILE_LENGTH = "File length must be greater than or equal to 0 bytes.";
    public static final String INVALID_GEO_REPLICATION_STATUS = "Null or Invalid geo-replication status in response: %s.";
    public static final String INVALID_IP_ADDRESS = "Error when parsing IPv4 address: IP address '%s' is invalid.";
    public static final String INVALID_KEY = "Storage Key is not a valid base64 encoded string.";
    public static final String INVALID_LISTING_DETAILS = "Invalid blob listing details specified.";
    public static final String INVALID_LOGGING_LEVEL = "Invalid logging operations specified.";
    public static final String INVALID_MAX_WRITE_SIZE = "Max write size is 4MB. Please specify a smaller range.";
    public static final String INVALID_MESSAGE_LENGTH = "The message size cannot be larger than %s bytes.";
    public static final String INVALID_MIME_RESPONSE = "Invalid MIME response received.";
    public static final String INVALID_NUMBER_OF_BYTES_IN_THE_BUFFER = "Page data must be a multiple of 512 bytes. Buffer currently contains %d bytes.";
    public static final String INVALID_OPERATION_FOR_A_SNAPSHOT = "Cannot perform this operation on a blob representing a snapshot.";
    public static final String INVALID_OPERATION_FOR_A_SHARE_SNAPSHOT = "Cannot perform this operation on a share representing a snapshot.";
    public static final String INVALID_PAGE_BLOB_LENGTH = "Page blob length must be multiple of 512.";
    public static final String INVALID_PAGE_START_OFFSET = "Page start offset must be multiple of 512.";
    public static final String INVALID_RANGE_CONTENT_MD5_HEADER = "Cannot specify x-ms-range-get-content-md5 header on ranges larger than 4 MB. Either use a BlobReadStream via openRead, or disable TransactionalMD5 via the BlobRequestOptions.";
    public static final String INVALID_RESOURCE_NAME = "Invalid %s name. Check MSDN for more information about valid naming.";
    public static final String INVALID_RESOURCE_NAME_LENGTH = "Invalid %s name length. The name must be between %s and %s characters long.";
    public static final String INVALID_RESOURCE_RESERVED_NAME = "Invalid %s name. This name is reserved.";
    public static final String INVALID_RESPONSE_RECEIVED = "The response received is invalid or improperly formatted.";
    public static final String INVALID_STORAGE_PROTOCOL_VERSION = "Storage protocol version prior to 2009-09-19 do not support shared key authentication.";
    public static final String INVALID_STORAGE_SERVICE = "Invalid storage service specified.";
    public static final String INVALID_STREAM_LENGTH = "Invalid stream length; stream must be between 0 and %s MB in length.";
    public static final String ITERATOR_EMPTY = "There are no more elements in this enumeration.";
    public static final String LEASE_CONDITION_ON_SOURCE = "A lease condition cannot be specified on the source of a copy.";
    public static final String LOG_STREAM_END_ERROR = "Error parsing log record: unexpected end of stream.";
    public static final String LOG_STREAM_DELIMITER_ERROR = "Error parsing log record: unexpected delimiter encountered.";
    public static final String LOG_STREAM_QUOTE_ERROR = "Error parsing log record: unexpected quote character encountered.";
    public static final String LOG_VERSION_UNSUPPORTED = "A storage log version of %s is unsupported.";
    public static final String MARK_EXPIRED = "Stream mark expired.";
    public static final String MAXIMUM_EXECUTION_TIMEOUT_EXCEPTION = "The client could not finish the operation within specified maximum execution timeout.";
    public static final String METADATA_KEY_INVALID = "The key for one of the metadata key-value pairs is null, empty, or whitespace.";
    public static final String METADATA_VALUE_INVALID = "The value for one of the metadata key-value pairs is null, empty, or whitespace.";
    public static final String MISSING_CREDENTIALS = "No credentials provided.";
    public static final String MISSING_MANDATORY_DATE_HEADER = "Canonicalization did not find a non-empty x-ms-date header in the request. Please use a request with a valid x-ms-date header in RFC 123 format.";
    public static final String MISSING_MANDATORY_PARAMETER_FOR_SAS = "Missing mandatory parameters for valid Shared Access Signature.";
    public static final String MISSING_MD5 = "ContentMD5 header is missing in the response.";
    public static final String MISSING_NULLARY_CONSTRUCTOR = "Class type must contain contain a nullary constructor.";
    public static final String MULTIPLE_CREDENTIALS_PROVIDED = "Cannot provide credentials as part of the address and as constructor parameter. Either pass in the address or use a different constructor.";
    public static final String NETWORK_ON_MAIN_THREAD_EXCEPTION = "Network operations may not be performed on the main thread.";
    public static final String OPS_IN_BATCH_MUST_HAVE_SAME_PARTITION_KEY = "All entities in a given batch must have the same partition key.";
    public static final String PARAMETER_NOT_IN_RANGE = "The value of the parameter '%s' should be between %s and %s.";
    public static final String PARAMETER_SHOULD_BE_GREATER = "The value of the parameter '%s' should be greater than %s.";
    public static final String PARAMETER_SHOULD_BE_GREATER_OR_EQUAL = "The value of the parameter '%s' should be greater than or equal to %s.";
    public static final String PARTITIONKEY_MISSING_FOR_DELETE = "Delete requires a partition key.";
    public static final String PARTITIONKEY_MISSING_FOR_MERGE = "Merge requires a partition key.";
    public static final String PARTITIONKEY_MISSING_FOR_UPDATE = "Replace requires a partition key.";
    public static final String PARTITIONKEY_MISSING_FOR_INSERT = "Insert requires a partition key.";
    public static final String PATH_STYLE_URI_MISSING_ACCOUNT_INFORMATION = "Missing account name information inside path style URI. Path style URIs should be of the form http://<IPAddress:Port>/<accountName>";
    public static final String PRIMARY_ONLY_COMMAND = "This operation can only be executed against the primary storage location.";
    public static final String PROPERTY_CANNOT_BE_SERIALIZED_AS_GIVEN_EDMTYPE = "Property %s with Edm Type %s cannot be de-serialized.";
    public static final String PRECONDITION_FAILURE_IGNORED = "Pre-condition failure on a retry is being ignored since the request should have succeeded in the first attempt.";
    public static final String QUERY_PARAMETER_NULL_OR_EMPTY = "Cannot encode a query parameter with a null or empty key.";
    public static final String QUERY_REQUIRES_VALID_CLASSTYPE_OR_RESOLVER = "Query requires a valid class type or resolver.";
    public static final String QUEUE = "queue";
    public static final String QUEUE_ENDPOINT_NOT_CONFIGURED = "No queue endpoint configured.";
    public static final String RELATIVE_ADDRESS_NOT_PERMITTED = "Address %s is a relative address. Only absolute addresses are permitted.";
    public static final String RESOURCE_NAME_EMPTY = "Invalid %s name. The name may not be null, empty, or whitespace only.";
    public static final String RESPONSE_RECEIVED_IS_INVALID = "The response received is invalid or improperly formatted.";
    public static final String RETRIEVE_MUST_BE_ONLY_OPERATION_IN_BATCH = "A batch transaction with a retrieve operation cannot contain any other operations.";
    public static final String ROWKEY_MISSING_FOR_DELETE = "Delete requires a row key.";
    public static final String ROWKEY_MISSING_FOR_MERGE = "Merge requires a row key.";
    public static final String ROWKEY_MISSING_FOR_UPDATE = "Replace requires a row key.";
    public static final String ROWKEY_MISSING_FOR_INSERT = "Insert requires a row key.";
    public static final String SCHEME_NULL_OR_EMPTY = "The protocol to use is null. Please specify whether to use http or https.";
    public static final String SECONDARY_ONLY_COMMAND = "This operation can only be executed against the secondary storage location.";
    public static final String SHARE = "share";
    public static final String SNAPSHOT_LISTING_ERROR = "Listing snapshots is only supported in flat mode (no delimiter). Consider setting useFlatBlobListing to true.";
    public static final String SNAPSHOT_QUERY_OPTION_ALREADY_DEFINED = "Snapshot query parameter is already defined in the blob URI. Either pass in a snapshotTime parameter or use a full URL with a snapshot query parameter.";
    public static final String STORAGE_CREDENTIALS_NULL_OR_ANONYMOUS = "StorageCredentials cannot be null or anonymous for this service.";
    public static final String STORAGE_CLIENT_OR_SAS_REQUIRED = "Either a SAS token or a service client must be specified.";
    public static final String STORAGE_URI_MISSING_LOCATION = "The URI for the target storage location is not specified. Please consider changing the request's location mode.";
    public static final String STORAGE_URI_MUST_MATCH = "Primary and secondary location URIs in a StorageUri must point to the same resource.";
    public static final String STORAGE_URI_NOT_NULL = "Primary and secondary location URIs in a StorageUri must not both be null.";
    public static final String STOREAS_DIFFERENT_FOR_GETTER_AND_SETTER = "StoreAs Annotation found for both getter and setter for property %s with unequal values.";
    public static final String STOREAS_USED_ON_EMPTY_PROPERTY = "StoreAs Annotation found for property %s with empty value.";
    public static final String STREAM_CLOSED = "Stream is already closed.";
    public static final String STREAM_LENGTH_GREATER_THAN_4MB = "Invalid stream length, length must be less than or equal to 4 MB in size.";
    public static final String STREAM_LENGTH_NEGATIVE = "Invalid stream length, specify -1 for unknown length stream, or a positive number of bytes.";
    public static final String STRING_NOT_VALID = "The String is not a valid Base64-encoded string.";
    public static final String TABLE = "table";
    public static final String TABLE_ENDPOINT_NOT_CONFIGURED = "No table endpoint configured.";
    public static final String TABLE_OBJECT_RELATIVE_URIS_NOT_SUPPORTED = "Table Object relative URIs not supported.";
    public static final String TAKE_COUNT_ZERO_OR_NEGATIVE = "Take count must be positive and greater than 0.";
    public static final String TOO_MANY_PATH_SEGMENTS = "The count of URL path segments (strings between '/' characters) as part of the blob name cannot exceed 254.";
    public static final String TOO_MANY_SHARED_ACCESS_POLICY_IDENTIFIERS = "Too many %d shared access policy identifiers provided. Server does not support setting more than %d on a single container, queue, or table.";
    public static final String TOO_MANY_SHARED_ACCESS_POLICY_IDS = "Too many %d shared access policy identifiers provided. Server does not support setting more than %d on a single container.";
    public static final String TYPE_NOT_SUPPORTED = "Type %s is not supported.";
    public static final String UNEXPECTED_CONTINUATION_TYPE = "The continuation type passed in is unexpected. Please verify that the correct continuation type is passed in. Expected {%s}, found {%s}.";
    public static final String UNEXPECTED_FIELD_NAME = "Unexpected field name. Expected: '%s'. Actual: '%s'.";
    public static final String UNEXPECTED_STATUS_CODE_RECEIVED = "Unexpected http status code received.";
    public static final String UNEXPECTED_STREAM_READ_ERROR = "Unexpected error. Stream returned unexpected number of bytes.";
    public static final String UNKNOWN_TABLE_OPERATION = "Unknown table operation.";
} public final class CloudFile implements ListFileItem {
    /**
     * Holds the number of bytes to buffer when writing to a {@link FileOutputStream}.
     */
    protected int streamWriteSizeInBytes = Constants.DEFAULT_STREAM_WRITE_IN_BYTES;

    /**
     * Holds the minimum read size when using a {@link FileInputStream}.
     */
    protected int streamMinimumReadSizeInBytes = Constants.DEFAULT_MINIMUM_READ_SIZE_IN_BYTES;

    /**
     * Holds the file's share reference.
     */
    private CloudFileShare share;

    /**
     * Represents the file's directory reference.
     */
    protected CloudFileDirectory parent;

    /**
     * Holds the file's name.
     */
    private String name;

    /**
     * Represents the file service client.
     */
    protected CloudFileClient fileServiceClient;

    /**
     * Holds the metadata for the file.
     */
    private HashMap<String, String> metadata = new HashMap<String, String>();

    /**
     * Holds the properties of the file.
     */
    private FileProperties properties = new FileProperties();

    /**
     * Stores the absolute URI to the file.
     */
    private StorageUri storageUri;

    /**
     * Creates an instance of the <code>CloudFile</code> class using the specified absolute URI.
     * 
     * @param fileAbsoluteUri
     *            A <code>java.net.URI</code> object that represents the absolute URI to the file.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws URISyntaxException
     *             If the resource URI is invalid.
     */
    public CloudFile(final URI fileAbsoluteUri) throws StorageException, URISyntaxException {
        this(new StorageUri(fileAbsoluteUri));
    }

    /**
     * Creates an instance of the <code>CloudFile</code> class using the specified absolute StorageUri.
     * 
     * @param fileAbsoluteUri
     *            A {@link StorageUri} object that represents the absolute URI to the file.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws URISyntaxException
     *             If the resource URI is invalid.
     */
    public CloudFile(final StorageUri fileAbsoluteUri) throws StorageException, URISyntaxException {
        this(fileAbsoluteUri, null);
    }
    
    /**
     * Creates an instance of the <code>CloudFile</code> class using the specified absolute URI 
     * and credentials.
     * 
     * @param fileAbsoluteUri
     *            A <code>java.net.URI</code> object that represents the absolute URI to the file.
     * @param credentials
     *            A {@link StorageCredentials} object used to authenticate access.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws URISyntaxException 
     */
    public CloudFile(final URI fileAbsoluteUri, final StorageCredentials credentials) throws StorageException, URISyntaxException {
        this(new StorageUri(fileAbsoluteUri), credentials);
    }

    /**
     * Creates an instance of the <code>CloudFile</code> class using the specified absolute StorageUri 
     * and credentials.
     * 
     * @param fileAbsoluteUri
     *            A {@link StorageUri} object that represents the absolute URI to the file.
     * @param credentials
     *            A {@link StorageCredentials} object used to authenticate access.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws URISyntaxException 
     */
    public CloudFile(final StorageUri fileAbsoluteUri, final StorageCredentials credentials) throws StorageException, URISyntaxException {    
        this.parseQueryAndVerify(fileAbsoluteUri, credentials);
    }

    /**
     * Creates an instance of the <code>CloudFile</code> class by copying values from another cloud file.
     * 
     * @param otherFile
     *            A <code>CloudFile</code> object that represents the file to copy.
     */
    public CloudFile(final CloudFile otherFile) {
        this.metadata = new HashMap<String, String>();
        this.properties = new FileProperties(otherFile.properties);
        if (otherFile.metadata != null) {
            for (final String key : otherFile.metadata.keySet()) {
                this.metadata.put(key, otherFile.metadata.get(key));
            }
        }

        this.storageUri = otherFile.storageUri;
        this.share = otherFile.share;
        this.parent = otherFile.parent;
        this.fileServiceClient = otherFile.fileServiceClient;
        this.name = otherFile.name;
        this.setStreamMinimumReadSizeInBytes(otherFile.getStreamMinimumReadSizeInBytes());
        this.setStreamWriteSizeInBytes(otherFile.getStreamWriteSizeInBytes());
    }
    
    /**
     * Creates an instance of the <code>CloudFile</code> class using the specified address, share,
     * and client.
     * 
     * @param uri
     *            A {@link StorageUri} that represents the file directory's address.
     * @param fileName
     *            A <code>String</code> that represents the name of the file.
     * @param share
     *            A {@link CloudFileShare} object that represents the associated file share.
     */
    protected CloudFile(final StorageUri uri, final String fileName, final CloudFileShare share) {
        Utility.assertNotNull("uri", uri);
        Utility.assertNotNull("fileName", fileName);
        Utility.assertNotNull("share", share);

        this.name = fileName;
        this.fileServiceClient = share.getServiceClient();
        this.share = share;
        this.storageUri = uri;
    }

    /**
     * Aborts an ongoing Azure File copy operation.
     *
     * @param copyId
     *            A <code>String</code> object that identifies the copy operation.
     *
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws URISyntaxException 
     */
    @DoesServiceRequest
    public final void abortCopy(final String copyId) throws StorageException, URISyntaxException {
        this.abortCopy(copyId, null /* accessCondition */, null /* options */, null /* opContext */);
    }

    /**
     * Aborts an ongoing Azure File copy operation.
     *
     * @param copyId
     *            A <code>String</code> object that identifies the copy operation.
     * @param accessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the Azure File.
     * @param options
     *            A {@link FileRequestOptions} 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 CloudFileClient}).
     * @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.
     * @throws URISyntaxException
     */
    @DoesServiceRequest
    public final void abortCopy(final String copyId, final AccessCondition accessCondition, FileRequestOptions options,
            OperationContext opContext) throws StorageException, URISyntaxException {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        this.getShare().assertNoSnapshot();

        opContext.initialize();
        options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);

        ExecutionEngine.executeWithRetry(this.fileServiceClient, this,
                this.abortCopyImpl(copyId, accessCondition, options), options.getRetryPolicyFactory(), opContext);
    }

    private StorageRequest<CloudFileClient, CloudFile, Void> abortCopyImpl(final String copyId,
            final AccessCondition accessCondition, final FileRequestOptions options) {
        Utility.assertNotNull("copyId", copyId);

        final StorageRequest<CloudFileClient, CloudFile, Void> putRequest =
                new StorageRequest<CloudFileClient, CloudFile, Void>(options, this.getStorageUri()) {

            @Override
            public HttpURLConnection buildRequest(CloudFileClient client, CloudFile file, OperationContext context)
                    throws Exception {
                return FileRequest.abortCopy(file.getTransformedAddress(context).getUri(this.getCurrentLocation()),
                        options, context, accessCondition, copyId);
            }

            @Override
            public void signRequest(HttpURLConnection connection, CloudFileClient client, OperationContext context)
                    throws Exception {
                StorageRequest.signBlobQueueAndFileRequest(connection, client, 0, context);
            }

            @Override
            public Void preProcessResponse(CloudFile parentObject, CloudFileClient client, OperationContext context)
                    throws Exception {
                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_ACCEPTED) {
                    this.setNonExceptionedRetryableFailure(true);
                    return null;
                }

                return null;
            }
        };

        return putRequest;
    }

    /**
     * Requests the service to start copying a blob's contents, properties, and metadata to a new file.
     *
     * @param sourceBlob
     *            A <code>CloudBlob</code> object that represents the source blob to copy.
     *
     * @return A <code>String</code> which represents the copy ID associated with the copy operation.
     *
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws URISyntaxException
     */
    @DoesServiceRequest
    public final String startCopy(final CloudBlob sourceBlob) throws StorageException, URISyntaxException {
        return this.startCopy(sourceBlob, null /* sourceAccessCondition */,
                null /* destinationAccessCondition */, null /* options */, null /* opContext */);
    }

    /**
     * Requests the service to start copying a file's contents, properties, and metadata to a new file,
     * using the specified access conditions, lease ID, request options, and operation context.
     *
     * @param sourceBlob
     *            A <code>CloudBlob</code> object that represents the source blob to copy.
     * @param sourceAccessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the source blob.
     * @param destinationAccessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the destination file.
     * @param options
     *            A {@link BlobRequestOptions} 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}).
     * @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 <code>String</code> which represents the copy ID associated with the copy operation.
     *
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws URISyntaxException
     *
     */
    @DoesServiceRequest
    public final String startCopy(final CloudBlob sourceBlob, final AccessCondition sourceAccessCondition,
            final AccessCondition destinationAccessCondition, FileRequestOptions options, OperationContext opContext)
            throws StorageException, URISyntaxException {
        Utility.assertNotNull("sourceBlob", sourceBlob);

        URI source = sourceBlob.getSnapshotQualifiedUri();
        if (sourceBlob.getServiceClient() != null && sourceBlob.getServiceClient().getCredentials() != null)
        {
            source = sourceBlob.getServiceClient().getCredentials().transformUri(sourceBlob.getSnapshotQualifiedUri());
        }

        return this.startCopy(source, sourceAccessCondition, destinationAccessCondition, options, opContext);
    }

    /**
     * Requests the service to start copying an Azure File's contents, properties, and metadata to a new Azure File.
     *
     * @param sourceFile
     *            A <code>CloudFile</code> object that represents the source Azure File to copy.
     *
     * @return A <code>String</code> which represents the copy ID associated with the copy operation.
     *
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws URISyntaxException
     */
    @DoesServiceRequest
    public final String startCopy(final CloudFile sourceFile) throws StorageException, URISyntaxException {
        return this.startCopy(sourceFile, null /* sourceAccessCondition */,
                null /* destinationAccessCondition */, null /* options */, null /* opContext */);
    }

    /**
     * Requests the service to start copying an Azure File's contents, properties, and metadata to a new Azure File,
     * using the specified access conditions, lease ID, request options, and operation context.
     *
     * @param sourceFile
     *            A <code>CloudFile</code> object that represents the source file to copy.
     * @param sourceAccessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the source.
     * @param destinationAccessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the destination.
     * @param options
     *            A {@link FileRequestOptions} 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 CloudFileClient}).
     * @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 <code>String</code> which represents the copy ID associated with the copy operation.
     *
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws URISyntaxException
     *
     */
    @DoesServiceRequest
    public final String startCopy(final CloudFile sourceFile, final AccessCondition sourceAccessCondition,
            final AccessCondition destinationAccessCondition, FileRequestOptions options, OperationContext opContext)
            throws StorageException, URISyntaxException {
        Utility.assertNotNull("sourceFile", sourceFile);
        return this.startCopy(sourceFile.getTransformedAddress(opContext).getPrimaryUri(),
                sourceAccessCondition, destinationAccessCondition, options, opContext);

    }

    /**
     * Requests the service to start copying a URI's contents, properties, and metadata to a new Azure File.
     *
     * @param source
     *            The source's <code>java.net.URI</code>.
     *
     * @return A <code>String</code> which represents the copy ID associated with the copy operation.
     *
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws URISyntaxException 
     */
    @DoesServiceRequest
    public final String startCopy(final URI source) throws StorageException, URISyntaxException {
        return this.startCopy(source, null /* sourceAccessCondition */,
                null /* destinationAccessCondition */, null /* options */, null /* opContext */);
    }

    /**
     * Requests the service to start copying a URI's contents, properties, and metadata to a new Azure File,
     * using the specified access conditions, lease ID, request options, and operation context.
     *
     * @param source
     *            The source's <code>java.net.URI</code>.
     * @param sourceAccessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the source.
     * @param destinationAccessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the destination.
     * @param options
     *            A {@link FileRequestOptions} 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 CloudFileClient}).
     * @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 <code>String</code> which represents the copy ID associated with the copy operation.
     *
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws URISyntaxException 
     *
     */
    @DoesServiceRequest
    public final String startCopy(final URI source, final AccessCondition sourceAccessCondition,
            final AccessCondition destinationAccessCondition, FileRequestOptions options, OperationContext opContext)
            throws StorageException, URISyntaxException {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        this.getShare().assertNoSnapshot();

        opContext.initialize();
        options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);

        return ExecutionEngine.executeWithRetry(this.fileServiceClient, this,
                this.startCopyImpl(source, sourceAccessCondition, destinationAccessCondition, options),
                options.getRetryPolicyFactory(), opContext);
    }

    private StorageRequest<CloudFileClient, CloudFile, String> startCopyImpl(final URI source,
            final AccessCondition sourceAccessCondition, final AccessCondition destinationAccessCondition,
            final FileRequestOptions options) {

        if (sourceAccessCondition != null && !Utility.isNullOrEmpty(sourceAccessCondition.getLeaseID())) {
            throw new IllegalArgumentException(SR.LEASE_CONDITION_ON_SOURCE);
        }

        final StorageRequest<CloudFileClient, CloudFile, String> putRequest =
                new StorageRequest<CloudFileClient, CloudFile, String>(options, this.getStorageUri()) {

            @Override
            public HttpURLConnection buildRequest(CloudFileClient client, CloudFile file, OperationContext context)
                    throws Exception {
                return FileRequest.copyFrom(file.getTransformedAddress(context).getUri(this.getCurrentLocation()),
                        options, context, sourceAccessCondition, destinationAccessCondition, source.toASCIIString());
            }

            @Override
            public void setHeaders(HttpURLConnection connection, CloudFile file, OperationContext context) {
                FileRequest.addMetadata(connection, file.metadata, context);
            }

            @Override
            public void signRequest(HttpURLConnection connection, CloudFileClient client, OperationContext context)
                    throws Exception {
                StorageRequest.signBlobQueueAndFileRequest(connection, client, 0, context);
            }

            @Override
            public String preProcessResponse(CloudFile file, CloudFileClient client, OperationContext context)
                    throws Exception {
                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_ACCEPTED) {
                    this.setNonExceptionedRetryableFailure(true);
                    return null;
                }

                file.updateEtagAndLastModifiedFromResponse(this.getConnection());
                file.properties.setCopyState(FileResponse.getCopyState(this.getConnection()));

                return file.properties.getCopyState().getCopyId();
            }
        };

        return putRequest;
    }

    /**
     * Clears a range from a file.
     * <p>
     * Calling <code>clearRange</code> releases the storage space used by the specified range. Ranges that have been
     * cleared are no longer tracked as part of the file.
     * 
     * @param offset
     *            The offset, in bytes, at which to begin clearing.
     * @param length
     *            The length, in bytes, of the data range to be cleared.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws URISyntaxException 
     */
    @DoesServiceRequest
    public void clearRange(final long offset, final long length) throws StorageException, URISyntaxException {
        this.clearRange(offset, length, null /* accessCondition */, null /* options */, null /* opContext */);
    }

    /**
     * Clears a range from a file using the specified lease ID, request options, and operation context.
     * <p>
     * Calling <code>clearRange</code> releases the storage space used by the specified range. Ranges that have been
     * cleared are no longer tracked as part of the file.
     * 
     * @param offset
     *            A <code>long</code> which represents the offset, in bytes, at which to begin clearing.
     * @param length
     *            A <code>long</code> which represents the length, in bytes, of the data range to be cleared.
     * @param accessCondition
     *            An {@link AccessCondition} object which represents the access conditions for the file.
     * @param options
     *            A {@link FileRequestOptions} 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 CloudFileClient}).
     * @param opContext
     *            An {@link OperationContext} object which 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.
     * @throws URISyntaxException 
     */
    @DoesServiceRequest
    public void clearRange(final long offset, final long length, final AccessCondition accessCondition,
            FileRequestOptions options, OperationContext opContext) throws StorageException, URISyntaxException {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        this.getShare().assertNoSnapshot();

        options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);

        final FileRange range = new FileRange(offset, offset + length - 1);

        this.putRangeInternal(range, FileRangeOperationType.CLEAR, null, length, null, accessCondition, options,
                opContext);
    }

    /**
     * Creates a file. If the file already exists, this will replace it.
     * 
     * @param size
     *            A <code>long</code> which represents the size, in bytes, of the file.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws URISyntaxException 
     */
    @DoesServiceRequest
    public void create(final long size) throws StorageException, URISyntaxException {
        this.create(size, null /* accessCondition */, null /* options */, null /* opContext */);
    }

    /**
     * Creates a file using the specified access condition, request options and operation context. If the file already 
     * exists, this will replace it.
     * 
     * @param size
     *            A <code>long</code> which represents the size, in bytes, of the file.
     * @param accessCondition
     *            An {@link AccessCondition} object which represents the access conditions for the file.
     * @param options
     *            A {@link FileRequestOptions} 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 CloudFileClient}).
     * @param opContext
     *            An {@link OperationContext} object which 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.
     * @throws URISyntaxException 
     */
    @DoesServiceRequest
    public void create(final long size, final AccessCondition accessCondition, FileRequestOptions options,
            OperationContext opContext) throws StorageException, URISyntaxException {

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

        this.getShare().assertNoSnapshot();

        options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);

        ExecutionEngine.executeWithRetry(this.fileServiceClient, this, this.createImpl(size, accessCondition, options),
                options.getRetryPolicyFactory(), opContext);
    }

    private StorageRequest<CloudFileClient, CloudFile, Void> createImpl(final long size,
            final AccessCondition accessCondition, final FileRequestOptions options) {
        final StorageRequest<CloudFileClient, CloudFile, Void> putRequest =
                new StorageRequest<CloudFileClient, CloudFile, Void>(options, this.getStorageUri()) {

            @Override
            public HttpURLConnection buildRequest(CloudFileClient client, CloudFile file, OperationContext context)
                    throws Exception {
                return FileRequest.putFile(file.getTransformedAddress(context).getUri(this.getCurrentLocation()),
                        options, context, accessCondition, file.properties, size);
            }

            @Override
            public void setHeaders(HttpURLConnection connection, CloudFile file, OperationContext context) {
                FileRequest.addMetadata(connection, file.metadata, context);
            }

            @Override
            public void signRequest(HttpURLConnection connection, CloudFileClient client, OperationContext context)
                    throws Exception {
                StorageRequest.signBlobQueueAndFileRequest(connection, client, 0L, context);
            }

            @Override
            public Void preProcessResponse(CloudFile file, CloudFileClient client, OperationContext context)
                    throws Exception {
                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_CREATED) {
                    this.setNonExceptionedRetryableFailure(true);
                    return null;
                }

                file.updateEtagAndLastModifiedFromResponse(this.getConnection());
                this.getResult().setRequestServiceEncrypted(BaseResponse.isServerRequestEncrypted(this.getConnection()));
                return null;
            }

        };

        return putRequest;
    }

    /**
     * Deletes the file.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws URISyntaxException 
     */
    @DoesServiceRequest
    public final void delete() throws StorageException, URISyntaxException {
        this.delete(null /* accessCondition */, null /* options */, null /* opContext */);
    }

    /**
     * Deletes the file using the specified access condition, request options, and operation context.
     * 
     * @param accessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the file.
     * @param options
     *            A {@link FileRequestOptions} 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 CloudFileClient}).
     * @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.
     * @throws URISyntaxException 
     */
    @DoesServiceRequest
    public final void delete(final AccessCondition accessCondition, FileRequestOptions options,
            OperationContext opContext) throws StorageException, URISyntaxException {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        this.getShare().assertNoSnapshot();

        opContext.initialize();
        options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);

        ExecutionEngine.executeWithRetry(this.fileServiceClient, this, this.deleteImpl(accessCondition, options),
                options.getRetryPolicyFactory(), opContext);
    }

    /**
     * Deletes the file if it exists.
     * 
     * @return <code>true</code> if the file was deleted; otherwise, <code>false</code>.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws URISyntaxException 
     * 
     */
    @DoesServiceRequest
    public final boolean deleteIfExists() throws StorageException, URISyntaxException {
        return this.deleteIfExists(null /* accessCondition */, null /* options */, null /* opContext */);
    }

    /**
     * Deletes the file if it exists, using the specified access condition, request options, and operation context.
     * 
     * @param accessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the file.
     * @param options
     *            A {@link FileRequestOptions} 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 CloudFileClient}).
     * @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 <code>true</code> if the file existed and was deleted; otherwise, <code>false</code>
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws URISyntaxException 
     */
    @DoesServiceRequest
    public final boolean deleteIfExists(final AccessCondition accessCondition, FileRequestOptions options,
            OperationContext opContext) throws StorageException, URISyntaxException {
        options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);
        this.getShare().assertNoSnapshot();

        boolean exists = this.exists(true, accessCondition, options, opContext);
        if (exists) {
            try {
                this.delete(accessCondition, options, opContext);
                return true;
            }
            catch (StorageException e) {
                if (e.getHttpStatusCode() == HttpURLConnection.HTTP_NOT_FOUND
                        && StorageErrorCodeStrings.RESOURCE_NOT_FOUND.equals(e.getErrorCode())) {
                    return false;
                }
                else {
                    throw e;
                }
            }

        }
        else {
            return false;
        }
    }

    private StorageRequest<CloudFileClient, CloudFile, Void> deleteImpl(final AccessCondition accessCondition,
            final FileRequestOptions options) {
        final StorageRequest<CloudFileClient, CloudFile, Void> deleteRequest =
                new StorageRequest<CloudFileClient, CloudFile, Void>(options, this.getStorageUri()) {

            @Override
            public HttpURLConnection buildRequest(CloudFileClient client, CloudFile file, OperationContext context)
                    throws Exception {
                return FileRequest.deleteFile(file.getTransformedAddress(context).getUri(this.getCurrentLocation()),
                        options, context, accessCondition);
            }

            @Override
            public void signRequest(HttpURLConnection connection, CloudFileClient client, OperationContext context)
                    throws Exception {
                StorageRequest.signBlobQueueAndFileRequest(connection, client, 0L, null);
            }

            @Override
            public Void preProcessResponse(CloudFile file, CloudFileClient client, OperationContext context)
                    throws Exception {
                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_ACCEPTED) {
                    this.setNonExceptionedRetryableFailure(true);
                    return null;
                }

                return null;
            }
        };

        return deleteRequest;
    }

    /**
     * Downloads the contents of a file to a stream.
     * 
     * @param outStream
     *            An <code>{@link OutputStream}</code> object that represents the target stream.
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public final void download(final OutputStream outStream) throws StorageException {
        this.download(outStream, null /* accessCondition */, null /* options */, null /* opContext */);
    }

    /**
     * Downloads the contents of a file to a stream using the specified request options and operation context.
     * 
     * @param outStream
     *            An <code>OutputStream</code> object that represents the target stream.
     * @param accessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the file.
     * @param options
     *            A {@link FileRequestOptions} 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 CloudFileClient}).
     * @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.
     */
    @DoesServiceRequest
    public final void download(final OutputStream outStream, final AccessCondition accessCondition,
            FileRequestOptions options, OperationContext opContext) throws StorageException {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        opContext.initialize();
        options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);

        ExecutionEngine.executeWithRetry(this.fileServiceClient, this, this.downloadToStreamImpl(
                null /* fileOffset */, null /* length */, outStream, accessCondition, options, opContext), options
                .getRetryPolicyFactory(), opContext);
    }

    /**
     * Downloads the contents of a file to a stream.
     * 
     * @param offset
     *            A <code>long</code> which represents the offset to use as the starting point for the source.
     * @param length
     *            A <code>Long</code> which represents the number of bytes to read or <code>null</code>.
     * @param outStream
     *            An <code>{@link OutputStream}</code> object that represents the target stream.
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public final void downloadRange(final long offset, final Long length, final OutputStream outStream)
            throws StorageException {
        this.downloadRange(offset, length, outStream, null /* accessCondition */, null /* options */, null /* opContext */);
    }

    /**
     * Downloads the contents of a file to a stream using the specified request options and operation context.
     * 
     * @param offset
     *            A <code>long</code> which represents the offset to use as the starting point for the source.
     * @param length
     *            A <code>Long</code> which represents the number of bytes to read or <code>null</code>.
     * @param outStream
     *            An <code>{@link OutputStream}</code> object that represents the target stream.
     * @param accessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the file.
     * @param options
     *            A {@link FileRequestOptions} 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 CloudFileClient}).
     * @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.
     */
    @DoesServiceRequest
    public final void downloadRange(final long offset, final Long length, final OutputStream outStream,
            final AccessCondition accessCondition, FileRequestOptions options, OperationContext opContext)
            throws StorageException {
        if (offset < 0 || (length != null && length <= 0)) {
            throw new IndexOutOfBoundsException();
        }

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

        opContext.initialize();
        options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);

        ExecutionEngine.executeWithRetry(this.fileServiceClient, this,
                this.downloadToStreamImpl(offset, length, outStream, accessCondition, options, opContext),
                options.getRetryPolicyFactory(), opContext);
    }

    /**
     * Downloads a range of bytes from the file to the given byte buffer.
     * 
     * @param fileOffset
     *            A <code>long</code> which represents the offset within the file to begin downloading.
     * @param length
     *            A <code>Long</code> which represents the number of bytes to read.
     * @param buffer
     *            A <code>byte</code> array which represents the buffer to write to.
     * @param bufferOffset
     *            An <code>int</code> which represents the offset in the byte buffer to begin writing.
     * @param accessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the file.
     * @param options
     *            A {@link FileRequestOptions} object that specifies any additional options for the request.
     * @param opContext
     *            An {@link OperationContext} object used to track the execution of the operation.
     * @throws StorageException
     *             an exception representing any error which occurred during the operation.
     */
    @DoesServiceRequest
    protected final int downloadRangeInternal(final long fileOffset, final Long length, final byte[] buffer,
            final int bufferOffset, final AccessCondition accessCondition, FileRequestOptions options,
            OperationContext opContext) throws StorageException {

        if (bufferOffset < 0 || fileOffset < 0 || (length != null && length <= 0)) {
            throw new IndexOutOfBoundsException();
        }

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

        options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);
        
        WrappedByteArrayOutputStream outputStream = new WrappedByteArrayOutputStream(buffer, bufferOffset);
        ExecutionEngine.executeWithRetry(this.fileServiceClient, this,
                this.downloadToStreamImpl(fileOffset, length, outputStream, accessCondition, options, opContext),
                options.getRetryPolicyFactory(), opContext);
        return outputStream.getPosition();
    }

    /**
     * Downloads a range of bytes from the file to the given byte buffer.
     * 
     * @param offset
     *            A <code>long</code> which represents the byte offset to use as the starting point for the source.
     * @param length
     *            A <code>Long</code> which represents the number of bytes to read or null.
     * @param buffer
     *            A <code>byte</code> array which represents the buffer to which the file bytes are downloaded.
     * @param bufferOffset
     *            An <code>int</code> which represents the byte offset to use as the starting point for the target.
     * 
     * @throws StorageException
     */
    @DoesServiceRequest
    public final int downloadRangeToByteArray(final long offset, final Long length, final byte[] buffer,
            final int bufferOffset) throws StorageException {
        return this.downloadRangeToByteArray(offset, length, buffer, bufferOffset, null /* accessCondition */,
                null /* options */, null /* opContext */);
    }

    /**
     * Downloads a range of bytes from the file to the given byte buffer, using the specified request options and
     * operation context.
     * 
     * @param offset
     *            A <code>long</code> which represents the byte offset to use as the starting point for the source.
     * @param length
     *            A <code>Long</code> which represents the number of bytes to read or <code>null</code>.
     * @param buffer
     *            A <code>byte</code> array which represents the buffer to which the file bytes are downloaded.
     * @param bufferOffset
     *            An <code>int</code> which represents the byte offset to use as the starting point for the target.
     * @param accessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the file.
     * @param options
     *            A {@link FileRequestOptions} 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 CloudFileClient}).
     * @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.
     */
    @DoesServiceRequest
    public final int downloadRangeToByteArray(final long offset, final Long length, final byte[] buffer,
            final int bufferOffset, final AccessCondition accessCondition, FileRequestOptions options,
            OperationContext opContext) throws StorageException {

        Utility.assertNotNull("buffer", buffer);

        if (length != null) {
            if (length + bufferOffset > buffer.length) {
                throw new IndexOutOfBoundsException();
            }
        }

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

        opContext.initialize();

        return this.downloadRangeInternal(offset, length, buffer, bufferOffset, accessCondition, options, opContext);
    }

    /**
     * Downloads a range of bytes from the file to the given byte buffer.
     * 
     * @param buffer
     *            A <code>byte</code> array which represents the buffer to which the file bytes are downloaded.
     * @param bufferOffset
     *            An <code>int</code> which represents the byte offset to use as the starting point for the target.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public final int downloadToByteArray(final byte[] buffer, final int bufferOffset) throws StorageException {
        return this
                .downloadToByteArray(buffer, bufferOffset, null /* accessCondition */, null /* options */, null /* opContext */);
    }

    /**
     * Downloads a range of bytes from the file to the given byte buffer, using the specified request options and
     * operation context.
     * 
     * @param buffer
     *            A <code>byte</code> array which represents the buffer to which the file bytes are downloaded.
     * @param bufferOffset
     *            A <code>long</code> which represents the byte offset to use as the starting point for the target.
     * @param accessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the file.
     * @param options
     *            A {@link FileRequestOptions} 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 CloudFileClient}).
     * @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.
     */
    @DoesServiceRequest
    public final int downloadToByteArray(final byte[] buffer, final int bufferOffset,
            final AccessCondition accessCondition, FileRequestOptions options, OperationContext opContext)
            throws StorageException {

        Utility.assertNotNull("buffer", buffer);
        if (bufferOffset < 0) {
            throw new IndexOutOfBoundsException();
        }

        if (bufferOffset >= buffer.length) {
            throw new IndexOutOfBoundsException();
        }

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

        opContext.initialize();
        options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);
        
        WrappedByteArrayOutputStream outputStream = new WrappedByteArrayOutputStream(buffer, bufferOffset);
        ExecutionEngine.executeWithRetry(this.fileServiceClient, this,
                this.downloadToStreamImpl(null, null, outputStream, accessCondition, options, opContext),
                options.getRetryPolicyFactory(), opContext);
        return outputStream.getPosition();
    }

    /**
     * Downloads a file.
     * 
     * @param path
     *            A <code>String</code> which represents the path to the file that will be created.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws IOException
     */
    public void downloadToFile(final String path) throws StorageException, IOException {
        downloadToFile(path, null /* accessCondition */, null /* options */, null /* opContext */);
    }

    /**
     * Downloads a file.
     * 
     * @param path
     *            A <code>String</code> which represents the path to the file that will be created.
     * @param accessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the file.
     * @param options
     *            A {@link FileRequestOptions} 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 CloudFileClient}).
     * @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.
     * @throws IOException
     */
    public void downloadToFile(final String path, final AccessCondition accessCondition, FileRequestOptions options,
            OperationContext opContext) throws StorageException, IOException {
        OutputStream outputStream = new BufferedOutputStream(new java.io.FileOutputStream(path));
        try {
            this.download(outputStream, accessCondition, options, opContext);
            outputStream.close();
        }
        catch (StorageException e) {
            deleteEmptyFileOnException(outputStream, path);
            throw e;
        }
        catch (IOException e) {
            deleteEmptyFileOnException(outputStream, path);
            throw e;
        }
    }

    /**
     * Helper to delete an empty file in the case of an exception
     * 
     * @param outputStream
     * @param path
     * @throws IOException
     */
    private void deleteEmptyFileOnException(OutputStream outputStream, String path) {
        try {
            outputStream.close();
            File fileToDelete = new File(path);
            fileToDelete.delete();
        }
        catch (Exception e) {
            // Best effort delete.
        }
    }

    /**
     * Downloads a file to a string using the platform's default encoding.
     * 
     * @return A <code>String</code> which represents the file's contents.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws IOException
     */
    public String downloadText() throws StorageException, IOException {
        return this
                .downloadText(null /* charsetName */, null /* accessCondition */, null /* options */, null /* opContext */);
    }

    /**
     * Downloads a file to a string using the specified encoding.
     * 
     * @param charsetName
     *            A <code>String</code> which represents the name of the charset to use to encode the content.
     *            If null, the platform's default encoding is used.
     * @param accessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the file.
     * @param options
     *            A {@link FileRequestOptions} 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 CloudFileClient}).
     * @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 <code>String</code> which represents the file's contents.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws IOException
     */
    public String downloadText(final String charsetName, final AccessCondition accessCondition,
            FileRequestOptions options, OperationContext opContext) throws StorageException, IOException {

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        this.download(baos, accessCondition, options, opContext);
        return charsetName == null ? baos.toString() : baos.toString(charsetName);
    }

    /**
     * Returns a collection of file ranges and their starting and ending byte offsets.
     * <p>
     * The start and end byte offsets for each file range are inclusive.
     * 
     * @return An <code>ArrayList</code> object which represents the set of file ranges and their starting and ending
     *         byte offsets.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public ArrayList<FileRange> downloadFileRanges() throws StorageException {
        return this.downloadFileRanges(null /* accessCondition */, null /* options */, null /* opContext */);
    }

    /**
     * Returns a collection of file ranges and their starting and ending byte offsets using the specified request
     * options and operation context.
     * 
     * @param accessCondition
     *            An {@link AccessCondition} object which represents the access conditions for the file.
     * @param options
     *            A {@link FileRequestOptions} 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 CloudFileClient}).
     * @param opContext
     *            An {@link OperationContext} object which 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 An <code>ArrayList</code> object which represents the set of file ranges and their starting
     *            and ending byte offsets.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public ArrayList<FileRange> downloadFileRanges(final AccessCondition accessCondition, FileRequestOptions options,
            OperationContext opContext) throws StorageException {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);

        return ExecutionEngine.executeWithRetry(this.fileServiceClient, this,
                this.downloadFileRangesImpl(accessCondition, options), options.getRetryPolicyFactory(), opContext);
    }

    private StorageRequest<CloudFileClient, CloudFile, ArrayList<FileRange>> downloadFileRangesImpl(
            final AccessCondition accessCondition, final FileRequestOptions options) {
        final StorageRequest<CloudFileClient, CloudFile, ArrayList<FileRange>> getRequest =
                new StorageRequest<CloudFileClient, CloudFile, ArrayList<FileRange>>(options, this.getStorageUri()) {

            @Override
            public void setRequestLocationMode() {
                this.setRequestLocationMode(RequestLocationMode.PRIMARY_OR_SECONDARY);
            }

            @Override
            public HttpURLConnection buildRequest(CloudFileClient client, CloudFile file, OperationContext context)
                    throws Exception {
                return FileRequest.getFileRanges(file.getTransformedAddress(context).getUri(this.getCurrentLocation()),
                        options, context, accessCondition, file.getShare().snapshotID);
            }

            @Override
            public void signRequest(HttpURLConnection connection, CloudFileClient client, OperationContext context)
                    throws Exception {
                StorageRequest.signBlobQueueAndFileRequest(connection, client, -1L, context);
            }

            @Override
            public ArrayList<FileRange> preProcessResponse(CloudFile parentObject, CloudFileClient client,
                    OperationContext context) throws Exception {
                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_OK) {
                    this.setNonExceptionedRetryableFailure(true);
                }

                return null;
            }

            @Override
            public ArrayList<FileRange> postProcessResponse(HttpURLConnection connection, CloudFile file,
                    CloudFileClient client, OperationContext context, ArrayList<FileRange> storageObject)
                    throws Exception {
                file.updateEtagAndLastModifiedFromResponse(this.getConnection());
                file.updateLengthFromResponse(this.getConnection());

                return FileRangeHandler.getFileRanges(this.getConnection().getInputStream());
            }

        };

        return getRequest;
    }

    @DoesServiceRequest
    private final StorageRequest<CloudFileClient, CloudFile, Integer> downloadToStreamImpl(final Long fileOffset,
            final Long length, final OutputStream outStream, final AccessCondition accessCondition,
            final FileRequestOptions options, OperationContext opContext) {

        final long startingOffset = fileOffset == null ? 0 : fileOffset;
        final StorageRequest<CloudFileClient, CloudFile, Integer> getRequest = new StorageRequest<CloudFileClient, CloudFile, Integer>(
                options, this.getStorageUri()) {

            @Override
            public void setRequestLocationMode() {
                this.setRequestLocationMode(RequestLocationMode.PRIMARY_OR_SECONDARY);
            }

            @Override
            public HttpURLConnection buildRequest(CloudFileClient client, CloudFile file, OperationContext context)
                    throws Exception {

                // The first time this is called, we have to set the length and offset.
                // On retries, these will already have values and need not be called.
                if (this.getOffset() == null) {
                    this.setOffset(fileOffset);
                }

                if (this.getLength() == null) {
                    this.setLength(length);
                }

                // Only do this when we have support from the service.
                // AccessCondition tempCondition = (this.getETagLockCondition() != null) ? this.getETagLockCondition()
                //         : accessCondition;

                return FileRequest.getFile(file.getTransformedAddress(context).getUri(this.getCurrentLocation()),
                        options, context, accessCondition, file.getShare().snapshotID, this.getOffset(), this.getLength(),
                        (options.getUseTransactionalContentMD5() && !this.getArePropertiesPopulated()));
            }

            @Override
            public void signRequest(HttpURLConnection connection, CloudFileClient client, OperationContext context)
                    throws Exception {
                StorageRequest.signBlobQueueAndFileRequest(connection, client, -1L, context);
            }

            @Override
            public Integer preProcessResponse(CloudFile file, CloudFileClient client, OperationContext context)
                    throws Exception {
                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_PARTIAL
                        && this.getResult().getStatusCode() != HttpURLConnection.HTTP_OK) {
                    this.setNonExceptionedRetryableFailure(true);
                    return null;
                }

                if (!this.getArePropertiesPopulated()) {
                    final FileAttributes retrievedAttributes = FileResponse.getFileAttributes(this.getConnection(),
                            file.getStorageUri());

                    file.properties = retrievedAttributes.getProperties();
                    file.metadata = retrievedAttributes.getMetadata();

                    // Need to store the Content MD5 in case we fail part way through.
                    // We would still need to verify the entire range.
                    this.setContentMD5(this.getConnection().getHeaderField(Constants.HeaderConstants.CONTENT_MD5));

                    if (!options.getDisableContentMD5Validation() && options.getUseTransactionalContentMD5()
                            && Utility.isNullOrEmpty(this.getContentMD5())) {
                        throw new StorageException(StorageErrorCodeStrings.MISSING_MD5_HEADER, SR.MISSING_MD5,
                                Constants.HeaderConstants.HTTP_UNUSED_306, null, null);
                    }

                    this.setLockedETag(file.properties.getEtag());
                    this.setArePropertiesPopulated(true);
                }
                else {
                    if (this.getLockedETag() != null) {
                        if (!this.getLockedETag().equals(file.properties.getEtag())) {
                            throw new StorageException(StorageErrorCode.CONDITION_FAILED.toString(),
                                    SR.INVALID_CONDITIONAL_HEADERS, HttpURLConnection.HTTP_PRECON_FAILED, null, null);
                        }
                    }
                }

                // If the download fails and we need to resume the download, going to the
                // same storage location is important to prevent a possible ETag mismatch.
                this.setRequestLocationMode((this.getResult().getTargetLocation() == StorageLocation.PRIMARY) ?
                        RequestLocationMode.PRIMARY_ONLY : RequestLocationMode.SECONDARY_ONLY);
                return null;
            }

            @Override
            public Integer postProcessResponse(HttpURLConnection connection, CloudFile file, CloudFileClient client,
                    OperationContext context, Integer storageObject) throws Exception {
                final Boolean validateMD5 = !options.getDisableContentMD5Validation()
                        && !Utility.isNullOrEmpty(this.getContentMD5());
                final String contentLength = connection.getHeaderField(Constants.HeaderConstants.CONTENT_LENGTH);
                final long expectedLength = Long.parseLong(contentLength);

                Logger.info(context, String.format(SR.CREATING_NETWORK_STREAM, expectedLength));
                final NetworkInputStream streamRef = new NetworkInputStream(connection.getInputStream(), expectedLength);

                try {
                    // writeToOutputStream will update the currentRequestByteCount on this request in case a retry
                    // is needed and download should resume from that point
                    final StreamMd5AndLength descriptor = Utility.writeToOutputStream(streamRef, outStream, -1, false,
                            validateMD5, context, options, this, this.getCurrentDescriptor());

                    // length was already checked by the NetworkInputStream, now check Md5
                    if (validateMD5 && !this.getContentMD5().equals(descriptor.getMd5())) {
                        throw new StorageException(StorageErrorCodeStrings.INVALID_MD5, String.format(
                                SR.FILE_HASH_MISMATCH, this.getContentMD5(), descriptor.getMd5()),
                                Constants.HeaderConstants.HTTP_UNUSED_306, null, null);
                    }
                }
                finally {
                    // Close the stream and return. Closing an already closed stream is harmless. So its fine to try 
                    // to drain the response and close the stream again in the executor.
                    streamRef.close();
                }

                return null;
            }

            @Override
            public void recoveryAction(OperationContext context) throws IOException {
                if (this.getETagLockCondition() == null && (!Utility.isNullOrEmpty(this.getLockedETag()))) {
                    AccessCondition etagLockCondition = new AccessCondition();
                    etagLockCondition.setIfMatch(this.getLockedETag());
                    if (accessCondition != null) {
                        etagLockCondition.setLeaseID(accessCondition.getLeaseID());
                    }
                    this.setETagLockCondition(etagLockCondition);
                }

                if (this.getCurrentRequestByteCount() > 0) {
                    this.setOffset(startingOffset + this.getCurrentRequestByteCount());
                    if (length != null) {
                        this.setLength(length - this.getCurrentRequestByteCount());
                    }
                }
            }
        };

        return getRequest;
    }

    /**
     * Populates a file's properties and metadata.
     * <p>
     * This method populates the file's system properties and user-defined metadata. Before reading or modifying
     * a file's properties or metadata, call this method or its overload to retrieve the latest values for the
     * file's properties and metadata from the Microsoft Azure storage service.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public final void downloadAttributes() throws StorageException {
        this.downloadAttributes(null /* accessCondition */, null /* options */, null /* opContext */);
    }

    /**
     * Populates a file's properties and metadata using the specified request options and operation context.
     * <p>
     * This method populates the file's system properties and user-defined metadata. Before reading or modifying
     * a file's properties or metadata, call this method or its overload to retrieve the latest values for
     * the file's properties and metadata from the Microsoft Azure storage service.
     * 
     * @param accessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the file.
     * @param options
     *            A {@link FileRequestOptions} 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 CloudFileClient}).
     * @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.
     */
    @DoesServiceRequest
    public final void downloadAttributes(final AccessCondition accessCondition, FileRequestOptions options,
            OperationContext opContext) throws StorageException {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);

        ExecutionEngine.executeWithRetry(this.fileServiceClient, this,
                this.downloadAttributesImpl(accessCondition, options), options.getRetryPolicyFactory(), opContext);
    }

    private StorageRequest<CloudFileClient, CloudFile, Void> downloadAttributesImpl(
            final AccessCondition accessCondition, final FileRequestOptions options) {
        final StorageRequest<CloudFileClient, CloudFile, Void> getRequest =
                new StorageRequest<CloudFileClient, CloudFile, Void>(options, this.getStorageUri()) {

            @Override
            public void setRequestLocationMode() {
                this.setRequestLocationMode(RequestLocationMode.PRIMARY_OR_SECONDARY);
            }

            @Override
            public HttpURLConnection buildRequest(CloudFileClient client, CloudFile file, OperationContext context)
                    throws Exception {
                return FileRequest.getFileProperties(
                        file.getTransformedAddress(context).getUri(this.getCurrentLocation()),
                        options, context, accessCondition, file.getShare().snapshotID);
            }

            @Override
            public void signRequest(HttpURLConnection connection, CloudFileClient client, OperationContext context)
                    throws Exception {
                StorageRequest.signBlobQueueAndFileRequest(connection, client, -1L, context);
            }

            @Override
            public Void preProcessResponse(CloudFile file, CloudFileClient client, OperationContext context)
                    throws Exception {
                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_OK) {
                    this.setNonExceptionedRetryableFailure(true);
                    return null;
                }

                // Set attributes
                final FileAttributes retrievedAttributes = FileResponse.getFileAttributes(this.getConnection(),
                        file.getStorageUri());
                file.properties = retrievedAttributes.getProperties();
                file.metadata = retrievedAttributes.getMetadata();

                return null;
            }
        };

        return getRequest;
    }

    /**
     * Checks to see if the file exists.
     * 
     * @return <code>true</code> if the file exists, otherwise <code>false</code>.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public final boolean exists() throws StorageException {
        return this.exists(null /* accessCondition */, null /* options */, null /* opContext */);
    }

    /**
     * Checks to see if the file exists, using the specified access condition, request options and operation context.
     * 
     * @param accessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the file.
     * @param options
     *            A {@link FileRequestOptions} 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 CloudFileClient}).
     * @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 <code>true</code> if the file exists, otherwise <code>false</code>.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public final boolean exists(final AccessCondition accessCondition, FileRequestOptions options,
            OperationContext opContext) throws StorageException {
        return this.exists(false /* primaryOnly */, accessCondition, options, opContext);
    }

    @DoesServiceRequest
    private final boolean exists(final boolean primaryOnly, final AccessCondition accessCondition,
            FileRequestOptions options, OperationContext opContext) throws StorageException {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        opContext.initialize();
        options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);

        return ExecutionEngine.executeWithRetry(this.fileServiceClient, this,
                this.existsImpl(primaryOnly, accessCondition, options), options.getRetryPolicyFactory(), opContext);
    }

    private StorageRequest<CloudFileClient, CloudFile, Boolean> existsImpl(final boolean primaryOnly,
            final AccessCondition accessCondition, final FileRequestOptions options) {
        final StorageRequest<CloudFileClient, CloudFile, Boolean> getRequest =
                new StorageRequest<CloudFileClient, CloudFile, Boolean>(options, this.getStorageUri()) {
            @Override
            public void setRequestLocationMode() {
                this.setRequestLocationMode(primaryOnly ? RequestLocationMode.PRIMARY_ONLY
                        : RequestLocationMode.PRIMARY_OR_SECONDARY);
            }

            @Override
            public HttpURLConnection buildRequest(CloudFileClient client, CloudFile file, OperationContext context)
                    throws Exception {
                return FileRequest.getFileProperties(
                        file.getTransformedAddress(context).getUri(this.getCurrentLocation()),
                        options, context, accessCondition, file.getShare().snapshotID);
            }

            @Override
            public void signRequest(HttpURLConnection connection, CloudFileClient client, OperationContext context)
                    throws Exception {
                StorageRequest.signBlobQueueAndFileRequest(connection, client, -1L, context);
            }

            @Override
            public Boolean preProcessResponse(CloudFile file, CloudFileClient client, OperationContext context)
                    throws Exception {
                if (this.getResult().getStatusCode() == HttpURLConnection.HTTP_OK) {
                    final FileAttributes retrievedAttributes = FileResponse.getFileAttributes(this.getConnection(),
                            file.getStorageUri());
                    file.properties = retrievedAttributes.getProperties();
                    file.metadata = retrievedAttributes.getMetadata();
                    return Boolean.valueOf(true);
                }
                else if (this.getResult().getStatusCode() == HttpURLConnection.HTTP_NOT_FOUND) {
                    return Boolean.valueOf(false);
                }
                else {
                    this.setNonExceptionedRetryableFailure(true);

                    // return false instead of null to avoid SCA issues
                    return false;
                }
            }
        };

        return getRequest;
    }

    /**
     * Returns a shared access signature for the file using the specified group policy identifier and
     * shared access file headers. Note this does not contain the leading "?".
     *
     * @param policy
     *            A <code>{@link SharedAccessFilePolicy}</code> object that represents the access policy for the shared
     *            access signature.
     * @param groupPolicyIdentifier
     *            A <code>String</code> that represents the share-level access policy.
     *
     * @return A <code>String</code> that represents the shared access signature.
     *
     * @throws InvalidKeyException
     *             If the credentials are invalid.
     * @throws StorageException
     *             If a storage service error occurred.
     */
    public String generateSharedAccessSignature(final SharedAccessFilePolicy policy, final String groupPolicyIdentifier)
            throws InvalidKeyException, StorageException {
        return generateSharedAccessSignature(policy, null /* headers */, groupPolicyIdentifier);
    }
    
    /**
     * Returns a shared access signature for the file using the specified group policy identifier and
     * shared access file headers. Note this does not contain the leading "?".
     *
     * @param policy
     *            A <code>{@link SharedAccessFilePolicy}</code> object that represents the access policy for the shared
     *            access signature.
     * @param headers
     *            A <code>{@link SharedAccessFileHeaders}</code> object that represents the optional header values to
     *            set for a file accessed with this shared access signature.
     * @param groupPolicyIdentifier
     *            A <code>String</code> that represents the share-level access policy.
     *
     * @return A <code>String</code> that represents the shared access signature.
     *
     * @throws IllegalArgumentException
     *             If the credentials are invalid.
     * @throws InvalidKeyException
     *             If the credentials are invalid.
     * @throws StorageException
     *             If a storage service error occurred.
     */
    public String generateSharedAccessSignature(
            final SharedAccessFilePolicy policy, final SharedAccessFileHeaders headers, final String groupPolicyIdentifier)
            throws InvalidKeyException, StorageException {

        return this.generateSharedAccessSignature(policy, headers, groupPolicyIdentifier,
                null /* IP range */, null /* protocols */);
    }
    
    /**
     * Returns a shared access signature for the file using the specified group policy identifier and
     * shared access file headers. Note this does not contain the leading "?".
     *
     * @param policy
     *            A <code>{@link SharedAccessFilePolicy}</code> object that represents the access policy for the shared
     *            access signature.
     * @param headers
     *            A <code>{@link SharedAccessFileHeaders}</code> object that represents the optional header values to
     *            set for a file accessed with this shared access signature.
     * @param groupPolicyIdentifier
     *            A <code>String</code> that represents the share-level access policy.
     * @param ipRange
     *            A {@link IPRange} object containing the range of allowed IP addresses.
     * @param protocols
     *            A {@link SharedAccessProtocols} representing the allowed Internet protocols.
     *
     * @return A <code>String</code> that represents the shared access signature.
     *
     * @throws IllegalArgumentException
     *             If the credentials are invalid.
     * @throws InvalidKeyException
     *             If the credentials are invalid.
     * @throws StorageException
     *             If a storage service error occurred.
     */
    public String generateSharedAccessSignature(
            final SharedAccessFilePolicy policy, final SharedAccessFileHeaders headers,
            final String groupPolicyIdentifier, final IPRange ipRange, final SharedAccessProtocols protocols)
            throws InvalidKeyException, StorageException {

        if (!StorageCredentialsHelper.canCredentialsSignRequest(this.fileServiceClient.getCredentials())) {
            throw new IllegalArgumentException(SR.CANNOT_CREATE_SAS_WITHOUT_ACCOUNT_KEY);
        }

        final String signature = SharedAccessSignatureHelper.generateSharedAccessSignatureHashForBlobAndFile(
                policy, headers, groupPolicyIdentifier, this.getCanonicalName(),
                ipRange, protocols, this.fileServiceClient);

        final UriQueryBuilder builder = SharedAccessSignatureHelper.generateSharedAccessSignatureForBlobAndFile(
                policy, headers, groupPolicyIdentifier, "f", ipRange, protocols, signature);

        return builder.toString();
    }
    
    /**
     * Returns the canonical name of the file in the format of
     * <i>/&lt;service-name&gt;/&lt;account-name&gt;/&lt;share-name&gt;/&lt;file-name&gt;</i>.
     * <p>
     * This format is used for Shared Access operations.
     *
     * @return The canonical name in the format of <i>/&lt;service-name&gt;/&lt;account-name&gt;
     *         /&lt;share-name&gt;/&lt;file-name&gt;</i>.
     */
    String getCanonicalName() {
        StringBuilder canonicalName = new StringBuilder("/");
        canonicalName.append(SR.FILE);

        String rawPath = this.getUri().getRawPath();
        if (this.fileServiceClient.isUsePathStyleUris()) {
            canonicalName.append(rawPath);
        }
        else {
            canonicalName.append(PathUtility.getCanonicalPathFromCredentials(
                    this.getServiceClient().getCredentials(), rawPath));
        }
        
        return canonicalName.toString();
    }

    /**
     * Returns the Azure File's copy state.
     *
     * @return A {@link CopyState} object that represents the copy state of the file.
     */
    public CopyState getCopyState() {
        return this.properties.getCopyState();
    }

    /**
     * Opens a file input stream to download the file.
     * <p>
     * Use {@link CloudFile#setStreamMinimumReadSizeInBytes(int)} to configure the read size.
     * 
     * @return An <code>InputStream</code> object that represents the stream to use for reading from the file.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public final FileInputStream openRead() throws StorageException {
        return this.openRead(null /* accessCondition */, null /* options */, null /* opContext */);
    }

    /**
     * Opens a file input stream to download the file using the specified request options and
     * operation context.
     * <p>
     * Use {@link #setStreamMinimumReadSizeInBytes(int)} to configure the read size.
     * 
     * @param accessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the file.
     * @param options
     *            A {@link FileRequestOptions} 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 CloudFileClient}).
     * @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 An <code>InputStream</code> object that represents the stream to use for reading from the file.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public final FileInputStream openRead(final AccessCondition accessCondition, FileRequestOptions options,
            OperationContext opContext) throws StorageException {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient, false /* setStartTime */);

        return new FileInputStream(this, accessCondition, options, opContext);
    }

    /**
     * Opens an output stream object to write data to the file. The file must already exist and any existing data may 
     * be overwritten.
     * 
     * @return A {@link FileOutputStream} object used to write data to the file.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws URISyntaxException 
     */
    @DoesServiceRequest
    public FileOutputStream openWriteExisting() throws StorageException, URISyntaxException {
        return this
                .openOutputStreamInternal(null /* length */, null /* accessCondition */, null /* options */, null /* opContext */);
    }

    /**
     * Opens an output stream object to write data to the file, using specified request options and
     * operation context. The file must already exist and any existing data may be overwritten.
     * 
     * @param accessCondition
     *            An {@link AccessCondition} object which represents the access conditions for the file.
     * @param options
     *            A {@link FileRequestOptions} 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 CloudFileClient}).
     * @param opContext
     *            An {@link OperationContext} object which 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 FileOutputStream} object used to write data to the file.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws URISyntaxException 
     */
    @DoesServiceRequest
    public FileOutputStream openWriteExisting(AccessCondition accessCondition, FileRequestOptions options,
            OperationContext opContext) throws StorageException, URISyntaxException {
        return this.openOutputStreamInternal(null /* length */, accessCondition, options, opContext);
    }

    /**
     * Opens an output stream object to write data to the file. The file does not yet exist and will
     * be created with the length specified. If the file already exists on the service, it will be overwritten.
     * <p>
     * To avoid overwriting and instead throw an error, please use the
     * {@link #openWriteNew(long, AccessCondition, FileRequestOptions, OperationContext)} overload with the appropriate
     * {@link AccessCondition}.
     * 
     * @param length
     *            A <code>long</code> which represents the length, in bytes, of the stream to create.
     * 
     * @return A {@link FileOutputStream} object used to write data to the file.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws URISyntaxException 
     */
    @DoesServiceRequest
    public FileOutputStream openWriteNew(final long length) throws StorageException, URISyntaxException {
        return this
                .openOutputStreamInternal(length, null /* accessCondition */, null /* options */, null /* opContext */);
    }

    /**
     * Opens an output stream object to write data to the file, using the specified lease ID, request options and
     * operation context. The file does not need to yet exist and will be created with the length specified. If the file
     *  already exists on the service, it will be overwritten.
     * <p>
     * To avoid overwriting and instead throw an error, please pass in an {@link AccessCondition} generated using 
     * {@link AccessCondition#generateIfNotExistsCondition()}.
     * 
     * @param length
     *            A <code>long</code> which represents the length, in bytes, of the stream to create.
     * @param accessCondition
     *            An {@link AccessCondition} object which represents the access conditions for the file.
     * @param options
     *            A {@link FileRequestOptions} 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 CloudFileClient}).
     * @param opContext
     *            An {@link OperationContext} object which 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 FileOutputStream} object used to write data to the file.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws URISyntaxException 
     */
    @DoesServiceRequest
    public FileOutputStream openWriteNew(final long length, AccessCondition accessCondition,
            FileRequestOptions options, OperationContext opContext) throws StorageException, URISyntaxException {
        return openOutputStreamInternal(length, accessCondition, options, opContext);
    }

    /**
     * Opens an output stream object to write data to the file, using the specified lease ID, request options and
     * operation context. If the length is specified, a new file will be created with the length specified.
     * Otherwise, the file must already exist and a stream of its current length will be opened.
     * 
     * @param length
     *            A <code>long</code> which represents the length, in bytes, of the stream to create. This value must be
     *            null if the file already exists.
     * @param accessCondition
     *            An {@link AccessCondition} object which represents the access conditions for the file.
     * @param options
     *            A {@link FileRequestOptions} 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 CloudFileClient}).
     * @param opContext
     *            An {@link OperationContext} object which 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 FileOutputStream} object used to write data to the file.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws URISyntaxException 
     */
    private FileOutputStream openOutputStreamInternal(Long length, AccessCondition accessCondition,
            FileRequestOptions options, OperationContext opContext) throws StorageException, URISyntaxException {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        this.getShare().assertNoSnapshot();

        options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient, false /* setStartTime */);

        if (length != null) {
            this.create(length, accessCondition, options, opContext);
        }
        else {
            if (options.getStoreFileContentMD5()) {
                throw new IllegalArgumentException(SR.FILE_MD5_NOT_POSSIBLE);
            }

            this.downloadAttributes(accessCondition, options, opContext);
            length = this.getProperties().getLength();
        }

        if (accessCondition != null) {
            accessCondition = AccessCondition.generateLeaseCondition(accessCondition.getLeaseID());
        }

        return new FileOutputStream(this, length, accessCondition, options, opContext);
    }

    /**
     * Uploads a file from data in a byte array. If the file already exists on the service, it will be overwritten.
     * 
     * @param buffer
     *            A <code>byte</code> array which represents the data to write to the file.
     * @param offset
     *            A <code>int</code> which represents the offset of the byte array from which to start the data upload.
     * @param length
     *            An <code>int</code> which represents the number of bytes to upload from the input buffer.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws IOException
     * @throws URISyntaxException 
     */
    public void uploadFromByteArray(final byte[] buffer, final int offset, final int length) throws StorageException,
            IOException, URISyntaxException {
        uploadFromByteArray(buffer, offset, length, null /* accessCondition */, null /* options */, null /* opContext */);
    }

    /**
     * Uploads a file from data in a byte array. If the file already exists on the service, it will be overwritten.
     * 
     * @param buffer
     *            A <code>byte</code> array which represents the data to write to the file.
     * @param offset
     *            A <code>int</code> which represents the offset of the byte array from which to start the data upload.
     * @param length
     *            An <code>int</code> which represents the number of bytes to upload from the input buffer.
     * @param accessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the file.
     * @param options
     *            A {@link FileRequestOptions} 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 CloudFileClient}).
     * @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.
     * @throws IOException
     * @throws URISyntaxException 
     */
    public void uploadFromByteArray(final byte[] buffer, final int offset, final int length,
            final AccessCondition accessCondition, FileRequestOptions options, OperationContext opContext)
            throws StorageException, IOException, URISyntaxException {
        ByteArrayInputStream inputStream = new ByteArrayInputStream(buffer, offset, length);
        this.upload(inputStream, length, accessCondition, options, opContext);
        inputStream.close();
    }

    /**
     * Uploads a local file. If the file already exists on the service, it will be overwritten.
     * 
     * @param path
     *            A <code>String</code> which represents the path to the file to be uploaded.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws IOException
     * @throws URISyntaxException 
     */
    public void uploadFromFile(final String path) throws StorageException, IOException, URISyntaxException {
        uploadFromFile(path, null /* accessCondition */, null /* options */, null /* opContext */);
    }

    /**
     * Uploads a file from a local file. If the file already exists on the service, it will be overwritten.
     * 
     * @param path
     *            A <code>String</code> which represents the path to the file to be uploaded.
     * @param accessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the file.
     * @param options
     *            A {@link FileRequestOptions} 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 CloudFileClient}).
     * @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.
     * @throws IOException
     * @throws URISyntaxException 
     */
    public void uploadFromFile(final String path, final AccessCondition accessCondition, FileRequestOptions options,
            OperationContext opContext) throws StorageException, IOException, URISyntaxException {
        File file = new File(path);
        long fileLength = file.length();
        InputStream inputStream = new BufferedInputStream(new java.io.FileInputStream(file));
        this.upload(inputStream, fileLength, accessCondition, options, opContext);
        inputStream.close();
    }

    /**
     * Uploads a file from a string using the platform's default encoding. If the file already exists on the service, it
     * will be overwritten.
     * 
     * @param content
     *            A <code>String</code> which represents the content that will be uploaded to the file.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws IOException
     * @throws URISyntaxException 
     */
    public void uploadText(final String content) throws StorageException, IOException, URISyntaxException {
        this.uploadText(content, null /* charsetName */, null /* accessCondition */, null /* options */, null /* opContext */);
    }

    /**
     * Uploads a file from a string using the specified encoding. If the file already exists on the service, it will be 
     * overwritten.
     * 
     * @param content
     *            A <code>String</code> which represents the content that will be uploaded to the file.
     * @param charsetName
     *            A <code>String</code> which represents the name of the charset to use to encode the content.
     *            If null, the platform's default encoding is used.
     * @param accessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the file.
     * @param options
     *            A {@link FileRequestOptions} 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 CloudFileClient}).
     * @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.
     * @throws IOException
     * @throws URISyntaxException 
     */
    public void uploadText(final String content, final String charsetName, final AccessCondition accessCondition,
            FileRequestOptions options, OperationContext opContext) throws StorageException, IOException, URISyntaxException {
        byte[] bytes = (charsetName == null) ? content.getBytes() : content.getBytes(charsetName);
        this.uploadFromByteArray(bytes, 0, bytes.length, accessCondition, options, opContext);
    }

    /**
     * Uploads a range to a file. 
     * 
     * @param sourceStream
     *            An {@link InputStream} object which represents the input stream to write to the file.
     * @param offset
     *            A <code>long</code> which represents the offset, in number of bytes, at which to begin writing the
     *            data.
     * @param length
     *            A <code>long</code> which represents the length, in bytes, of the data to write.
     * 
     * @throws IOException
     *             If an I/O exception occurred.
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws URISyntaxException 
     */
    @DoesServiceRequest
    public void uploadRange(final InputStream sourceStream, final long offset, final long length)
            throws StorageException, IOException, URISyntaxException {
        this.uploadRange(sourceStream, offset, length, null /* accessCondition */, null /* options */, null /* opContext */);
    }

    /**
     * Uploads a range to a file using the specified lease ID, request options, and operation context.
     * 
     * @param sourceStream
     *            An {@link InputStream} object which represents the input stream to write to the file.
     * @param offset
     *            A <code>long</code> which represents the offset, in number of bytes, at which to begin writing the
     *            data.
     * @param length
     *            A <code>long</code> which represents the length, in bytes, of the data to write.
     * @param accessCondition
     *            An {@link AccessCondition} object which represents the access conditions for the file.
     * @param options
     *            A {@link FileRequestOptions} 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 CloudFileClient}).
     * @param opContext
     *            An {@link OperationContext} object which 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 IOException
     *             If an I/O exception occurred.
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws URISyntaxException 
     */
    @DoesServiceRequest
    public void uploadRange(final InputStream sourceStream, final long offset, final long length,
            final AccessCondition accessCondition, FileRequestOptions options, OperationContext opContext)
            throws StorageException, IOException, URISyntaxException {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        this.getShare().assertNoSnapshot();

        options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);

        final FileRange range = new FileRange(offset, offset + length - 1);
        final byte[] data = new byte[(int) length];
        String md5 = null;

        int count = 0;
        int total = 0;
        while (total < length) {
            count = sourceStream.read(data, total, (int) Math.min(length - total, Integer.MAX_VALUE));
            total += count;
        }

        if (options.getUseTransactionalContentMD5()) {
            try {
                final MessageDigest digest = MessageDigest.getInstance("MD5");
                digest.update(data, 0, data.length);
                md5 = Base64.encode(digest.digest());
            }
            catch (final NoSuchAlgorithmException e) {
                // This wont happen, throw fatal.
                throw Utility.generateNewUnexpectedStorageException(e);
            }
        }

        this.putRangeInternal(range, FileRangeOperationType.UPDATE, data, length, md5, accessCondition, options,
                opContext);
    }

    /**
     * Used for both uploadRange and clearRange.
     * 
     * @param range
     *            A {@link FileRange} object that specifies the file range.
     * @param operationType
     *            A {@link FileRangeOperationType} enumeration value that specifies the operation type.
     * @param data
     *            A <code>byte</code> array which represents the data to write.
     * @param length
     *            A <code>long</code> which represents the number of bytes to write.
     * @param md5
     *            A <code>String</code> which represents the MD5 hash for the data.
     * @param accessCondition
     *            An {@link AccessCondition} object which represents the access conditions for the file.
     * @param options
     *            A {@link FileRequestOptions} 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 CloudFileClient}).
     * @param opContext
     *            An {@link OperationContext} object which 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.
     */
    @DoesServiceRequest
    private void putRangeInternal(final FileRange range, final FileRangeOperationType operationType, final byte[] data,
            final long length, final String md5, final AccessCondition accessCondition,
            final FileRequestOptions options, final OperationContext opContext) throws StorageException {
        ExecutionEngine.executeWithRetry(this.fileServiceClient, this,
                putRangeImpl(range, operationType, data, length, md5, accessCondition, options, opContext),
                options.getRetryPolicyFactory(), opContext);
    }

    private StorageRequest<CloudFileClient, CloudFile, Void> putRangeImpl(final FileRange range,
            final FileRangeOperationType operationType, final byte[] data, final long length, final String md5,
            final AccessCondition accessCondition, final FileRequestOptions options, final OperationContext opContext) {
        final StorageRequest<CloudFileClient, CloudFile, Void> putRequest =
                new StorageRequest<CloudFileClient, CloudFile, Void>(options, this.getStorageUri()) {

            @Override
            public HttpURLConnection buildRequest(CloudFileClient client, CloudFile file, OperationContext context)
                    throws Exception {
                if (operationType == FileRangeOperationType.UPDATE) {
                    this.setSendStream(new ByteArrayInputStream(data));
                    this.setLength(length);
                }

                return FileRequest.putRange(file.getTransformedAddress(context).getUri(this.getCurrentLocation()),
                        options, opContext, accessCondition, range, operationType);
            }

            @Override
            public void setHeaders(HttpURLConnection connection, CloudFile file, OperationContext context) {
                if (operationType == FileRangeOperationType.UPDATE) {
                    if (options.getUseTransactionalContentMD5()) {
                        connection.setRequestProperty(Constants.HeaderConstants.CONTENT_MD5, md5);
                    }
                }
            }

            @Override
            public void signRequest(HttpURLConnection connection, CloudFileClient client, OperationContext context)
                    throws Exception {
                if (operationType == FileRangeOperationType.UPDATE) {
                    StorageRequest.signBlobQueueAndFileRequest(connection, client, length, context);
                }
                else {
                    StorageRequest.signBlobQueueAndFileRequest(connection, client, 0L, context);
                }
            }

            @Override
            public Void preProcessResponse(CloudFile file, CloudFileClient client, OperationContext context)
                    throws Exception {
                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_CREATED) {
                    this.setNonExceptionedRetryableFailure(true);
                    return null;
                }

                file.updateEtagAndLastModifiedFromResponse(this.getConnection());
                this.getResult().setRequestServiceEncrypted(BaseResponse.isServerRequestEncrypted(this.getConnection()));
                return null;
            }
        };

        return putRequest;
    }

    /**
     * Uploads the file's metadata to the storage service.
     * <p>
     * Use {@link CloudFile#downloadAttributes} to retrieve the latest values for the file's properties and metadata
     * from the Microsoft Azure storage service.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws URISyntaxException 
     */
    @DoesServiceRequest
    public final void uploadMetadata() throws StorageException, URISyntaxException {
        this.uploadMetadata(null /* accessCondition */, null /* options */, null /* opContext */);
    }

    /**
     * Uploads the file's metadata to the storage service using the access condition, request options, and operation
     * context.
     * <p>
     * Use {@link CloudFile#downloadAttributes} to retrieve the latest values for the file's properties and metadata
     * from the Microsoft Azure storage service.
     * 
     * @param accessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the file.
     * @param options
     *            A {@link FileRequestOptions} 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 CloudFileClient}).
     * @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.
     * @throws URISyntaxException 
     */
    @DoesServiceRequest
    public final void uploadMetadata(final AccessCondition accessCondition, FileRequestOptions options,
            OperationContext opContext) throws StorageException, URISyntaxException {

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

        this.getShare().assertNoSnapshot();

        opContext.initialize();
        options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);

        ExecutionEngine.executeWithRetry(this.fileServiceClient, this,
                this.uploadMetadataImpl(accessCondition, options), options.getRetryPolicyFactory(), opContext);
    }

    private StorageRequest<CloudFileClient, CloudFile, Void> uploadMetadataImpl(final AccessCondition accessCondition,
            final FileRequestOptions options) {
        final StorageRequest<CloudFileClient, CloudFile, Void> putRequest = new StorageRequest<CloudFileClient, CloudFile, Void>(
                options, this.getStorageUri()) {

            @Override
            public HttpURLConnection buildRequest(CloudFileClient client, CloudFile file, OperationContext context)
                    throws Exception {
                return FileRequest.setFileMetadata(
                        file.getTransformedAddress(context).getUri(this.getCurrentLocation()),
                        options, context, accessCondition);
            }

            @Override
            public void setHeaders(HttpURLConnection connection, CloudFile file, OperationContext context) {
                FileRequest.addMetadata(connection, file.metadata, context);
            }

            @Override
            public void signRequest(HttpURLConnection connection, CloudFileClient client, OperationContext context)
                    throws Exception {
                StorageRequest.signBlobQueueAndFileRequest(connection, client, 0L, context);
            }

            @Override
            public Void preProcessResponse(CloudFile file, CloudFileClient client, OperationContext context)
                    throws Exception {
                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_OK) {
                    this.setNonExceptionedRetryableFailure(true);
                    return null;
                }

                file.updateEtagAndLastModifiedFromResponse(this.getConnection());
                this.getResult().setRequestServiceEncrypted(BaseResponse.isServerRequestEncrypted(this.getConnection()));
                return null;
            }
        };

        return putRequest;
    }

    /**
     * Updates the file's properties to the storage service.
     * <p>
     * Use {@link CloudFile#downloadAttributes} to retrieve the latest values for the file's properties and metadata
     * from the Microsoft Azure storage service.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws URISyntaxException 
     */
    @DoesServiceRequest
    public final void uploadProperties() throws StorageException, URISyntaxException {
        this.uploadProperties(null /* accessCondition */, null /* options */, null /*opContext */);
    }

    /**
     * Updates the file's properties using the access condition, request options, and operation context.
     * <p>
     * Use {@link CloudFile#downloadAttributes} to retrieve the latest values for the file's properties and metadata
     * from the Microsoft Azure storage service.
     * 
     * @param accessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the file.
     * @param options
     *            A {@link FileRequestOptions} 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 CloudFileClient}).
     * @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.
     * @throws URISyntaxException 
     */
    @DoesServiceRequest
    public final void uploadProperties(final AccessCondition accessCondition, FileRequestOptions options,
            OperationContext opContext) throws StorageException, URISyntaxException {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        this.getShare().assertNoSnapshot();

        opContext.initialize();
        options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);

        ExecutionEngine.executeWithRetry(this.fileServiceClient, this,
                this.uploadPropertiesImpl(accessCondition, options), options.getRetryPolicyFactory(), opContext);
    }

    private StorageRequest<CloudFileClient, CloudFile, Void> uploadPropertiesImpl(
            final AccessCondition accessCondition, final FileRequestOptions options) {
        final StorageRequest<CloudFileClient, CloudFile, Void> putRequest =
                new StorageRequest<CloudFileClient, CloudFile, Void>(options, this.getStorageUri()) {

            @Override
            public HttpURLConnection buildRequest(CloudFileClient client, CloudFile file, OperationContext context)
                    throws Exception {
                return FileRequest.setFileProperties(
                        file.getTransformedAddress(context).getUri(this.getCurrentLocation()),
                        options, context, accessCondition, file.properties);
            }

            @Override
            public void signRequest(HttpURLConnection connection, CloudFileClient client, OperationContext context)
                    throws Exception {
                StorageRequest.signBlobQueueAndFileRequest(connection, client, 0L, context);

            }

            @Override
            public Void preProcessResponse(CloudFile file, CloudFileClient client, OperationContext context)
                    throws Exception {
                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_OK) {
                    this.setNonExceptionedRetryableFailure(true);
                    return null;
                }

                file.updateEtagAndLastModifiedFromResponse(this.getConnection());
                this.getResult().setRequestServiceEncrypted(BaseResponse.isServerRequestEncrypted(this.getConnection()));
                return null;
            }
        };

        return putRequest;
    }

    /**
     * Resizes the file to the specified size.
     * 
     * @param size
     *            A <code>long</code> which represents the size of the file, in bytes.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws URISyntaxException 
     */
    public void resize(long size) throws StorageException, URISyntaxException {
        this.resize(size, null /* accessCondition */, null /* options */, null /* operationContext */);
    }

    /**
     * Resizes the file to the specified size.
     * 
     * @param size
     *            A <code>long</code> which represents the size of the file, in bytes.
     * @param accessCondition
     *            An {@link AccessCondition} object which represents the access conditions for the file.
     * @param options
     *            A {@link FileRequestOptions} 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 CloudFileClient}).
     * @param opContext
     *            An {@link OperationContext} object which 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.
     * @throws URISyntaxException 
     */
    public void resize(long size, AccessCondition accessCondition, FileRequestOptions options,
            OperationContext opContext) throws StorageException, URISyntaxException {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        this.getShare().assertNoSnapshot();

        opContext.initialize();
        options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);

        ExecutionEngine.executeWithRetry(this.fileServiceClient, this, this.resizeImpl(size, accessCondition, options),
                options.getRetryPolicyFactory(), opContext);
    }

    private StorageRequest<CloudFileClient, CloudFile, Void> resizeImpl(final long size,
            final AccessCondition accessCondition, final FileRequestOptions options) {
        final StorageRequest<CloudFileClient, CloudFile, Void> putRequest =
                new StorageRequest<CloudFileClient, CloudFile, Void>(options, this.getStorageUri()) {

            @Override
            public HttpURLConnection buildRequest(CloudFileClient client, CloudFile file, OperationContext context)
                    throws Exception {
                return FileRequest.resize(file.getTransformedAddress(context).getUri(this.getCurrentLocation()),
                        options, context, accessCondition, size);
            }

            @Override
            public void signRequest(HttpURLConnection connection, CloudFileClient client, OperationContext context)
                    throws Exception {
                StorageRequest.signBlobQueueAndFileRequest(connection, client, 0L, context);
            }

            @Override
            public Void preProcessResponse(CloudFile file, CloudFileClient client, OperationContext context)
                    throws Exception {
                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_OK) {
                    this.setNonExceptionedRetryableFailure(true);
                    return null;
                }

                file.getProperties().setLength(size);
                file.updateEtagAndLastModifiedFromResponse(this.getConnection());
                this.getResult().setRequestServiceEncrypted(BaseResponse.isServerRequestEncrypted(this.getConnection()));
                return null;
            }
        };

        return putRequest;
    }

    /**
     * Uploads the source stream data to the file. If the file already exists on the service, it will be overwritten.
     * 
     * @param sourceStream
     *            An {@link InputStream} object to read from.
     * @param length
     *            A <code>long</code> which represents the length, in bytes, of the stream data. Must be non zero.
     * 
     * @throws IOException
     *             If an I/O exception occurred.
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws URISyntaxException 
     */
    @DoesServiceRequest
    public void upload(final InputStream sourceStream, final long length) throws StorageException, IOException, URISyntaxException {
        this.upload(sourceStream, length, null /* accessCondition */, null /* options */, null /* opContext */);
    }

    /**
     * Uploads the source stream data to the file using the specified access condition, request options, and operation
     * context. If the file already exists on the service, it will be overwritten.
     * 
     * @param sourceStream
     *            An {@link InputStream} object to read from.
     * @param length
     *            A <code>long</code> which represents the length, in bytes, of the stream data. This must be greater than
     *            or equal to zero.
     * @param accessCondition
     *            An {@link AccessCondition} object which represents the access conditions for the file.
     * @param options
     *            A {@link FileRequestOptions} 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 CloudFileClient}).
     * @param opContext
     *            An {@link OperationContext} object which 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 IOException
     *             If an I/O exception occurred.
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws URISyntaxException 
     */
    @DoesServiceRequest
    public void upload(final InputStream sourceStream, final long length, final AccessCondition accessCondition,
            FileRequestOptions options, OperationContext opContext) throws StorageException, IOException, URISyntaxException {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        this.getShare().assertNoSnapshot();

        options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);

        if (length < 0) {
            throw new IllegalArgumentException(SR.INVALID_FILE_LENGTH);
        }

        if (sourceStream.markSupported()) {
            // Mark sourceStream for current position.
            sourceStream.mark(Constants.MAX_MARK_LENGTH);
        }

        final FileOutputStream streamRef = this.openWriteNew(length, accessCondition, options, opContext);
        try {
            streamRef.write(sourceStream, length);
        }
        finally {
            streamRef.close();
        }
    }

    /**
     * Retrieves the parent name for a file URI.
     * 
     * @param resourceAddress
     *            A {@link StorageUri} object which represents the resource URI.
     * @param delimiter
     *            A <code>String</code> which specifies the directory delimiter to use.
     * @param usePathStyleUris
     *            A <code>Boolean</code> which specifies whether path style URIs are used.
     * @param share
     *            A {@link CloudFileShare} object which represents the file share.
     * 
     * @return A <code>String</code> which represents the parent address for a file URI.
     * 
     * @throws URISyntaxException
     */
    protected static String getParentNameFromURI(final StorageUri resourceAddress, final CloudFileShare share)
            throws URISyntaxException {
        Utility.assertNotNull("resourceAddress", resourceAddress);
        Utility.assertNotNull("share", share);

        String delimiter = "/";
        String shareName = share.getName() + delimiter;

        String relativeURIString = Utility.safeRelativize(share.getStorageUri().getPrimaryUri(),
                resourceAddress.getPrimaryUri());

        if (relativeURIString.endsWith(delimiter)) {
            relativeURIString = relativeURIString.substring(0, relativeURIString.length() - delimiter.length());
        }

        String parentName;

        if (Utility.isNullOrEmpty(relativeURIString)) {
            // Case 1 /<ShareName>[Delimiter]*? => /<ShareName>
            // Parent of share is share itself
            parentName = null;
        }
        else {
            final int lastDelimiterDex = relativeURIString.lastIndexOf(delimiter);

            if (lastDelimiterDex < 0) {
                // Case 2 /<Share>/<Directory>
                // Parent of a Directory is Share
                parentName = "";
            }
            else {
                // Case 3 /<Share>/<Directory>/[<subDirectory>/]*<FileName>
                // Parent of CloudFile is CloudFileDirectory
                parentName = relativeURIString.substring(0, lastDelimiterDex);
                if (parentName != null && parentName.equals(shareName)) {
                    parentName = "";
                }
            }
        }

        return parentName;
    }

    /**
     * Verifies the passed in URI. Then parses it and uses its components to populate this resource's properties.
     * 
     * @param completeUri
     *            A {@link StorageUri} object which represents the complete URI.
     * @param credentials
     *            A {@link StorageCredentials} object used to authenticate access.
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws URISyntaxException 
     */
    private void parseQueryAndVerify(final StorageUri completeUri, final StorageCredentials credentials)
            throws StorageException, URISyntaxException {
       Utility.assertNotNull("completeUri", completeUri);

        if (!completeUri.isAbsolute()) {
            throw new IllegalArgumentException(String.format(SR.RELATIVE_ADDRESS_NOT_PERMITTED, completeUri.toString()));
        }

        this.storageUri = PathUtility.stripURIQueryAndFragment(completeUri);
        
        final StorageCredentialsSharedAccessSignature parsedCredentials = 
                SharedAccessSignatureHelper.parseQuery(completeUri);

        if (credentials != null && parsedCredentials != null) {
            throw new IllegalArgumentException(SR.MULTIPLE_CREDENTIALS_PROVIDED);
        }

        try {
            final boolean usePathStyleUris = Utility.determinePathStyleFromUri(this.storageUri.getPrimaryUri());
            this.fileServiceClient = new CloudFileClient(PathUtility.getServiceClientBaseAddress(
                    this.getStorageUri(), usePathStyleUris), credentials != null ? credentials : parsedCredentials);
            this.name = PathUtility.getFileNameFromURI(this.storageUri.getPrimaryUri(), usePathStyleUris);
        }
        catch (final URISyntaxException e) {
            throw Utility.generateNewUnexpectedStorageException(e);
        }

        final HashMap<String, String[]> queryParameters = PathUtility.parseQueryString(completeUri.getQuery());

        final String[] snapshotIDs = queryParameters.get(Constants.QueryConstants.SHARE_SNAPSHOT);
        if (snapshotIDs != null && snapshotIDs.length > 0) {
            this.getShare().snapshotID = snapshotIDs[0];
        }
    }

    protected void updateEtagAndLastModifiedFromResponse(HttpURLConnection request) {
        // ETag
        this.getProperties().setEtag(request.getHeaderField(Constants.HeaderConstants.ETAG));

        // Last Modified
        if (0 != request.getLastModified()) {
            final Calendar lastModifiedCalendar = Calendar.getInstance(Utility.LOCALE_US);
            lastModifiedCalendar.setTimeZone(Utility.UTC_ZONE);
            lastModifiedCalendar.setTime(new Date(request.getLastModified()));
            this.getProperties().setLastModified(lastModifiedCalendar.getTime());
        }
    }

    protected void updateLengthFromResponse(HttpURLConnection request) {
        final String xContentLengthHeader = request.getHeaderField(FileConstants.CONTENT_LENGTH_HEADER);
        if (!Utility.isNullOrEmpty(xContentLengthHeader)) {
            this.getProperties().setLength(Long.parseLong(xContentLengthHeader));
        }
    }

    /**
     * Returns the file's share.
     * 
     * @return A {@link CloudFileShare} object that represents the share of the file.
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws URISyntaxException
     *             If the resource URI is invalid.
     */
    @Override
    public final CloudFileShare getShare() throws StorageException, URISyntaxException {
        if (this.share == null) {
            final StorageUri shareUri = PathUtility.getShareURI(this.getStorageUri(),
                    this.fileServiceClient.isUsePathStyleUris());
            this.share = new CloudFileShare(shareUri, this.fileServiceClient.getCredentials());
        }

        return this.share;
    }

    /**
     * Returns the metadata for the file.
     * 
     * @return A <code>java.util.HashMap</code> object that represents the metadata for the file.
     */
    public final HashMap<String, String> getMetadata() {
        return this.metadata;
    }

    /**
     * Returns the name of the file.
     * 
     * @return A <code>String</code> that represents the name of the file.
     */
    public final String getName() {
        return this.name;
    }

    /**
     * Returns the file item's parent.
     * 
     * @return A {@link CloudFileDirectory} object that represents the parent directory for the file.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws URISyntaxException
     *             If the resource URI is invalid.
     */
    @Override
    public final CloudFileDirectory getParent() throws URISyntaxException, StorageException {
        if (this.parent == null) {
            final String parentName = getParentNameFromURI(this.getStorageUri(), this.getShare());

            if (parentName != null) {
                StorageUri parentURI = PathUtility.appendPathToUri(this.share.getStorageUri(), parentName);
                this.parent = new CloudFileDirectory(parentURI, this.getServiceClient().getCredentials());
            }
        }
        return this.parent;
    }

    /**
     * Returns the file's properties.
     * 
     * @return A {@link FileProperties} object that represents the properties of the file.
     */
    public final FileProperties getProperties() {
        return this.properties;
    }

    /**
     * Returns the file service client associated with the file.
     * 
     * @return A {@link CloudFileClient} object that represents the client.
     */
    public final CloudFileClient getServiceClient() {
        return this.fileServiceClient;
    }

    /**
     * Gets the number of bytes to buffer when writing to a {@link FileOutputStream}.
     * 
     * @return
     *         A <code>int</code> which represents the number of bytes to buffer.
     */
    public final int getStreamWriteSizeInBytes() {
        return this.streamWriteSizeInBytes;
    }

    /**
     * Returns the minimum read size when using a {@link FileInputStream}.
     * 
     * @return A <code>int</code> which represents the minimum read size, in bytes, when using a {@link FileInputStream}
     *         object.
     */
    public final int getStreamMinimumReadSizeInBytes() {
        return this.streamMinimumReadSizeInBytes;
    }

    /**
     * Returns the list of URIs for all locations.
     * 
     * @return A {@link StorageUri} that represents the list of URIs for all locations.
     */
    @Override
    public final StorageUri getStorageUri() {
        return this.storageUri;
    }

    /**
     * Returns the URI for this file.
     * 
     * @return A <code>java.net.URI</code> object that represents the URI for the file.
     */
    @Override
    public final URI getUri() {
        return this.storageUri.getPrimaryUri();
    }

    /**
     * Sets the share for the file.
     * 
     * @param share
     *            A {@link CloudFileShare} object that represents the share being assigned to the file.
     */
    protected final void setShare(final CloudFileShare share) {
        this.share = share;
    }

    /**
     * Sets the metadata for the file.
     * 
     * @param metadata
     *            A <code>java.util.HashMap</code> object that contains the metadata being assigned to the file.
     */
    public final void setMetadata(final HashMap<String, String> metadata) {
        this.metadata = metadata;
    }

    /**
     * Sets the properties for the file.
     * 
     * @param properties
     *            A {@link FileProperties} object that represents the properties being assigned to the file.
     */
    protected final void setProperties(final FileProperties properties) {
        this.properties = properties;
    }

    /**
     * Sets the list of URIs for all locations.
     * 
     * @param storageUri
     *            A {@link StorageUri} that represents the list of URIs for all locations.
     */
    protected void setStorageUri(final StorageUri storageUri) {
        this.storageUri = storageUri;
    }

    /**
     * Sets the minimum read size when using a {@link FileInputStream}.
     * 
     * @param minimumReadSize
     *            An <code>int</code> that represents the minimum number of bytes to buffer when reading from
     *            a file while using a {@link FileInputStream} object. Must be greater than or equal to 16 KB.
     * @throws IllegalArgumentException
     *             If <code>minimumReadSize</code> is less than 16 KB.
     */
    public void setStreamMinimumReadSizeInBytes(final int minimumReadSize) {
        if (minimumReadSize < 16 * Constants.KB) {
            throw new IllegalArgumentException("MinimumReadSize");
        }

        this.streamMinimumReadSizeInBytes = minimumReadSize;
    }

    /**
     * Sets the number of bytes to buffer when writing to a {@link FileOutputStream}.
     * 
     * @param streamWriteSizeInBytes
     *            An <code>int</code> which represents the number of bytes to buffer while using a
     *            {@link FileOutputStream} object, ranging from 512 bytes to 4 MB, inclusive.
     * 
     * @throws IllegalArgumentException
     *             If <code>streamWriteSizeInBytes</code> is less than 512 bytes or greater than 4 MB.
     */
    public void setStreamWriteSizeInBytes(final int streamWriteSizeInBytes) {
        if (streamWriteSizeInBytes > Constants.MAX_BLOCK_SIZE || streamWriteSizeInBytes < Constants.PAGE_SIZE) {
            throw new IllegalArgumentException("StreamWriteSizeInBytes");
        }

        this.streamWriteSizeInBytes = streamWriteSizeInBytes;
    }

    /**
     * Returns the transformed URI for the resource if the given credentials require transformation.
     * 
     * @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 StorageUri} object that represents the transformed URI.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws URISyntaxException
     *             If the resource URI is invalid.
     */
    protected final StorageUri getTransformedAddress(final OperationContext opContext)
            throws URISyntaxException, StorageException {
        return this.fileServiceClient.getCredentials().transformUri(this.getStorageUri(), opContext);
    }
} public final class CloudFileShare {
    /**
     * Represents the share metadata.
     */
    private HashMap<String, String> metadata = new HashMap<String, String>();

    /**
     * Holds the share properties.
     */
    FileShareProperties properties = new FileShareProperties();

    /**
     * Holds the name of the share.
     */
    String name = null;

    /**
     * Holds the list of URIs for all locations.
     */
    private StorageUri storageUri;

    /**
     * Holds the snapshot ID.
     */
    String snapshotID;

    /**
     * Holds a reference to the associated service client.
     */
    private CloudFileClient fileServiceClient;

    /**
     * Creates an instance of the <code>CloudFileShare</code> class using the specified name and client.
     * 
     * @param shareName
     *            A <code>String</code> which represents the name of the share, which must adhere to share
     *            naming rules.
     *            The share name should not include any path separator characters (/).
     *            Share names must be lowercase, between 3-63 characters long and must start with a letter or
     *            number. Share names may contain only letters, numbers, and the dash (-) character.
     * @param client
     *            A {@link CloudFileClient} object that represents the associated service client, and that specifies the
     *            endpoint for the File service.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws URISyntaxException
     *             If the resource URI constructed based on the shareName is invalid.
     * 
     * @see <a href="http://msdn.microsoft.com/en-us/library/azure/dn167011.aspx">Naming and Referencing Shares,
     *      Directories, Files, and Metadata</a>
     */
    public CloudFileShare(final String shareName, String snapshotID, final CloudFileClient client) throws URISyntaxException,
            StorageException {
        Utility.assertNotNull("client", client);
        Utility.assertNotNull("shareName", shareName);

        this.storageUri = PathUtility.appendPathToUri(client.getStorageUri(), shareName);
        this.name = shareName;
        this.snapshotID = snapshotID;
        this.fileServiceClient = client;
    }
    
    /**
     * Creates an instance of the <code>CloudFileShare</code> class using the specified URI.
     * 
     * @param uri
     *            A <code>java.net.URI</code> object that represents the absolute URI of the share.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    public CloudFileShare(final URI uri) throws StorageException {
        this(new StorageUri(uri));
    }

    /**
     * Creates an instance of the <code>CloudFileShare</code> class using the specified URI.
     * 
     * @param storageUri
     *            A {@link StorageUri} object which represents the absolute URI of the share.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    public CloudFileShare(final StorageUri storageUri) throws StorageException {
        this(storageUri, (StorageCredentials) null);
    }
    
    /**
     * Creates an instance of the <code>CloudFileShare</code> class using the specified URI and credentials.
     * 
     * @param uri
     *            A <code>java.net.URI</code> object that represents the absolute URI of the share.
     * @param credentials
     *            A {@link StorageCredentials} object used to authenticate access.
     * @param snapshotID
     *            A <code>String</code> that represents the snapshot version, if applicable.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    public CloudFileShare(final URI uri, final StorageCredentials credentials) throws StorageException {
        this(new StorageUri(uri), credentials);
    }

    /**
     * Creates an instance of the <code>CloudFileShare</code> class using the specified StorageUri and credentials.
     * 
     * @param storageUri
     *            A {@link StorageUri} object which represents the absolute StorageUri of the share.
     * @param credentials
     *            A {@link StorageCredentials} object used to authenticate access.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    public CloudFileShare(final StorageUri storageUri, final StorageCredentials credentials) throws StorageException {
        this.parseQueryAndVerify(storageUri, credentials);
    }

    /**
     * Creates the share.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public void create() throws StorageException {
        this.create(null /* options */, null /* opContext */);
    }

    /**
     * Creates the share using the specified options and operation context.
     * 
     * @param options
     *            A {@link FileRequestOptions} 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 CloudFileClient}).
     * @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.
     */
    @DoesServiceRequest
    public void create(FileRequestOptions options, OperationContext opContext) throws StorageException {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        assertNoSnapshot();
        if (this.properties != null && this.properties.getShareQuota() != null) {
            Utility.assertInBounds("Share Quota", this.properties.getShareQuota(), 1, FileConstants.MAX_SHARE_QUOTA);
        }

        opContext.initialize();
        options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);

        ExecutionEngine.executeWithRetry(this.fileServiceClient, this, createImpl(options),
                options.getRetryPolicyFactory(), opContext);
    }

    private StorageRequest<CloudFileClient, CloudFileShare, Void> createImpl(final FileRequestOptions options) {
        final StorageRequest<CloudFileClient, CloudFileShare, Void> putRequest =
                new StorageRequest<CloudFileClient, CloudFileShare, Void>(options, this.getStorageUri()) {
            @Override
            public HttpURLConnection buildRequest(CloudFileClient client, CloudFileShare share, OperationContext context)
                    throws Exception {
                final HttpURLConnection request = FileRequest.createShare(
                        share.getTransformedAddress().getUri(this.getCurrentLocation()),
                        options, context, share.getProperties());
                return request;
            }

            @Override
            public void setHeaders(HttpURLConnection connection, CloudFileShare share, OperationContext context) {
                FileRequest.addMetadata(connection, share.metadata, context);
            }

            @Override
            public void signRequest(HttpURLConnection connection, CloudFileClient client, OperationContext context)
                    throws Exception {
                StorageRequest.signBlobQueueAndFileRequest(connection, client, 0L, context);
            }

            @Override
            public Void preProcessResponse(CloudFileShare share, CloudFileClient client, OperationContext context)
                    throws Exception {
                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_CREATED) {
                    this.setNonExceptionedRetryableFailure(true);
                    return null;
                }

                // Set attributes
                final FileShareAttributes attributes = FileResponse.getFileShareAttributes(this.getConnection(),
                        client.isUsePathStyleUris());
                
                // The response from the service does not include the share quota.
                // Instead, we keep the existing value.
                Integer oldShareQuota = share.properties.getShareQuota();
                share.properties = attributes.getProperties();
                share.properties.setShareQuota(oldShareQuota);
                return null;
            }
        };

        return putRequest;
    }

    /**
     * Creates the share if it does not exist.
     * 
     * @return <code>true</code> if the share did not already exist and was created; otherwise, <code>false</code>.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public boolean createIfNotExists() throws StorageException {
        return this.createIfNotExists(null /* options */, null /* opContext */);
    }

    /**
     * Creates the share if it does not exist, using the specified request options and operation context.
     * 
     * @param options
     *            A {@link FileRequestOptions} 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 CloudFileClient}).
     * @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 <code>true</code> if the share did not already exist and was created; otherwise, <code>false</code>.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public boolean createIfNotExists(FileRequestOptions options, OperationContext opContext) throws StorageException {
        options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);

        boolean exists = this.exists(true /* primaryOnly */, null /* accessCondition */, options, opContext);
        if (exists) {
            return false;
        }
        else {
            try {
                this.create(options, opContext);
                return true;
            }
            catch (StorageException e) {
                if (e.getHttpStatusCode() == HttpURLConnection.HTTP_CONFLICT
                        && StorageErrorCodeStrings.SHARE_ALREADY_EXISTS.equals(e.getErrorCode())) {
                    return false;
                }
                else {
                    throw e;
                }
            }
        }
    }

    /**
     * Deletes the share.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public void delete() throws StorageException {
        this.delete(null /* accessCondition */, null /* options */, null /* opContext */);
    }

    /**
     * Deletes the share using the specified request options and operation context.
     * 
     * @param accessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the share.
     * @param options
     *            A {@link FileRequestOptions} 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 CloudFileClient}).
     * @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.
     */
    @DoesServiceRequest
    public void delete(AccessCondition accessCondition, FileRequestOptions options, OperationContext opContext)
            throws StorageException {
        this.delete(DeleteShareSnapshotsOption.NONE, accessCondition, options, opContext);
    }

    /**
     * Deletes the share using the specified snapshot and request options, and operation context.
     * <p>
     * A share that has snapshots cannot be deleted unless the snapshots are also deleted. If a share has snapshots, use
     * the {@link DeleteShareSnapshotsOption#INCLUDE_SNAPSHOTS} value
     * in the <code>deleteSnapshotsOption</code> parameter to include the snapshots when deleting the base share.
     *
     * @param deleteSnapshotsOption
     *            A {@link DeleteShareSnapshotsOption} object that indicates whether to delete only snapshots, or the share
     *            and its snapshots.
     * @param accessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the share.
     * @param options
     *            A {@link FileRequestOptions} 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 CloudFileClient}).
     * @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.
     */
    @DoesServiceRequest
    public void delete(DeleteShareSnapshotsOption deleteSnapshotsOption, AccessCondition accessCondition, FileRequestOptions options, OperationContext opContext)
            throws StorageException {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        opContext.initialize();
        options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);

        ExecutionEngine.executeWithRetry(this.fileServiceClient, this, deleteImpl(deleteSnapshotsOption, accessCondition, options),
                options.getRetryPolicyFactory(), opContext);
    }

    private StorageRequest<CloudFileClient, CloudFileShare, Void> deleteImpl(
            final DeleteShareSnapshotsOption deleteSnapshotsOption, final AccessCondition accessCondition, final FileRequestOptions options) {
        
        final StorageRequest<CloudFileClient, CloudFileShare, Void> putRequest =
                new StorageRequest<CloudFileClient, CloudFileShare, Void>(options, this.getStorageUri()) {
            
            @Override
            public HttpURLConnection buildRequest(
                    CloudFileClient client, CloudFileShare share, OperationContext context) throws Exception {
                return FileRequest.deleteShare(
                        share.getTransformedAddress().getPrimaryUri(), options, context, accessCondition, share.snapshotID, deleteSnapshotsOption);
            }

            @Override
            public void signRequest(HttpURLConnection connection, CloudFileClient client, OperationContext context)
                    throws Exception {
                StorageRequest.signBlobQueueAndFileRequest(connection, client, -1L, context);
            }

            @Override
            public Void preProcessResponse(CloudFileShare share, CloudFileClient client, OperationContext context)
                    throws Exception {
                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_ACCEPTED) {
                    this.setNonExceptionedRetryableFailure(true);
                }

                return null;
            }
        };

        return putRequest;
    }

    /**
     * Deletes the share if it exists.
     * 
     * @return <code>true</code> if the share existed and was deleted; otherwise, <code>false</code>.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public boolean deleteIfExists() throws StorageException {
        return this.deleteIfExists(null /* accessCondition */, null /* options */, null /* opContext */);
    }

    /**
     * Deletes the share if it exists using the specified request options and operation context.
     * 
     * @param accessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the share.
     * @param options
     *            A {@link FileRequestOptions} 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 CloudFileClient}).
     * @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 <code>true</code> if the share existed and was deleted; otherwise, <code>false</code>.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public boolean deleteIfExists(AccessCondition accessCondition, FileRequestOptions options,
            OperationContext opContext) throws StorageException {
        return this.deleteIfExists(DeleteShareSnapshotsOption.NONE, accessCondition, options, opContext);
        
    }

    /**
     * Deletes the share if it exists, using the specified snapshot and request options, and operation context.
     * <p>
     * A share that has snapshots cannot be deleted unless the snapshots are also deleted. If a share has snapshots, use
     * the {@link DeleteShareSnapshotsOption#INCLUDE_SNAPSHOTS} value
     * in the <code>deleteSnapshotsOption</code> parameter to include the snapshots when deleting the base share.
     *
     * @param deleteSnapshotsOption
     *            A {@link DeleteShareSnapshotsOption} object that indicates whether to delete only snapshots, or the share
     *            and its snapshots.
     * @param accessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the share.
     * @param options
     *            A {@link FileRequestOptions} 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 CloudFileClient}).
     * @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 <code>true</code> if the share existed and was deleted; otherwise, <code>false</code>.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public boolean deleteIfExists(DeleteShareSnapshotsOption deleteSnapshotsOption, AccessCondition accessCondition, FileRequestOptions options,
            OperationContext opContext) throws StorageException {
        options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);

        boolean exists = this.exists(true /* primaryOnly */, accessCondition, options, opContext);
        if (exists) {
            try {
                this.delete(deleteSnapshotsOption, accessCondition, options, opContext);
                return true;
            }
            catch (StorageException e) {
                if (e.getHttpStatusCode() == HttpURLConnection.HTTP_NOT_FOUND
                        && StorageErrorCodeStrings.SHARE_NOT_FOUND.equals(e.getErrorCode())) {
                    return false;
                }
                else {
                    throw e;
                }
            }
        }
        else {
            return false;
        }
    }

    /**
     * Downloads the share's attributes, which consist of metadata and properties.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public void downloadAttributes() throws StorageException {
        this.downloadAttributes(null /* accessCondition */, null /* options */, null /* opContext */);
    }

    /**
     * Downloads the share's attributes, which consist of metadata and properties, using the specified request
     * options and operation context.
     * 
     * @param accessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the share.
     * @param options
     *            A {@link FileRequestOptions} 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 CloudFileClient}).
     * @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.
     */
    @DoesServiceRequest
    public void downloadAttributes(AccessCondition accessCondition, FileRequestOptions options,
            OperationContext opContext) throws StorageException {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        opContext.initialize();
        options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);

        ExecutionEngine.executeWithRetry(this.fileServiceClient, this,
                this.downloadAttributesImpl(accessCondition, options), options.getRetryPolicyFactory(), opContext);
    }

    private StorageRequest<CloudFileClient, CloudFileShare, Void> downloadAttributesImpl(
            final AccessCondition accessCondition, final FileRequestOptions options) {
        final StorageRequest<CloudFileClient, CloudFileShare, Void> getRequest = new StorageRequest<CloudFileClient, CloudFileShare, Void>(
                options, this.getStorageUri()) {

            @Override
            public void setRequestLocationMode() {
                this.setRequestLocationMode(RequestLocationMode.PRIMARY_OR_SECONDARY);
            }

            @Override
            public HttpURLConnection buildRequest(CloudFileClient client, CloudFileShare share, OperationContext context)
                    throws Exception {
                return FileRequest.getShareProperties(share.getTransformedAddress().getUri(this.getCurrentLocation()),
                        options, context, accessCondition, share.snapshotID);
            }

            @Override
            public void signRequest(HttpURLConnection connection, CloudFileClient client, OperationContext context)
                    throws Exception {
                StorageRequest.signBlobQueueAndFileRequest(connection, client, -1L, context);
            }

            @Override
            public Void preProcessResponse(CloudFileShare share, CloudFileClient client, OperationContext context)
                    throws Exception {
                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_OK) {
                    this.setNonExceptionedRetryableFailure(true);
                    return null;
                }

                // Set attributes
                final FileShareAttributes attributes = FileResponse.getFileShareAttributes(this.getConnection(),
                        client.isUsePathStyleUris());
                share.metadata = attributes.getMetadata();
                share.properties = attributes.getProperties();
                return null;
            }
        };

        return getRequest;
    }

    /**
     * Downloads the permission settings for the share.
     * 
     * @return A {@link FileSharePermissions} object that represents the share's permissions.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public FileSharePermissions downloadPermissions() throws StorageException {
        return this.downloadPermissions(null /* accessCondition */, null /* options */, null /* opContext */);
    }

    /**
     * Downloads the permissions settings for the share using the specified request options and operation context.
     * 
     * @param accessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the share.
     * @param options
     *            A {@link FileRequestOptions} 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 CloudFileClient}).
     * @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 FileSharePermissions} object that represents the share's permissions.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public FileSharePermissions downloadPermissions(AccessCondition accessCondition, FileRequestOptions options,
            OperationContext opContext) throws StorageException {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        assertNoSnapshot();

        opContext.initialize();
        options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);

        return ExecutionEngine.executeWithRetry(this.fileServiceClient, this,
                downloadPermissionsImpl(accessCondition, options), options.getRetryPolicyFactory(), opContext);
    }

    private StorageRequest<CloudFileClient, CloudFileShare, FileSharePermissions> downloadPermissionsImpl(
            final AccessCondition accessCondition, final FileRequestOptions options) {
        final StorageRequest<CloudFileClient, CloudFileShare, FileSharePermissions> getRequest =
                new StorageRequest<CloudFileClient, CloudFileShare, FileSharePermissions>(options, this.getStorageUri()) {

            @Override
            public void setRequestLocationMode() {
                this.setRequestLocationMode(RequestLocationMode.PRIMARY_OR_SECONDARY);
            }

            @Override
            public HttpURLConnection buildRequest(CloudFileClient client, CloudFileShare share,
                    OperationContext context) throws Exception {
                return FileRequest.getAcl(share.getTransformedAddress().getUri(this.getCurrentLocation()), options,
                        accessCondition, context);
            }

            @Override
            public void signRequest(HttpURLConnection connection, CloudFileClient client, OperationContext context)
                    throws Exception {
                StorageRequest.signBlobQueueAndFileRequest(connection, client, -1L, context);
            }

            @Override
            public FileSharePermissions preProcessResponse(CloudFileShare share, CloudFileClient client,
                    OperationContext context) throws Exception {
                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_OK) {
                    this.setNonExceptionedRetryableFailure(true);
                }

                share.updatePropertiesFromResponse(this.getConnection());
                return new FileSharePermissions();
            }

            @Override
            public FileSharePermissions postProcessResponse(HttpURLConnection connection,
                    CloudFileShare share, CloudFileClient client, OperationContext context,
                    FileSharePermissions shareAcl) throws Exception {
                HashMap<String, SharedAccessFilePolicy> accessIds = SharedAccessPolicyHandler.getAccessIdentifiers(
                        this.getConnection().getInputStream(), SharedAccessFilePolicy.class);

                for (final String key : accessIds.keySet()) {
                    shareAcl.getSharedAccessPolicies().put(key, accessIds.get(key));
                }

                return shareAcl;
            }
        };

        return getRequest;
    }

    /**
     * Creates a snapshot of the share.
     *
     * @return A <code>CloudFileShare</code> object that represents the snapshot of the share.
     *
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public final CloudFileShare createSnapshot() throws StorageException {
        return this
                .createSnapshot(null /* metadata */, null /* accessCondition */, null /* options */, null /* opContext */);
    }

    /**
     * Creates a snapshot of the file share using the specified request options and operation context.
     *
     * @param accessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the share.
     * @param options
     *            A {@link FileRequestOptions} 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 CloudFileClient}).
     * @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 <code>CloudFileShare</code> object that represents the snapshot of the file share.
     *
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public final CloudFileShare createSnapshot(final AccessCondition accessCondition, FileRequestOptions options,
            OperationContext opContext) throws StorageException {
        return this.createSnapshot(null /* metadata */, accessCondition, options, opContext);
    }

    /**
     * Creates a snapshot of the file share using the specified request options and operation context.
     *
     * @param metadata
     *            A collection of name-value pairs defining the metadata of the snapshot, or null.
     * @param accessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the file share.
     * @param options
     *            A {@link FileRequestOptions} 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 CloudFileClient}).
     * @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 <code>CloudFileShare</code> object that represents the snapshot of the file share.
     *
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public final CloudFileShare createSnapshot(final HashMap<String, String> metadata,
            final AccessCondition accessCondition, FileRequestOptions options, OperationContext opContext)
            throws StorageException {
        assertNoSnapshot();

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

        opContext.initialize();
        options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);

        return ExecutionEngine
                .executeWithRetry(this.fileServiceClient, this,
                        this.createSnapshotImpl(metadata, accessCondition, options), options.getRetryPolicyFactory(),
                        opContext);
    }

    private StorageRequest<CloudFileClient, CloudFileShare, CloudFileShare> createSnapshotImpl(
            final HashMap<String, String> metadata, final AccessCondition accessCondition,
            final FileRequestOptions options) {
        final StorageRequest<CloudFileClient, CloudFileShare, CloudFileShare> putRequest =
                new StorageRequest<CloudFileClient, CloudFileShare, CloudFileShare>(
                options, this.getStorageUri()) {

            @Override
            public HttpURLConnection buildRequest(CloudFileClient client, CloudFileShare share, OperationContext context)
                    throws Exception {
                return FileRequest.snapshotShare(share.getTransformedAddress().getUri(this.getCurrentLocation()),
                        options, context, accessCondition);
            }

            @Override
            public void setHeaders(HttpURLConnection connection, CloudFileShare share, OperationContext context) {
                if (metadata != null) {
                    FileRequest.addMetadata(connection, metadata, context);
                }
            }

            @Override
            public void signRequest(HttpURLConnection connection, CloudFileClient client, OperationContext context)
                    throws Exception {
                StorageRequest.signBlobQueueAndFileRequest(connection, client, 0L, context);
            }

            @Override
            public CloudFileShare preProcessResponse(CloudFileShare share, CloudFileClient client, OperationContext context)
                    throws Exception {
                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_CREATED) {
                    this.setNonExceptionedRetryableFailure(true);
                    return null;
                }

                final String snapshotTime = FileResponse.getSnapshotTime(this.getConnection());
                CloudFileShare snapshot = new CloudFileShare(share.getName(), snapshotTime, client);
                snapshot.setProperties(new FileShareProperties(share.properties));

                // use the specified metadata if not null : otherwise share's metadata
                snapshot.setMetadata(metadata != null ? metadata : share.metadata);

                snapshot.updatePropertiesFromResponse(this.getConnection());

                return snapshot;
            }
        };

        return putRequest;
    }

    /**
     * Queries the service for this share's {@link ShareStats}.
     * 
     * @return A {@link ShareStats} object for the given storage service.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public ShareStats getStats() throws StorageException {
        return getStats(null /* options */, null /* context */);
    }

    /**
     * Queries the service for this share's {@link ShareStats}.
     * 
     * @param options
     *            A {@link FileRequestOptions} 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 CloudFileClient}).
     * @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 ShareStats} object for the given storage service.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public ShareStats getStats(FileRequestOptions options, OperationContext opContext) throws StorageException {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        assertNoSnapshot();

        opContext.initialize();
        options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);

        return ExecutionEngine.executeWithRetry(this.fileServiceClient, this,
                getStatsImpl(options), options.getRetryPolicyFactory(), opContext);
    }

    private StorageRequest<CloudFileClient, CloudFileShare, ShareStats> getStatsImpl(final FileRequestOptions options) {
        final StorageRequest<CloudFileClient, CloudFileShare, ShareStats> getRequest =
                new StorageRequest<CloudFileClient, CloudFileShare, ShareStats>(options, this.getStorageUri()) {

            @Override
            public void setRequestLocationMode() {
                this.setRequestLocationMode(RequestLocationMode.PRIMARY_OR_SECONDARY);
            }

            @Override
            public HttpURLConnection buildRequest(CloudFileClient client, CloudFileShare share, OperationContext context)
                    throws Exception {
                return FileRequest.getShareStats(share.getTransformedAddress().getUri(this.getCurrentLocation()),
                        options, context);
            }

            @Override
            public void signRequest(HttpURLConnection connection, CloudFileClient client, OperationContext context)
                    throws Exception {
                StorageRequest.signBlobQueueAndFileRequest(connection, client, -1L, context);
            }

            @Override
            public ShareStats preProcessResponse(CloudFileShare share, CloudFileClient client, OperationContext context)
                    throws Exception {
                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_OK) {
                    this.setNonExceptionedRetryableFailure(true);
                    return null;
                }

                share.updatePropertiesFromResponse(this.getConnection());
                return null;
            }

            @Override
            public ShareStats postProcessResponse(HttpURLConnection connection, CloudFileShare share,
                    CloudFileClient client, OperationContext context, ShareStats shareStats) throws Exception {
                return ShareStatsHandler.readShareStatsFromStream(connection.getInputStream());
            }
        };

        return getRequest;
    }

    /**
     * Returns a value that indicates whether the share exists.
     * 
     * @return <code>true</code> if the share exists, otherwise <code>false</code>.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public boolean exists() throws StorageException {
        return this.exists(null /* accessCondition */, null /* options */, null /* opContext */);
    }

    /**
     * Returns a value that indicates whether the share exists, using the specified request options and operation
     * context.
     * 
     * @param accessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the share.
     * @param options
     *            A {@link FileRequestOptions} 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 CloudFileClient}).
     * @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 <code>true</code> if the share exists, otherwise <code>false</code>.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public boolean exists(final AccessCondition accessCondition, FileRequestOptions options, OperationContext opContext)
            throws StorageException {
        return this.exists(false, accessCondition, options, opContext);
    }

    @DoesServiceRequest
    private boolean exists(final boolean primaryOnly, final AccessCondition accessCondition,
            FileRequestOptions options, OperationContext opContext) throws StorageException {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        opContext.initialize();
        options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);

        return ExecutionEngine.executeWithRetry(this.fileServiceClient, this,
                this.existsImpl(primaryOnly, accessCondition, options), options.getRetryPolicyFactory(), opContext);
    }

    private StorageRequest<CloudFileClient, CloudFileShare, Boolean> existsImpl(final boolean primaryOnly,
            final AccessCondition accessCondition, final FileRequestOptions options) {
        final StorageRequest<CloudFileClient, CloudFileShare, Boolean> getRequest = new StorageRequest<CloudFileClient, CloudFileShare, Boolean>(
                options, this.getStorageUri()) {

            @Override
            public void setRequestLocationMode() {
                this.setRequestLocationMode(
                        primaryOnly ? RequestLocationMode.PRIMARY_ONLY : RequestLocationMode.PRIMARY_OR_SECONDARY);
            }

            @Override
            public HttpURLConnection buildRequest(CloudFileClient client, CloudFileShare share, OperationContext context)
                    throws Exception {
                return FileRequest.getShareProperties(share.getTransformedAddress().getUri(this.getCurrentLocation()),
                        options, context, accessCondition, share.snapshotID);
            }

            @Override
            public void signRequest(HttpURLConnection connection, CloudFileClient client, OperationContext context)
                    throws Exception {
                StorageRequest.signBlobQueueAndFileRequest(connection, client, -1L, context);
            }

            @Override
            public Boolean preProcessResponse(CloudFileShare share, CloudFileClient client, OperationContext context)
                    throws Exception {
                if (this.getResult().getStatusCode() == HttpURLConnection.HTTP_OK) {
                    share.updatePropertiesFromResponse(this.getConnection());
                    final FileShareAttributes attributes = FileResponse.getFileShareAttributes(this.getConnection(),
                            client.isUsePathStyleUris());
                    share.metadata = attributes.getMetadata();
                    share.properties = attributes.getProperties();

                    return Boolean.valueOf(true);
                }
                else if (this.getResult().getStatusCode() == HttpURLConnection.HTTP_NOT_FOUND) {
                    return Boolean.valueOf(false);
                }
                else {
                    this.setNonExceptionedRetryableFailure(true);
                    // return false instead of null to avoid SCA issues
                    return false;
                }
            }
        };

        return getRequest;
    }

    private void updatePropertiesFromResponse(HttpURLConnection request) {
        // ETag
        this.getProperties().setEtag(request.getHeaderField(Constants.HeaderConstants.ETAG));

        // Last Modified
        if (0 != request.getLastModified()) {
            final Calendar lastModifiedCalendar = Calendar.getInstance(Utility.LOCALE_US);
            lastModifiedCalendar.setTimeZone(Utility.UTC_ZONE);
            lastModifiedCalendar.setTime(new Date(request.getLastModified()));
            this.getProperties().setLastModified(lastModifiedCalendar.getTime());
        }
    }

    /**
     * Asserts that the share is not a snapshot.
     */
    protected void assertNoSnapshot() {
        if (isSnapshot()) {
            throw new IllegalArgumentException(SR.INVALID_OPERATION_FOR_A_SHARE_SNAPSHOT);
        }
    }

    /**
     * Returns a shared access signature for the share. Note this does not contain the leading "?".
     * 
     * @param policy
     *            An {@link SharedAccessFilePolicy} object that represents the access policy for the
     *            shared access signature.
     * @param groupPolicyIdentifier
     *            A <code>String</code> which represents the share-level access policy.
     * 
     * @return A <code>String</code> which represents a shared access signature for the share.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws InvalidKeyException
     *             If the key is invalid.
     */
    public String generateSharedAccessSignature(final SharedAccessFilePolicy policy, final String groupPolicyIdentifier)
            throws InvalidKeyException, StorageException {

        return this.generateSharedAccessSignature(policy, groupPolicyIdentifier, null /* IP range */, null /* protocols */);
    }

    /**
     * Returns a shared access signature for the share. Note this does not contain the leading "?".
     * 
     * @param policy
     *            An {@link SharedAccessFilePolicy} object that represents the access policy for the
     *            shared access signature.
     * @param groupPolicyIdentifier
     *            A <code>String</code> which represents the share-level access policy.
     * @param ipRange
     *            A {@link IPRange} object containing the range of allowed IP addresses.
     * @param protocols
     *            A {@link SharedAccessProtocols} representing the allowed Internet protocols.
     * 
     * @return A <code>String</code> which represents a shared access signature for the share.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws InvalidKeyException
     *             If the key is invalid.
     */
    public String generateSharedAccessSignature(
            final SharedAccessFilePolicy policy, final String groupPolicyIdentifier, final IPRange ipRange,
            final SharedAccessProtocols protocols)
            throws InvalidKeyException, StorageException {

        if (!StorageCredentialsHelper.canCredentialsSignRequest(this.fileServiceClient.getCredentials())) {
            final String errorMessage = SR.CANNOT_CREATE_SAS_WITHOUT_ACCOUNT_KEY;
            throw new IllegalArgumentException(errorMessage);
        }

        final String resourceName = this.getSharedAccessCanonicalName();

        final String signature = SharedAccessSignatureHelper.generateSharedAccessSignatureHashForBlobAndFile(
                policy, null /* SharedAccessHeaders */, groupPolicyIdentifier, resourceName,
                ipRange, protocols, this.fileServiceClient);

        final UriQueryBuilder builder = SharedAccessSignatureHelper.generateSharedAccessSignatureForBlobAndFile(
                policy, null /* SharedAccessHeaders */, groupPolicyIdentifier, "s", ipRange, protocols, signature);

        return builder.toString();
    }

    /**
     * Returns the canonical name for shared access.
     * 
     * @return the canonical name for shared access.
     */
    private String getSharedAccessCanonicalName() {
        String accountName = this.getServiceClient().getCredentials().getAccountName();
        String shareName = this.getName();

        return String.format("/%s/%s/%s", SR.FILE, accountName, shareName);
    }

    /**
     * Uploads the share's metadata.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public void uploadMetadata() throws StorageException {
        this.uploadMetadata(null /* accessCondition */, null /* options */, null /* opContext */);
    }

    /**
     * Uploads the share's metadata using the specified request options and operation context.
     * 
     * @param accessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the share.
     * @param options
     *            A {@link FileRequestOptions} 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 CloudFileClient}).
     * @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.
     */
    @DoesServiceRequest
    public void uploadMetadata(AccessCondition accessCondition, FileRequestOptions options, OperationContext opContext)
            throws StorageException {
        assertNoSnapshot();

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

        opContext.initialize();
        options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);

        ExecutionEngine.executeWithRetry(this.fileServiceClient, this,
                this.uploadMetadataImpl(accessCondition, options), options.getRetryPolicyFactory(), opContext);
    }

    private StorageRequest<CloudFileClient, CloudFileShare, Void> uploadMetadataImpl(
            final AccessCondition accessCondition, final FileRequestOptions options) {
        final StorageRequest<CloudFileClient, CloudFileShare, Void> putRequest = new StorageRequest<CloudFileClient, CloudFileShare, Void>(
                options, this.getStorageUri()) {

            @Override
            public HttpURLConnection buildRequest(CloudFileClient client, CloudFileShare share, OperationContext context)
                    throws Exception {
                return FileRequest.setShareMetadata(share.getTransformedAddress().getUri(this.getCurrentLocation()),
                        options, context, accessCondition);
            }

            @Override
            public void setHeaders(HttpURLConnection connection, CloudFileShare share, OperationContext context) {
                FileRequest.addMetadata(connection, share.metadata, context);
            }

            @Override
            public void signRequest(HttpURLConnection connection, CloudFileClient client, OperationContext context)
                    throws Exception {
                StorageRequest.signBlobQueueAndFileRequest(connection, client, 0L, context);
            }

            @Override
            public Void preProcessResponse(CloudFileShare share, CloudFileClient client, OperationContext context)
                    throws Exception {
                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_OK) {
                    this.setNonExceptionedRetryableFailure(true);
                    return null;
                }

                share.updatePropertiesFromResponse(this.getConnection());
                return null;
            }
        };

        return putRequest;
    }

    /**
     * Updates the share's properties on the storage service.
     * <p>
     * Use {@link CloudFileShare#downloadAttributes} to retrieve the latest values for
     * the share's properties and metadata from the Microsoft Azure storage service.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public final void uploadProperties() throws StorageException {
        this.uploadProperties(null /* accessCondition */, null /* options */, null /*opContext */);
    }

    /**
     * Updates the share's properties using the request options, and operation context.
     * <p>
     * Use {@link CloudFileShare#downloadAttributes} to retrieve the latest values for
     * the share's properties and metadata from the Microsoft Azure storage service.
     * 
     * @param accessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the share.
     * @param options
     *            A {@link FileRequestOptions} 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 CloudFileClient}).
     * @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.
     */
    @DoesServiceRequest
    public final void uploadProperties(
            AccessCondition accessCondition, FileRequestOptions options, OperationContext opContext)
            throws StorageException {
        assertNoSnapshot();

        if (this.properties != null && this.properties.getShareQuota() != null) {
            Utility.assertInBounds("Share Quota", this.properties.getShareQuota(), 1, FileConstants.MAX_SHARE_QUOTA);
        }

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

        opContext.initialize();
        options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);

        ExecutionEngine.executeWithRetry(this.fileServiceClient, this,
                this.uploadPropertiesImpl(accessCondition, options), options.getRetryPolicyFactory(), opContext);
    }

    private StorageRequest<CloudFileClient, CloudFileShare, Void> uploadPropertiesImpl(
            final AccessCondition accessCondition, final FileRequestOptions options) {
        final StorageRequest<CloudFileClient, CloudFileShare, Void> putRequest =
                new StorageRequest<CloudFileClient, CloudFileShare, Void>(options, this.getStorageUri()) {

            @Override
            public HttpURLConnection buildRequest(CloudFileClient client, CloudFileShare share, OperationContext context)
                    throws Exception {
                return FileRequest.setShareProperties(share.getTransformedAddress().getUri(this.getCurrentLocation()),
                        options, context, accessCondition, share.properties);
            }

            @Override
            public void signRequest(HttpURLConnection connection, CloudFileClient client, OperationContext context)
                    throws Exception {
                StorageRequest.signBlobQueueAndFileRequest(connection, client, 0L, context);
            }

            @Override
            public Void preProcessResponse(CloudFileShare share, CloudFileClient client, OperationContext context)
                    throws Exception {
                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_OK) {
                    this.setNonExceptionedRetryableFailure(true);
                    return null;
                }

                share.updatePropertiesFromResponse(this.getConnection());
                return null;
            }
        };

        return putRequest;
    }
    
    /**
     * Uploads the share's permissions.
     * 
     * @param permissions
     *            A {@link FileSharePermissions} object that represents the permissions to upload.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public void uploadPermissions(final FileSharePermissions permissions) throws StorageException {
        this.uploadPermissions(permissions, null /* accessCondition */, null /* options */, null /* opContext */);
    }

    /**
     * Uploads the share's permissions using the specified request options and operation context.
     * 
     * @param permissions
     *            A {@link FileSharePermissions} object that represents the permissions to upload.
     * @param accessCondition
     *            An {@link AccessCondition} object that represents the access conditions for the share.
     * @param options
     *            A {@link FileRequestOptions} 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 CloudFileClient}).
     * @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.
     */
    @DoesServiceRequest
    public void uploadPermissions(final FileSharePermissions permissions, final AccessCondition accessCondition,
            FileRequestOptions options, OperationContext opContext) throws StorageException {
        assertNoSnapshot();

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

        opContext.initialize();
        options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);

        ExecutionEngine.executeWithRetry(this.fileServiceClient, this,
                uploadPermissionsImpl(permissions, accessCondition, options), options.getRetryPolicyFactory(),
                opContext);
    }

    private StorageRequest<CloudFileClient, CloudFileShare, Void> uploadPermissionsImpl(
            final FileSharePermissions permissions, final AccessCondition accessCondition,
            final FileRequestOptions options) throws StorageException {
        try {
            final StringWriter outBuffer = new StringWriter();
            SharedAccessPolicySerializer.writeSharedAccessIdentifiersToStream(permissions.getSharedAccessPolicies(),
                    outBuffer);
            final byte[] aclBytes = outBuffer.toString().getBytes(Constants.UTF8_CHARSET);
            final StorageRequest<CloudFileClient, CloudFileShare, Void> putRequest =
                    new StorageRequest<CloudFileClient, CloudFileShare, Void>(options, this.getStorageUri()) {
                @Override
                public HttpURLConnection buildRequest(CloudFileClient client, CloudFileShare share,
                        OperationContext context) throws Exception {
                    this.setSendStream(new ByteArrayInputStream(aclBytes));
                    this.setLength((long) aclBytes.length);
                    return FileRequest.setAcl(share.getTransformedAddress().getUri(this.getCurrentLocation()),
                            options, context, accessCondition);
                }

                @Override
                public void signRequest(HttpURLConnection connection, CloudFileClient client, OperationContext context)
                        throws Exception {
                    StorageRequest.signBlobQueueAndFileRequest(connection, client, aclBytes.length, context);
                }

                @Override
                public Void preProcessResponse(CloudFileShare share, CloudFileClient client,
                        OperationContext context) throws Exception {
                    if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_OK) {
                        this.setNonExceptionedRetryableFailure(true);
                        return null;
                    }

                    share.updatePropertiesFromResponse(this.getConnection());
                    return null;
                }
            };

            return putRequest;
        }
        catch (IllegalArgumentException e) {
            // The request was not even made. There was an error while trying to write the permissions. Just throw.
            StorageException translatedException = StorageException.translateClientException(e);
            throw translatedException;
        }
        catch (IllegalStateException e) {
            // The request was not even made. There was an error while trying to write the permissions. Just throw.
            StorageException translatedException = StorageException.translateClientException(e);
            throw translatedException;
        }
        catch (IOException e) {
            // The request was not even made. There was an error while trying to write the permissions. Just throw.
            StorageException translatedException = StorageException.translateClientException(e);
            throw translatedException;
        }
    }

    /**
     * Returns a reference to a {@link CloudFileDirectory} object that represents the root file directory within this
     * share.
     * 
     * @return A {@link CloudFileDirectory} reference to the root directory for this share.
     * @throws StorageException
     * @throws URISyntaxException
     */
    public CloudFileDirectory getRootDirectoryReference() throws StorageException, URISyntaxException {
        return new CloudFileDirectory(this.storageUri, "", this);
    }

    /**
     * Verifies the passed in URI. Then parses it and uses its components to populate this resource's properties.
     * 
     * @param completeUri
     *            A {@link StorageUri} object which represents the complete URI.
     * @param credentials
     *            A {@link StorageCredentials} object used to authenticate access.
     * @throws StorageException
     *             If a storage service error occurred.
     */
    private void parseQueryAndVerify(final StorageUri completeUri, final StorageCredentials credentials)
            throws StorageException {
       Utility.assertNotNull("completeUri", completeUri);

        if (!completeUri.isAbsolute()) {
            throw new IllegalArgumentException(String.format(SR.RELATIVE_ADDRESS_NOT_PERMITTED, completeUri.toString()));
        }

        this.storageUri = PathUtility.stripURIQueryAndFragment(completeUri);

        final HashMap<String, String[]> queryParameters = PathUtility.parseQueryString(completeUri.getQuery());

        final String[] snapshotIDs = queryParameters.get(Constants.QueryConstants.SHARE_SNAPSHOT);
        if (snapshotIDs != null && snapshotIDs.length > 0) {
            this.snapshotID = snapshotIDs[0];
        }

        final StorageCredentialsSharedAccessSignature parsedCredentials = 
                SharedAccessSignatureHelper.parseQuery(completeUri);

        if (credentials != null && parsedCredentials != null) {
            throw new IllegalArgumentException(SR.MULTIPLE_CREDENTIALS_PROVIDED);
        }

        try {
            final boolean usePathStyleUris = Utility.determinePathStyleFromUri(this.storageUri.getPrimaryUri());
            this.fileServiceClient = new CloudFileClient(PathUtility.getServiceClientBaseAddress(
                    this.getStorageUri(), usePathStyleUris), credentials != null ? credentials : parsedCredentials);
            this.name = PathUtility.getShareNameFromUri(this.storageUri.getPrimaryUri(), usePathStyleUris);
        }
        catch (final URISyntaxException e) {
            throw Utility.generateNewUnexpectedStorageException(e);
        }
    }

    /**
     * Returns the File service client associated with this share.
     * 
     * @return A {@link CloudFileClient} object that represents the service client associated with this share.
     */
    public CloudFileClient getServiceClient() {
        return this.fileServiceClient;
    }

    /**
     * Returns the URI for this share.
     * 
     * @return The absolute URI to the share.
     */
    public URI getUri() {
        return this.storageUri.getPrimaryUri();
    }

    /**
     * Returns the snapshotID for this share.
     * 
     * @return The snapshotID as a string for this share.
     */
    public final String getSnapshot() {
        return this.snapshotID;
    }

    /**
     * Indicates whether this share is a snapshot.
     *
     * @return <code>true</code> if the share is a snapshot, otherwise <code>false</code>.
     *
     * @see DeleteSnapshotsOption
     */
    public final boolean isSnapshot() {
        return this.snapshotID != null;
    }

    /**
     * Returns the list of URIs for all locations.
     * 
     * @return A {@link StorageUri} object which represents the list of URIs for all locations.
     */
    public StorageUri getStorageUri() {
        return this.storageUri;
    }

    /**
     * Returns the snapshot or shared access signature qualified URI for this share.
     *
     * @return A <code>java.net.URI</code> object that represents the snapshot or shared access signature.
     *
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws URISyntaxException
     *             If the resource URI is invalid.
     */
    public final URI getQualifiedUri() throws URISyntaxException, StorageException {
        if (this.isSnapshot()) {
            return PathUtility.addToQuery(this.getUri(), String.format("sharesnapshot=%s", this.snapshotID));
        }

        return this.fileServiceClient.getCredentials().transformUri(this.getUri());
    }

    /**
     * Returns the name of the share.
     * 
     * @return A <code>String</code> that represents the name of the share.
     */
    public String getName() {
        return this.name;
    }

    /**
     * Returns the metadata for the share. This value is initialized with the metadata from the share by a call to
     * {@link #downloadAttributes}, and is set on the share with a call to {@link #uploadMetadata}.
     * 
     * @return A <code>java.util.HashMap</code> object that represents the metadata for the share.
     */
    public HashMap<String, String> getMetadata() {
        return this.metadata;
    }

    /**
     * Returns the properties for the share.
     * 
     * @return A {@link FileShareProperties} object that represents the properties for the share.
     */
    public FileShareProperties getProperties() {
        return this.properties;
    }

    /**
     * Sets the metadata collection of name-value pairs to be set on the share with an {@link #uploadMetadata} call.
     * This collection will overwrite any existing share metadata. If this is set to an empty collection, the
     * share metadata will be cleared on an {@link #uploadMetadata} call.
     * 
     * @param metadata
     *            A <code>java.util.HashMap</code> object that represents the metadata being assigned to the share.
     */
    public void setMetadata(final HashMap<String, String> metadata) {
        this.metadata = metadata;
    }

    /**
     * Sets the properties for the share.
     * 
     * @param properties
     *            A {@link FileShareProperties} object that represents the properties being assigned to the
     *            share.
     */
    public void setProperties(final FileShareProperties properties) {
        this.properties = properties;
    }

    /**
     * Returns the URI after applying the authentication transformation.
     * 
     * @return A <code>java.net.URI</code> object that represents the URI after applying the authentication
     *         transformation.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws URISyntaxException
     *             If the resource URI is invalid.
     */
    private StorageUri getTransformedAddress() throws URISyntaxException, StorageException {
        return this.fileServiceClient.getCredentials().transformUri(this.storageUri);
    }
} public class FileTestHelper extends TestHelper {

    protected static String generateRandomShareName() {
        String shareName = "share" + UUID.randomUUID().toString();
        return shareName.replace("-", "");
    }

    public static String generateRandomFileName() {
        String shareName = "file" + UUID.randomUUID().toString();
        return shareName.replace("-", "");
    }
    
    public static CloudFile uploadNewFile(CloudFileShare share, int length, OperationContext context)
            throws StorageException, IOException, URISyntaxException {
        
        return uploadNewFile(share, getRandomDataStream(length), length, context);
    }
    
    public static CloudFile uploadNewFile(
            CloudFileShare share, InputStream stream, int length, OperationContext context)
            throws StorageException, IOException, URISyntaxException {
        String name = generateRandomFileName();

        CloudFile file = null;

        file = share.getRootDirectoryReference().getFileReference(name);
        file.upload(stream, length, null, null, context);
        return file;
    }

    public static CloudFileShare getRandomShareReference() throws URISyntaxException, StorageException {
        String shareName = generateRandomShareName();
        CloudFileClient fileClient = TestHelper.createCloudFileClient();
        CloudFileShare share = fileClient.getShareReference(shareName);

        return share;
    }
    
    static StorageUri ensureTrailingSlash(StorageUri uri) throws URISyntaxException {
        URI primary = uri.getPrimaryUri();
        URI secondary = uri.getSecondaryUri();
        
        // Add a trailing slash to primary if it did not previously have one
        if (primary != null) {
            String primaryUri = primary.toString();
            if (!primaryUri.isEmpty() && !primaryUri.substring(primaryUri.length() - 1).equals("/")) {
                primaryUri += "/";
                primary = new URI(primaryUri);
            }
        }

        // Add a trailing slash to secondary if it did not previously have one
        if (secondary != null) {
            String secondaryUri = secondary.toString();
            if (!secondaryUri.isEmpty() &&  !secondaryUri.substring(secondaryUri.length() - 1).equals("/")) {
                secondaryUri += "/";
                secondary = new URI(secondaryUri);
            }
        }
        
        return new StorageUri(primary, secondary);
    }

    protected static void doDownloadTest(CloudFile file, int fileSize, int bufferSize, int bufferOffset)
            throws StorageException, IOException, URISyntaxException {
        final Random randGenerator = new Random();
        final byte[] buffer = new byte[fileSize];
        randGenerator.nextBytes(buffer);
        byte[] resultBuffer = new byte[bufferSize];

        FileRequestOptions options = new FileRequestOptions();
        file.upload(new ByteArrayInputStream(buffer), buffer.length);
        file.downloadToByteArray(resultBuffer, bufferOffset, null, options, null);

        for (int i = 0; i < file.getProperties().getLength(); i++) {
            Assert.assertEquals(buffer[i], resultBuffer[bufferOffset + i]);
        }

        if (bufferOffset + fileSize < bufferSize) {
            for (int k = bufferOffset + fileSize; k < bufferSize; k++) {
                Assert.assertEquals(0, resultBuffer[k]);
            }
        }
    }

    protected static void doDownloadRangeToByteArrayTest(CloudFile file, int fileSize, int bufferSize,
            int bufferOffset, Long fileOffset, Long length) throws IOException, StorageException, URISyntaxException {
        final Random randGenerator = new Random();
        final byte[] buffer = new byte[fileSize];
        randGenerator.nextBytes(buffer);
        byte[] resultBuffer = new byte[bufferSize];
        int downloadLength;

        FileRequestOptions options = new FileRequestOptions();
        file.upload(new ByteArrayInputStream(buffer), buffer.length);

        downloadLength = file.downloadRangeToByteArray(fileOffset, length, resultBuffer, bufferOffset, null, options,
                null);

        int downloadSize = Math.min(fileSize - (int) (fileOffset != null ? fileOffset : 0), bufferSize - bufferOffset);
        if (length != null && length < downloadSize) {
            downloadSize = length.intValue();
        }

        Assert.assertEquals(downloadSize, downloadLength);

        for (int i = 0; i < bufferOffset; i++) {
            Assert.assertEquals(0, resultBuffer[i]);
        }

        for (int j = 0; j < downloadLength; j++) {
            Assert.assertEquals(buffer[(int) ((fileOffset != null ? fileOffset : 0) + j)], resultBuffer[bufferOffset
                    + j]);
        }

        for (int k = bufferOffset + downloadLength; k < bufferSize; k++) {
            Assert.assertEquals(0, resultBuffer[k]);
        }
    }

    protected static void doDownloadRangeToByteArrayNegativeTests(CloudFile file) throws StorageException, IOException, URISyntaxException {
        int fileLength = 1024;
        int resultBufSize = 1024;
        final Random randGenerator = new Random();
        final byte[] buffer = new byte[fileLength];
        randGenerator.nextBytes(buffer);
        byte[] resultBuffer = new byte[resultBufSize];

        file.upload(new ByteArrayInputStream(buffer), buffer.length);

        try {
            file.downloadRangeToByteArray(1024, (long) 1, resultBuffer, 0);
            Assert.fail();
        }
        catch (StorageException ex) {
            Assert.assertEquals(416, ex.getHttpStatusCode());
        }

        try {
            file.downloadToByteArray(resultBuffer, 1024);
            Assert.fail();
        }
        catch (IndexOutOfBoundsException ex) {
        }

        try {
            file.downloadRangeToByteArray(0, (long) 1023, resultBuffer, 2);
            Assert.fail();
        }
        catch (IndexOutOfBoundsException ex) {

        }

        // negative length
        try {
            file.downloadRangeToByteArray(0, (long) -10, resultBuffer, 0);
            Assert.fail();
        }
        catch (IndexOutOfBoundsException ex) {

        }

        // negative file offset
        try {
            file.downloadRangeToByteArray(-10, (long) 20, resultBuffer, 0);
            Assert.fail();
        }
        catch (IndexOutOfBoundsException ex) {

        }

        // negative buffer offset
        try {
            file.downloadRangeToByteArray(0, (long) 20, resultBuffer, -10);
            Assert.fail();
        }
        catch (IndexOutOfBoundsException ex) {

        }
    }
    
    public static CloudFile defiddler(CloudFile file) throws URISyntaxException, StorageException {
        URI oldUri = file.getUri();
        URI newUri = defiddler(oldUri);

        if (newUri != oldUri) {
            CloudFile newFile = new CloudFile(newUri, file.getServiceClient().getCredentials());
            return newFile;
        }
        else {
            return file;
        }
    }
    
    public static void waitForCopy(CloudFile file) throws StorageException, InterruptedException {
        boolean copyInProgress = true;
        while (copyInProgress) {
            file.downloadAttributes();
            copyInProgress = (file.getCopyState().getStatus() == CopyStatus.PENDING)
                    || (file.getCopyState().getStatus() == CopyStatus.ABORTED);
            // One second sleep if retry is needed
            if (copyInProgress) {
                Thread.sleep(1000);
            }
        }
    }

    public static void assertAreEqual(CloudFile file1, CloudFile file2) {
        if (file1 == null) {
            Assert.assertNull(file2);
        }
        else {
            Assert.assertNotNull(file2);
            Assert.assertEquals(file1.getUri(), file2.getUri());
            assertAreEqual(file1.getProperties(), file2.getProperties());
        }
    }

    public static void assertAreEqual(FileProperties prop1, FileProperties prop2) {
        if (prop1 == null) {
            Assert.assertNull(prop2);
        }
        else {
            Assert.assertNotNull(prop2);
            Assert.assertEquals(prop1.getCacheControl(), prop2.getCacheControl());
            Assert.assertEquals(prop1.getContentDisposition(), prop2.getContentDisposition());
            Assert.assertEquals(prop1.getContentEncoding(), prop2.getContentEncoding());
            Assert.assertEquals(prop1.getContentLanguage(), prop2.getContentLanguage());
            Assert.assertEquals(prop1.getContentMD5(), prop2.getContentMD5());
            Assert.assertEquals(prop1.getContentType(), prop2.getContentType());
            Assert.assertEquals(prop1.getEtag(), prop2.getEtag());
            Assert.assertEquals(prop1.getLastModified(), prop2.getLastModified());
            Assert.assertEquals(prop1.getLength(), prop2.getLength());
        }
    }

    public static void setFileProperties(CloudFile file) {
        file.getProperties().setCacheControl("no-transform");
        file.getProperties().setContentDisposition("attachment");
        file.getProperties().setContentEncoding("gzip");
        file.getProperties().setContentLanguage("tr,en");
        file.getProperties().setContentMD5("MDAwMDAwMDA=");
        file.getProperties().setContentType("text/html");
    }
} public final class CloudQueue {

    /**
     * Gets the first message from a list of queue messages, if any.
     * 
     * @param messages
     *            The <code>Iterable</code> collection of {@link CloudQueueMessage} objects to get the first message
     *            from.
     * 
     * @return The first {@link CloudQueueMessage} from the list of queue messages, or <code>null</code> if the list is
     *         empty.
     */
    private static CloudQueueMessage getFirstOrNull(final Iterable<CloudQueueMessage> messages) {
        for (final CloudQueueMessage m : messages) {
            return m;
        }

        return null;
    }

    /**
     * The name of the queue.
     */
    private String name;

    /**
     * A reference to the queue's associated service client.
     */
    private CloudQueueClient queueServiceClient;

    /**
     * The queue's Metadata collection.
     */
    private HashMap<String, String> metadata;

    /**
     * The queue's approximate message count, as reported by the server.
     */
    private long approximateMessageCount;

    /**
     * The <code for the messages of the queue.
     */
    private StorageUri messageRequestAddress;

    /**
     * Holds the list of URIs for all locations.
     */
    private StorageUri storageUri;

    /**
     * A flag indicating whether or not the message should be encoded in base-64.
     */
    private boolean shouldEncodeMessage;

    /**
     * Creates an instance of the <code>CloudQueue</code> class using the specified queue URI. The queue
     * <code>URI</code> must include a SAS token.
     * 
     * @param uri
     *            A <code>java.net.URI</code> object that represents the absolute URI of the queue.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    public CloudQueue(final URI uri) throws StorageException {
        this(new StorageUri(uri, null));
    }

    /**
     * Creates an instance of the <code>CloudQueue</code> class using the specified queue <code>StorageUri</code>. The 
     * queue <code>StorageUri</code> must include a SAS token.
     * 
     * @param uri
     *            A <code>StorageUri</code> object that represents the absolute URI of the queue.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    public CloudQueue(final StorageUri uri) throws StorageException {
        this(uri, (StorageCredentials)null);
    }

    /**
     * Creates an instance of the <code>CloudQueue</code> class using the specified queue <code>URI</code> and
     * credentials. If the <code>URI</code> contains a SAS token, the credentials must be <code>null</code>.
     * 
     * @param uri
     *            A <code>java.net.URI</code> object that represents the absolute URI of the queue.
     * @param credentials
     *            A {@link StorageCredentials} object used to authenticate access.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    public CloudQueue(final URI uri, final StorageCredentials credentials) throws StorageException {
        this(new StorageUri(uri), credentials);
    }

    /**
     * Creates an instance of the <code>CloudQueue</code> class using the specified queue <code>StorageUri</code> and
     * credentials. If the <code>StorageUri</code> contains a SAS token, the credentials must be <code>null</code>.
     * 
     * @param uri
     *            A <code>StorageUri</code> object that represents the absolute URI of the queue.
     * @param credentials
     *            A {@link StorageCredentials} object used to authenticate access.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    public CloudQueue(final StorageUri uri, final StorageCredentials credentials) throws StorageException {
        this.shouldEncodeMessage = true;
        this.parseQueryAndVerify(uri, credentials);
    }
    
    /**
     * Creates an instance of the <code>CloudQueue</code> class using the specified name and client.
     * 
     * @param queueName
     *            The name of the queue, which must adhere to queue naming rules. The queue name should not include any
     *            path separator characters (/).
     *            Queue names must be lowercase, between 3-63 characters long and must start with a letter or number.
     *            Queue names may contain only letters, numbers, and the dash (-) character.
     * @param client
     *            A {@link CloudQueueClient} object that represents the associated service client, and that specifies
     *            the endpoint for the Queue service.
     * @throws URISyntaxException
     *             If the resource URI constructed based on the queueName is invalid.
     * @throws StorageException
     *             If a storage service error occurred.
     * @see <a href="http://msdn.microsoft.com/en-us/library/azure/dd179349.aspx">Naming Queues and Metadata</a>
     */
    protected CloudQueue(final String queueName, final CloudQueueClient client) throws URISyntaxException,
            StorageException {
        Utility.assertNotNull("client", client);
        Utility.assertNotNull("queueName", queueName);

        this.storageUri = PathUtility.appendPathToUri(client.getStorageUri(), queueName);
        this.name = queueName;
        this.queueServiceClient = client;
        this.shouldEncodeMessage = true;
    }

    /**
     * Adds a message to the back of the queue.
     * 
     * @param message
     *            A {@link CloudQueueMessage} object that specifies the message to add.
     *            The message object is modified to include the message ID and pop receipt,
     *            and can be used in subsequent calls to updateMessage and deleteMessage.
     *
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public void addMessage(final CloudQueueMessage message) throws StorageException {
        this.addMessage(message, 0, 0, null /* options */, null /* opContext */);
    }

    /**
     * Adds a message to the back of the queue with the specified options.
     * 
     * @param message
     *            A {@link CloudQueueMessage} object that specifies the message to add.
     *            The message object is modified to include the message ID and pop receipt,
     *            and can be used in subsequent calls to updateMessage and deleteMessage.
     * 
     * @param timeToLiveInSeconds
     *            The maximum time to allow the message to be in the queue. A value of zero will set the time-to-live to
     *            the service default value of seven days.
     * 
     * @param initialVisibilityDelayInSeconds
     *            The length of time during which the message will be invisible, starting when it is added to the queue,
     *            or 0 to make the message visible immediately. This value must be greater than or equal to zero and
     *            less than or equal to the time-to-live value.
     * 
     * @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 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
    public void addMessage(final CloudQueueMessage message, final int timeToLiveInSeconds,
            final int initialVisibilityDelayInSeconds, QueueRequestOptions options, OperationContext opContext)
            throws StorageException {
        Utility.assertNotNull("message", message);
        Utility.assertNotNull("messageContent", message.getMessageContentAsByte());
        Utility.assertInBounds("timeToLiveInSeconds", timeToLiveInSeconds, 0,
                QueueConstants.MAX_TIME_TO_LIVE_IN_SECONDS);

        final int realTimeToLiveInSeconds = timeToLiveInSeconds == 0 ? QueueConstants.MAX_TIME_TO_LIVE_IN_SECONDS
                : timeToLiveInSeconds;
        Utility.assertInBounds("initialVisibilityDelayInSeconds", initialVisibilityDelayInSeconds, 0,
                realTimeToLiveInSeconds - 1);

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

        opContext.initialize();
        options = QueueRequestOptions.populateAndApplyDefaults(options, this.queueServiceClient);

        ExecutionEngine.executeWithRetry(this.queueServiceClient, this,
            this.addMessageImpl(message, realTimeToLiveInSeconds, initialVisibilityDelayInSeconds, options),
            options.getRetryPolicyFactory(), opContext);
    }

    private StorageRequest<CloudQueueClient, CloudQueue, Void> addMessageImpl(final CloudQueueMessage message,
            final int timeToLiveInSeconds, final int initialVisibilityDelayInSeconds, final QueueRequestOptions options)
            throws StorageException {
        final String stringToSend = message.getMessageContentForTransfer(this.shouldEncodeMessage);

        try {
            final byte[] messageBytes = QueueMessageSerializer.generateMessageRequestBody(stringToSend);

            final StorageRequest<CloudQueueClient, CloudQueue, Void> putRequest =
                    new StorageRequest<CloudQueueClient, CloudQueue, Void>(options, this.getStorageUri()) {

                @Override
                public HttpURLConnection buildRequest(CloudQueueClient client, CloudQueue queue,
                        OperationContext context) throws Exception {
                    this.setSendStream(new ByteArrayInputStream(messageBytes));
                    this.setLength((long) messageBytes.length);
                    return QueueRequest.putMessage(
                            queue.getMessageRequestAddress(context).getUri(this.getCurrentLocation()), options,
                            context, initialVisibilityDelayInSeconds, timeToLiveInSeconds);
                }

                @Override
                public void signRequest(HttpURLConnection connection, CloudQueueClient client, OperationContext context)
                        throws Exception {
                    StorageRequest.signBlobQueueAndFileRequest(connection, client, messageBytes.length, context);
                }

                @Override
                public Void preProcessResponse(CloudQueue queue, CloudQueueClient client,
                        OperationContext context) throws Exception {
                    if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_CREATED) {
                        this.setNonExceptionedRetryableFailure(true);
                        return null;
                    }

                    // Parse the returned messages
                    CloudQueueMessage returnedMessage = QueueMessageHandler.readMessages(
                            this.getConnection().getInputStream(), queue.shouldEncodeMessage).get(0);

                    message.setInsertionTime(returnedMessage.getInsertionTime());
                    message.setExpirationTime(returnedMessage.getExpirationTime());
                    message.setNextVisibleTime(returnedMessage.getNextVisibleTime());
                    message.setMessageId(returnedMessage.getMessageId());
                    message.setPopReceipt(returnedMessage.getPopReceipt());

                    return null;
                }
            };

            return putRequest;
        }
        catch (IllegalArgumentException e) {
            // The request was not even made. There was an error while trying to write the message. Just throw.
            StorageException translatedException = StorageException.translateClientException(e);
            throw translatedException;
        }
        catch (IllegalStateException e) {
            // The request was not even made. There was an error while trying to write the message. Just throw.
            StorageException translatedException = StorageException.translateClientException(e);
            throw translatedException;
        }
        catch (IOException e) {
            // The request was not even made. There was an error while trying to write the message. Just throw.
            StorageException translatedException = StorageException.translateClientException(e);
            throw translatedException;
        }
    }

    /**
     * Clears all messages from the queue.
     * 
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public void clear() throws StorageException {
        this.clear(null /* options */, null /* opContext */);
    }

    /**
     * Clears all messages from the queue, using the specified request options and operation context.
     * 
     * @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 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
    public void clear(QueueRequestOptions options, OperationContext opContext) throws StorageException {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        opContext.initialize();
        options = QueueRequestOptions.populateAndApplyDefaults(options, this.queueServiceClient);

        ExecutionEngine.executeWithRetry(this.queueServiceClient, this, this.clearImpl(options),
                options.getRetryPolicyFactory(), opContext);
    }

    private StorageRequest<CloudQueueClient, CloudQueue, Void> clearImpl(final QueueRequestOptions options) {
        final StorageRequest<CloudQueueClient, CloudQueue, Void> putRequest = new StorageRequest<CloudQueueClient, CloudQueue, Void>(
                options, this.getStorageUri()) {

            @Override
            public HttpURLConnection buildRequest(CloudQueueClient client, CloudQueue queue, OperationContext context)
                    throws Exception {
                return QueueRequest.clearMessages(
                        queue.getMessageRequestAddress(context).getUri(this.getCurrentLocation()), options, context);
            }

            @Override
            public void signRequest(HttpURLConnection connection, CloudQueueClient client, OperationContext context)
                    throws Exception {
                StorageRequest.signBlobQueueAndFileRequest(connection, client, -1L, context);
            }

            @Override
            public Void preProcessResponse(CloudQueue parentObject, CloudQueueClient client, OperationContext context)
                    throws Exception {
                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_NO_CONTENT) {
                    this.setNonExceptionedRetryableFailure(true);
                }

                return null;
            }
        };

        return putRequest;
    }

    /**
     * Creates the queue.
     * 
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public void create() throws StorageException {
        this.create(null /* options */, null /* opContext */);
    }

    /**
     * Creates the queue, using the specified request options and operation context.
     * 
     * @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 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
    public void create(QueueRequestOptions options, OperationContext opContext) throws StorageException {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        opContext.initialize();
        options = QueueRequestOptions.populateAndApplyDefaults(options, this.queueServiceClient);

        ExecutionEngine.executeWithRetry(this.queueServiceClient, this, this.createImpl(options),
                options.getRetryPolicyFactory(), opContext);
    }

    private StorageRequest<CloudQueueClient, CloudQueue, Void> createImpl(final QueueRequestOptions options) {
        final StorageRequest<CloudQueueClient, CloudQueue, Void> putRequest = new StorageRequest<CloudQueueClient, CloudQueue, Void>(
                options, this.getStorageUri()) {

            @Override
            public HttpURLConnection buildRequest(CloudQueueClient client, CloudQueue queue, OperationContext context)
                    throws Exception {
                return QueueRequest.create(queue.getTransformedAddress(context).getUri(this.getCurrentLocation()),
                        options, context);
            }

            @Override
            public void setHeaders(HttpURLConnection connection, CloudQueue queue, OperationContext context) {
                QueueRequest.addMetadata(connection, queue.metadata, context);
            }

            @Override
            public void signRequest(HttpURLConnection connection, CloudQueueClient client, OperationContext context)
                    throws Exception {
                StorageRequest.signBlobQueueAndFileRequest(connection, client, 0L, context);
            }

            @Override
            public Void preProcessResponse(CloudQueue parentObject, CloudQueueClient client, OperationContext context)
                    throws Exception {
                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_CREATED
                        && this.getResult().getStatusCode() != HttpURLConnection.HTTP_NO_CONTENT) {
                    this.setNonExceptionedRetryableFailure(true);
                }

                return null;
            }

        };

        return putRequest;
    }

    /**
     * Creates the queue if it does not already exist.
     * 
     * @return A value of <code>true</code> if the queue is created in the storage service, otherwise <code>false</code>
     *         .
     * 
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public boolean createIfNotExists() throws StorageException {
        return this.createIfNotExists(null /* options */, null /* opContext */);
    }

    /**
     * Creates the queue if it does not already exist, using the specified request options and operation context.
     * 
     * @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 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.
     * 
     * @return A value of <code>true</code> if the queue is created in the storage service, otherwise <code>false</code>
     *         .
     * 
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public boolean createIfNotExists(QueueRequestOptions options, OperationContext opContext) throws StorageException {
        options = QueueRequestOptions.populateAndApplyDefaults(options, this.queueServiceClient);

        boolean exists = this.exists(true, options, opContext);
        if (exists) {
            return false;
        }
        else {
            try {
                this.create(options, opContext);
                return true;
            }
            catch (StorageException e) {
                if (e.getHttpStatusCode() == HttpURLConnection.HTTP_CONFLICT
                        && StorageErrorCodeStrings.QUEUE_ALREADY_EXISTS.equals(e.getErrorCode())) {
                    return false;
                }
                else {
                    throw e;
                }
            }
        }
    }

    /**
     * Deletes the queue.
     * 
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public void delete() throws StorageException {
        this.delete(null /* options */, null /* opContext */);
    }

    /**
     * Deletes the queue, using the specified request options and operation context.
     * 
     * @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 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
    public void delete(QueueRequestOptions options, OperationContext opContext) throws StorageException {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        opContext.initialize();
        options = QueueRequestOptions.populateAndApplyDefaults(options, this.queueServiceClient);

        ExecutionEngine.executeWithRetry(this.queueServiceClient, this, this.deleteImpl(options),
                options.getRetryPolicyFactory(), opContext);
    }

    private StorageRequest<CloudQueueClient, CloudQueue, Void> deleteImpl(final QueueRequestOptions options) {
        final StorageRequest<CloudQueueClient, CloudQueue, Void> deleteRequest = new StorageRequest<CloudQueueClient, CloudQueue, Void>(
                options, this.getStorageUri()) {

            @Override
            public HttpURLConnection buildRequest(CloudQueueClient client, CloudQueue queue, OperationContext context)
                    throws Exception {
                return QueueRequest.delete(queue.getTransformedAddress(context).getUri(this.getCurrentLocation()),
                        options, context);
            }

            @Override
            public void signRequest(HttpURLConnection connection, CloudQueueClient client, OperationContext context)
                    throws Exception {
                StorageRequest.signBlobQueueAndFileRequest(connection, client, -1L, context);
            }

            @Override
            public Void preProcessResponse(CloudQueue parentObject, CloudQueueClient client, OperationContext context)
                    throws Exception {
                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_NO_CONTENT) {
                    this.setNonExceptionedRetryableFailure(true);
                }

                return null;
            }
        };

        return deleteRequest;
    }

    /**
     * Deletes the queue if it exists.
     * 
     * @return A value of <code>true</code> if the queue existed in the storage service and has been deleted, otherwise
     *         <code>false</code>.
     * 
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public boolean deleteIfExists() throws StorageException {
        return this.deleteIfExists(null /* options */, null /* opContext */);
    }

    /**
     * Deletes the queue if it exists, using the specified request options and operation context.
     * 
     * @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 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.
     * 
     * @return A value of <code>true</code> if the queue existed in the storage service and has been deleted, otherwise
     *         <code>false</code>.
     * 
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public boolean deleteIfExists(QueueRequestOptions options, OperationContext opContext) throws StorageException {
        options = QueueRequestOptions.populateAndApplyDefaults(options, this.queueServiceClient);

        boolean exists = this.exists(true, options, opContext);
        if (exists) {
            try {
                this.delete(options, opContext);
                return true;
            }
            catch (StorageException e) {
                if (e.getHttpStatusCode() == HttpURLConnection.HTTP_NOT_FOUND
                        && StorageErrorCodeStrings.QUEUE_NOT_FOUND.equals(e.getErrorCode())) {
                    return false;
                }
                else {
                    throw e;
                }
            }

        }
        else {
            return false;
        }
    }

    /**
     * Deletes the specified message from the queue.
     * 
     * @param message
     *            A {@link CloudQueueMessage} object that specifies the message to delete.
     * 
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public void deleteMessage(final CloudQueueMessage message) throws StorageException {
        this.deleteMessage(message, null /* options */, null /* opContext */);
    }

    /**
     * Deletes the specified message from the queue, using the specified request options and operation context.
     * 
     * @param message
     *            A {@link CloudQueueMessage} object that specifies the message to delete.
     * 
     * @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 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
    public void deleteMessage(final CloudQueueMessage message, QueueRequestOptions options, OperationContext opContext)
            throws StorageException {
        Utility.assertNotNull("message", message);
        Utility.assertNotNullOrEmpty("messageId", message.getId());
        Utility.assertNotNullOrEmpty("popReceipt", message.getPopReceipt());

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

        opContext.initialize();
        options = QueueRequestOptions.populateAndApplyDefaults(options, this.queueServiceClient);

        ExecutionEngine.executeWithRetry(this.queueServiceClient, this, this.deleteMessageImpl(message, options),
                options.getRetryPolicyFactory(), opContext);
    }

    private StorageRequest<CloudQueueClient, CloudQueue, Void> deleteMessageImpl(final CloudQueueMessage message,
            final QueueRequestOptions options) {
        final String messageId = message.getId();
        final String messagePopReceipt = message.getPopReceipt();

        final StorageRequest<CloudQueueClient, CloudQueue, Void> putRequest = new StorageRequest<CloudQueueClient, CloudQueue, Void>(
                options, this.getStorageUri()) {

            @Override
            public HttpURLConnection buildRequest(CloudQueueClient client, CloudQueue queue, OperationContext context)
                    throws Exception {
                return QueueRequest.deleteMessage(
                        queue.getIndividualMessageAddress(messageId, context).getUri(this.getCurrentLocation()),
                        options, context, messagePopReceipt);
            }

            @Override
            public void signRequest(HttpURLConnection connection, CloudQueueClient client, OperationContext context)
                    throws Exception {
                StorageRequest.signBlobQueueAndFileRequest(connection, client, -1L, context);
            }

            @Override
            public Void preProcessResponse(CloudQueue parentObject, CloudQueueClient client, OperationContext context)
                    throws Exception {
                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_NO_CONTENT) {
                    this.setNonExceptionedRetryableFailure(true);
                }

                return null;
            }
        };

        return putRequest;
    }

    /**
     * Downloads the queue's metadata and approximate message count value.
     * 
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public void downloadAttributes() throws StorageException {
        this.downloadAttributes(null /* options */, null /* opContext */);
    }

    /**
     * Downloads the queue's metadata and approximate message count value, using the specified request options and
     * operation context.
     * 
     * @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.
     * 
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public void downloadAttributes(QueueRequestOptions options, OperationContext opContext) throws StorageException {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        opContext.initialize();
        options = QueueRequestOptions.populateAndApplyDefaults(options, this.queueServiceClient);

        ExecutionEngine.executeWithRetry(this.queueServiceClient, this, this.downloadAttributesImpl(options),
                options.getRetryPolicyFactory(), opContext);
    }

    private StorageRequest<CloudQueueClient, CloudQueue, Void> downloadAttributesImpl(final QueueRequestOptions options) {
        final StorageRequest<CloudQueueClient, CloudQueue, Void> getRequest = new StorageRequest<CloudQueueClient, CloudQueue, Void>(
                options, this.getStorageUri()) {

            @Override
            public void setRequestLocationMode() {
                this.setRequestLocationMode(RequestLocationMode.PRIMARY_OR_SECONDARY);
            }

            @Override
            public HttpURLConnection buildRequest(CloudQueueClient client, CloudQueue queue, OperationContext context)
                    throws Exception {
                return QueueRequest.downloadAttributes(
                        queue.getTransformedAddress(context).getUri(this.getCurrentLocation()), options, context);
            }

            @Override
            public void signRequest(HttpURLConnection connection, CloudQueueClient client, OperationContext context)
                    throws Exception {
                StorageRequest.signBlobQueueAndFileRequest(connection, client, -1L, context);
            }

            @Override
            public Void preProcessResponse(CloudQueue queue, CloudQueueClient client, OperationContext context)
                    throws Exception {
                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_OK) {
                    this.setNonExceptionedRetryableFailure(true);
                    return null;
                }

                queue.metadata = BaseResponse.getMetadata(this.getConnection());
                queue.approximateMessageCount = QueueResponse.getApproximateMessageCount(this.getConnection());
                return null;
            }
        };

        return getRequest;
    }

    /**
     * Returns a value that indicates whether the queue exists.
     * 
     * @return <code>true</code> if the queue exists in the storage service, otherwise <code>false</code>.
     * 
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public boolean exists() throws StorageException {
        return this.exists(null /* options */, null /* opContext */);
    }

    /**
     * Returns a value that indicates whether the queue existse, using the specified request options and operation
     * context.
     * 
     * @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 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.
     * 
     * @return <code>true</code> if the queue exists in the storage service, otherwise <code>false</code>.
     * 
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public boolean exists(QueueRequestOptions options, OperationContext opContext) throws StorageException {
        return this.exists(false, options, opContext);
    }

    @DoesServiceRequest
    private boolean exists(final boolean primaryOnly, QueueRequestOptions options, OperationContext opContext)
            throws StorageException {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        opContext.initialize();
        options = QueueRequestOptions.populateAndApplyDefaults(options, this.queueServiceClient);

        return ExecutionEngine.executeWithRetry(this.queueServiceClient, this, this.existsImpl(primaryOnly, options),
                options.getRetryPolicyFactory(), opContext);
    }

    private StorageRequest<CloudQueueClient, CloudQueue, Boolean> existsImpl(final boolean primaryOnly,
            final QueueRequestOptions options) {
        final StorageRequest<CloudQueueClient, CloudQueue, Boolean> getRequest = new StorageRequest<CloudQueueClient, CloudQueue, Boolean>(
                options, this.getStorageUri()) {

            @Override
            public void setRequestLocationMode() {
                this.setRequestLocationMode(primaryOnly ? RequestLocationMode.PRIMARY_ONLY
                        : RequestLocationMode.PRIMARY_OR_SECONDARY);
            }

            @Override
            public HttpURLConnection buildRequest(CloudQueueClient client, CloudQueue queue, OperationContext context)
                    throws Exception {
                return QueueRequest.downloadAttributes(
                        queue.getTransformedAddress(context).getUri(this.getCurrentLocation()), options, context);
            }

            @Override
            public void signRequest(HttpURLConnection connection, CloudQueueClient client, OperationContext context)
                    throws Exception {
                StorageRequest.signBlobQueueAndFileRequest(connection, client, -1L, context);
            }

            @Override
            public Boolean preProcessResponse(CloudQueue parentObject, CloudQueueClient client, OperationContext context)
                    throws Exception {
                if (this.getResult().getStatusCode() == HttpURLConnection.HTTP_OK) {
                    return Boolean.valueOf(true);
                }
                else if (this.getResult().getStatusCode() == HttpURLConnection.HTTP_NOT_FOUND) {
                    return Boolean.valueOf(false);
                }
                else {
                    this.setNonExceptionedRetryableFailure(true);
                    // return false instead of null to avoid SCA issues
                    return false;
                }
            }
        };

        return getRequest;
    }

    /**
     * Gets the approximate messages count of the queue. This value is initialized by a request to
     * {@link #downloadAttributes} and represents the approximate message count when that request completed.
     * 
     * @return A <code>Long</code> object that represents the approximate messages count of the queue.
     */
    public long getApproximateMessageCount() {
        return this.approximateMessageCount;
    }

    /**
     * Get a single message request (Used internally only).
     * 
     * @return The <code>URI</code> for a single message request.
     * 
     * @throws URISyntaxException
     *             If the resource URI is invalid.
     * @throws StorageException
     */
    private StorageUri getIndividualMessageAddress(final String messageId, final OperationContext opContext)
            throws URISyntaxException, StorageException {
        return PathUtility.appendPathToUri(this.getMessageRequestAddress(opContext), messageId);
    }

    /**
     * Get the message request base address (Used internally only).
     * 
     * @return The message request <code>URI</code>.
     * 
     * @throws URISyntaxException
     *             If the resource URI is invalid.
     * @throws StorageException
     */
    private StorageUri getMessageRequestAddress(final OperationContext opContext) throws URISyntaxException,
            StorageException {
        if (this.messageRequestAddress == null) {
            this.messageRequestAddress = PathUtility.appendPathToUri(this.getTransformedAddress(opContext),
                    QueueConstants.MESSAGES);
        }

        return this.messageRequestAddress;
    }

    /**
     * Gets the metadata collection for the queue as stored in this <code>CloudQueue</code> object. This value is
     * initialized with the metadata from the queue by a call to {@link #downloadAttributes}, and is set on the queue
     * with a call to {@link #uploadMetadata}.
     * 
     * @return A <code>java.util.HashMap</code> object that represents the metadata for the queue.
     */
    public HashMap<String, String> getMetadata() {
        return this.metadata;
    }

    /**
     * Gets the name of the queue.
     * 
     * @return A <code>String</code> object that represents the name of the queue.
     */
    public String getName() {
        return this.name;
    }

    /**
     * Gets the queue service client associated with this queue.
     * 
     * @return A {@link CloudQueueClient} object that represents the service client associated with this queue.
     */
    public CloudQueueClient getServiceClient() {
        return this.queueServiceClient;
    }

    /**
     * Gets the value indicating whether the message should be base-64 encoded.
     * 
     * @return A <code>Boolean</code> that represents whether the message should be base-64 encoded.
     */
    public boolean getShouldEncodeMessage() {
        return this.shouldEncodeMessage;
    }

    /**
     * Returns the list of URIs for all locations.
     * 
     * @return A <code>StorageUri</code> that represents the list of URIs for all locations..
     */
    public final StorageUri getStorageUri() {
        return this.storageUri;
    }

    /**
     * Gets the absolute URI for this queue.
     * 
     * @return A <code>java.net.URI</code> object that represents the URI for this queue.
     */
    public URI getUri() {
        return this.storageUri.getPrimaryUri();
    }

    /**
     * Peeks a message from the queue. A peek request retrieves a message from the front of the queue without changing
     * its visibility.
     * 
     * @return An {@link CloudQueueMessage} object that represents a message in this queue.
     * 
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public CloudQueueMessage peekMessage() throws StorageException {
        return this.peekMessage(null /* options */, null /* opContext */);
    }

    /**
     * Peeks a message from the queue, using the specified request options and operation context. A peek request
     * retrieves a message from the front of the queue without changing its visibility.
     * 
     * @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 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.
     * 
     * @return An {@link CloudQueueMessage} object that represents the requested message from the queue.
     * 
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public CloudQueueMessage peekMessage(final QueueRequestOptions options, final OperationContext opContext)
            throws StorageException {
        return getFirstOrNull(this.peekMessages(1, null /* options */, null /* opContext */));
    }

    /**
     * Peeks a specified number of messages from the queue. A peek request retrieves messages from the front of the
     * queue without changing their visibility.
     * 
     * @param numberOfMessages
     *            The number of messages to retrieve.
     * 
     * @return An enumerable collection of {@link CloudQueueMessage} objects that represents the requested messages from
     *         the queue.
     * 
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public Iterable<CloudQueueMessage> peekMessages(final int numberOfMessages) throws StorageException {
        return this.peekMessages(numberOfMessages, null /* options */, null /* opContext */);
    }

    /**
     * Peeks a set of messages from the queue, using the specified request options and operation context. A peek request
     * retrieves messages from the front of the queue without changing their visibility.
     * 
     * @param numberOfMessages
     *            The number of messages to retrieve.
     * 
     * @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 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.
     * 
     * @return An enumerable collection of {@link CloudQueueMessage} objects that represents the requested messages from
     *         the queue.
     * 
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public Iterable<CloudQueueMessage> peekMessages(final int numberOfMessages, QueueRequestOptions options,
            OperationContext opContext) throws StorageException {
        Utility.assertInBounds("numberOfMessages", numberOfMessages, 1, QueueConstants.MAX_NUMBER_OF_MESSAGES_TO_PEEK);

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

        opContext.initialize();
        options = QueueRequestOptions.populateAndApplyDefaults(options, this.queueServiceClient);

        return ExecutionEngine.executeWithRetry(this.queueServiceClient, this,
                this.peekMessagesImpl(numberOfMessages, options), options.getRetryPolicyFactory(), opContext);
    }

    private StorageRequest<CloudQueueClient, CloudQueue, ArrayList<CloudQueueMessage>> peekMessagesImpl(
            final int numberOfMessages, final QueueRequestOptions options) {
        final StorageRequest<CloudQueueClient, CloudQueue, ArrayList<CloudQueueMessage>> getRequest = new StorageRequest<CloudQueueClient, CloudQueue, ArrayList<CloudQueueMessage>>(
                options, this.getStorageUri()) {

            @Override
            public void setRequestLocationMode() {
                this.setRequestLocationMode(RequestLocationMode.PRIMARY_OR_SECONDARY);
            }

            @Override
            public HttpURLConnection buildRequest(CloudQueueClient client, CloudQueue queue, OperationContext context)
                    throws Exception {
                return QueueRequest.peekMessages(
                        queue.getMessageRequestAddress(context).getUri(this.getCurrentLocation()), options, context,
                        numberOfMessages);
            }

            @Override
            public void signRequest(HttpURLConnection connection, CloudQueueClient client, OperationContext context)
                    throws Exception {
                StorageRequest.signBlobQueueAndFileRequest(connection, client, -1L, context);
            }

            @Override
            public ArrayList<CloudQueueMessage> preProcessResponse(CloudQueue queue, CloudQueueClient client,
                    OperationContext context) throws Exception {
                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_OK) {
                    this.setNonExceptionedRetryableFailure(true);
                    return null;
                }
                else {
                    return QueueMessageHandler.readMessages(this.getConnection().getInputStream(),
                            queue.shouldEncodeMessage);
                }
            }

        };

        return getRequest;
    }

    /**
     * Retrieves a message from the front of the queue using the default request options. This operation marks the
     * retrieved message as invisible in the queue for the default visibility timeout period.
     * 
     * @return An {@link CloudQueueMessage} object that represents a message in this queue.
     * 
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public CloudQueueMessage retrieveMessage() throws StorageException {
        return this.retrieveMessage(QueueConstants.DEFAULT_VISIBILITY_MESSAGE_TIMEOUT_IN_SECONDS, null /* options */,
                null /* opContext */);
    }

    /**
     * Retrieves a message from the front of the queue, using the specified request options and operation context. This
     * operation marks the retrieved message as invisible in the queue for the specified visibility timeout period.
     * 
     * @param visibilityTimeoutInSeconds
     *            Specifies the visibility timeout for the message, in seconds.
     * 
     * @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 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.
     * 
     * @return An {@link CloudQueueMessage} object that represents a message in this queue.
     * 
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public CloudQueueMessage retrieveMessage(final int visibilityTimeoutInSeconds, final QueueRequestOptions options,
            final OperationContext opContext) throws StorageException {
        return getFirstOrNull(this.retrieveMessages(1, visibilityTimeoutInSeconds, options, opContext));
    }

    /**
     * Retrieves the specified number of messages from the front of the queue using the default request options. This
     * operation marks the retrieved messages as invisible in the queue for the default visibility timeout period.
     * 
     * @param numberOfMessages
     *            The number of messages to retrieve.
     * 
     * @return An enumerable collection of {@link CloudQueueMessage} objects that represents the retrieved messages from
     *         the queue.
     * 
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public Iterable<CloudQueueMessage> retrieveMessages(final int numberOfMessages) throws StorageException {
        return this.retrieveMessages(numberOfMessages, QueueConstants.DEFAULT_VISIBILITY_MESSAGE_TIMEOUT_IN_SECONDS,
                null /* options */, null /* opContext */);
    }

    /**
     * Retrieves the specified number of messages from the front of the queue using the specified request options and
     * operation context. This operation marks the retrieved messages as invisible in the queue for the default
     * visibility timeout period.
     * 
     * @param numberOfMessages
     *            The number of messages to retrieve.
     * 
     * @param visibilityTimeoutInSeconds
     *            Specifies the visibility timeout for the retrieved messages, in seconds.
     * 
     * @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 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.
     * 
     * @return An enumerable collection of {@link CloudQueueMessage} objects that represents the messages retrieved from
     *         the queue.
     * 
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public Iterable<CloudQueueMessage> retrieveMessages(final int numberOfMessages,
            final int visibilityTimeoutInSeconds, QueueRequestOptions options, OperationContext opContext)
            throws StorageException {
        Utility.assertInBounds("numberOfMessages", numberOfMessages, 1, QueueConstants.MAX_NUMBER_OF_MESSAGES_TO_PEEK);
        Utility.assertInBounds("visibilityTimeoutInSeconds", visibilityTimeoutInSeconds, 0,
                QueueConstants.MAX_TIME_TO_LIVE_IN_SECONDS);

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

        opContext.initialize();
        options = QueueRequestOptions.populateAndApplyDefaults(options, this.queueServiceClient);

        return ExecutionEngine.executeWithRetry(this.queueServiceClient, this,
                this.retrieveMessagesImpl(numberOfMessages, visibilityTimeoutInSeconds, options),
                options.getRetryPolicyFactory(), opContext);
    }

    private StorageRequest<CloudQueueClient, CloudQueue, ArrayList<CloudQueueMessage>> retrieveMessagesImpl(
            final int numberOfMessages, final int visibilityTimeoutInSeconds, final QueueRequestOptions options) {
        final StorageRequest<CloudQueueClient, CloudQueue, ArrayList<CloudQueueMessage>> getRequest = new StorageRequest<CloudQueueClient, CloudQueue, ArrayList<CloudQueueMessage>>(
                options, this.getStorageUri()) {

            @Override
            public HttpURLConnection buildRequest(CloudQueueClient client, CloudQueue queue, OperationContext context)
                    throws Exception {
                return QueueRequest.retrieveMessages(
                        queue.getMessageRequestAddress(context).getUri(this.getCurrentLocation()), options, context,
                        numberOfMessages, visibilityTimeoutInSeconds);
            }

            @Override
            public void signRequest(HttpURLConnection connection, CloudQueueClient client, OperationContext context)
                    throws Exception {
                StorageRequest.signBlobQueueAndFileRequest(connection, client, -1L, context);
            }

            @Override
            public ArrayList<CloudQueueMessage> preProcessResponse(CloudQueue queue, CloudQueueClient client,
                    OperationContext context) throws Exception {
                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_OK) {
                    this.setNonExceptionedRetryableFailure(true);
                    return null;
                }
                else {
                    return QueueMessageHandler.readMessages(this.getConnection().getInputStream(),
                            queue.shouldEncodeMessage);
                }
            }
        };

        return getRequest;
    }

    /**
     * Sets the metadata collection of name-value pairs to be set on the queue with an {@link #uploadMetadata} call.
     * This collection will overwrite any existing queue metadata. If this is set to an empty collection, the queue
     * metadata will be cleared on an {@link #uploadMetadata} call.
     * 
     * @param metadata
     *            A <code>java.util.HashMap</code> object that represents the metadata being assigned to the queue.
     */
    public void setMetadata(final HashMap<String, String> metadata) {
        this.metadata = metadata;
    }

    /**
     * Sets the flag indicating whether the message should be base-64 encoded.
     * 
     * @param shouldEncodeMessage
     *            The value indicates whether the message should be base-64 encoded.
     */
    public void setShouldEncodeMessage(final boolean shouldEncodeMessage) {
        this.shouldEncodeMessage = shouldEncodeMessage;
    }

    /**
     * Updates the specified message in the queue with a new visibility timeout value in seconds.
     * 
     * @param message
     *            The {@link CloudQueueMessage} to update in the queue.
     * 
     * @param visibilityTimeoutInSeconds
     *            Specifies the new visibility timeout for the message, in seconds.
     * 
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    public void updateMessage(final CloudQueueMessage message, final int visibilityTimeoutInSeconds)
            throws StorageException {
        this.updateMessage(message, visibilityTimeoutInSeconds, EnumSet.of(MessageUpdateFields.VISIBILITY),
                null /* options */, null /* opContext */);
    }

    /**
     * Updates a message in the queue, using the specified request options and operation context.
     * 
     * @param message
     *            The {@link CloudQueueMessage} to update in the queue.
     * 
     * @param visibilityTimeoutInSeconds
     *            Specifies the new visibility timeout for the message, in seconds.
     * 
     * @param messageUpdateFields
     *            An <code>EnumSet</code> of {@link MessageUpdateFields} values that specifies which parts of the
     *            message are to be updated.
     * 
     * @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 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
    public void updateMessage(final CloudQueueMessage message, final int visibilityTimeoutInSeconds,
            final EnumSet<MessageUpdateFields> messageUpdateFields, QueueRequestOptions options,
            OperationContext opContext) throws StorageException {
        Utility.assertNotNull("message", message);
        Utility.assertNotNullOrEmpty("messageId", message.getId());
        Utility.assertNotNullOrEmpty("popReceipt", message.getPopReceipt());

        Utility.assertInBounds("visibilityTimeoutInSeconds", visibilityTimeoutInSeconds, 0,
                QueueConstants.MAX_TIME_TO_LIVE_IN_SECONDS);

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

        opContext.initialize();
        options = QueueRequestOptions.populateAndApplyDefaults(options, this.queueServiceClient);

        ExecutionEngine.executeWithRetry(this.queueServiceClient, this,
                this.updateMessageImpl(message, visibilityTimeoutInSeconds, messageUpdateFields, options),
                options.getRetryPolicyFactory(), opContext);
    }

    private StorageRequest<CloudQueueClient, CloudQueue, Void> updateMessageImpl(final CloudQueueMessage message,
            final int visibilityTimeoutInSeconds, final EnumSet<MessageUpdateFields> messageUpdateFields,
            final QueueRequestOptions options) throws StorageException {
        final String stringToSend = message.getMessageContentForTransfer(this.shouldEncodeMessage);

        final StorageRequest<CloudQueueClient, CloudQueue, Void> putRequest = new StorageRequest<CloudQueueClient, CloudQueue, Void>(
                options, this.getStorageUri()) {

            @Override
            public HttpURLConnection buildRequest(CloudQueueClient client, CloudQueue queue, OperationContext context)
                    throws Exception {
                if (messageUpdateFields.contains(MessageUpdateFields.CONTENT)) {
                    try {
                        final byte[] messageBytes = QueueMessageSerializer.generateMessageRequestBody(stringToSend);
                        this.setSendStream(new ByteArrayInputStream(messageBytes));
                        this.setLength((long) messageBytes.length);
                    }
                    catch (IllegalArgumentException e) {
                        // The request was not even made. There was an error while trying to write the message. Just throw.
                        StorageException translatedException = StorageException.translateClientException(e);
                        throw translatedException;
                    }
                    catch (IllegalStateException e) {
                        // The request was not even made. There was an error while trying to write the message. Just throw.
                        StorageException translatedException = StorageException.translateClientException(e);
                        throw translatedException;
                    }
                    catch (IOException e) {
                        // The request was not even made. There was an error while trying to write the message. Just throw.
                        StorageException translatedException = StorageException.translateClientException(e);
                        throw translatedException;
                    }
                }

                return QueueRequest.updateMessage(
                        queue.getIndividualMessageAddress(message.getId(), context).getUri(this.getCurrentLocation()),
                        options, context, message.getPopReceipt(), visibilityTimeoutInSeconds);
            }

            @Override
            public void signRequest(HttpURLConnection connection, CloudQueueClient client, OperationContext context)
                    throws Exception {
                if (messageUpdateFields.contains(MessageUpdateFields.CONTENT)) {
                    StorageRequest.signBlobQueueAndFileRequest(connection, client, this.getLength(), context);
                }
                else {
                    connection.setFixedLengthStreamingMode(0);
                    StorageRequest.signBlobQueueAndFileRequest(connection, client, 0L, context);
                }
            }

            @Override
            public Void preProcessResponse(CloudQueue queue, CloudQueueClient client, OperationContext context)
                    throws Exception {
                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_NO_CONTENT) {
                    this.setNonExceptionedRetryableFailure(true);
                    return null;
                }

                message.setPopReceipt(this.getConnection().getHeaderField(Constants.HeaderConstants.POP_RECEIPT_HEADER));
                message.setNextVisibleTime(Utility.parseRFC1123DateFromStringInGMT(this.getConnection().getHeaderField(
                        Constants.HeaderConstants.TIME_NEXT_VISIBLE_HEADER)));

                return null;
            }
        };

        return putRequest;
    }

    /**
     * Uploads the metadata in the <code>CloudQueue</code> object to the queue.
     * 
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public void uploadMetadata() throws StorageException {
        this.uploadMetadata(null /* options */, null /* opContext */);
    }

    /**
     * Uploads the metadata in the <code>CloudQueue</code> object to the queue, using the specified request options and
     * operation context.
     * 
     * @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 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
    public void uploadMetadata(QueueRequestOptions options, OperationContext opContext) throws StorageException {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        opContext.initialize();
        options = QueueRequestOptions.populateAndApplyDefaults(options, this.queueServiceClient);

        ExecutionEngine.executeWithRetry(this.queueServiceClient, this, this.uploadMetadataImpl(options),
                options.getRetryPolicyFactory(), opContext);

    }

    private StorageRequest<CloudQueueClient, CloudQueue, Void> uploadMetadataImpl(final QueueRequestOptions options) {

        final StorageRequest<CloudQueueClient, CloudQueue, Void> putRequest = new StorageRequest<CloudQueueClient, CloudQueue, Void>(
                options, this.getStorageUri()) {

            @Override
            public HttpURLConnection buildRequest(CloudQueueClient client, CloudQueue queue, OperationContext context)
                    throws Exception {
                return QueueRequest.setMetadata(queue.getTransformedAddress(context).getUri(this.getCurrentLocation()),
                        options, context);
            }

            @Override
            public void setHeaders(HttpURLConnection connection, CloudQueue queue, OperationContext context) {
                QueueRequest.addMetadata(connection, queue.metadata, context);
            }

            @Override
            public void signRequest(HttpURLConnection connection, CloudQueueClient client, OperationContext context)
                    throws Exception {
                StorageRequest.signBlobQueueAndFileRequest(connection, client, 0L, context);
            }

            @Override
            public Void preProcessResponse(CloudQueue parentObject, CloudQueueClient client, OperationContext context)
                    throws Exception {
                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_NO_CONTENT) {
                    this.setNonExceptionedRetryableFailure(true);
                }

                return null;
            }

        };

        return putRequest;
    }

    /**
     * Uploads the queue's permissions.
     * 
     * @param permissions
     *            A {@link QueuePermissions} object that represents the permissions to upload.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public void uploadPermissions(final QueuePermissions permissions) throws StorageException {
        this.uploadPermissions(permissions, null /* options */, null /* opContext */);
    }

    /**
     * Uploads the queue's permissions using the specified request options and operation context.
     * 
     * @param permissions
     *            A {@link QueuePermissions} object that represents the permissions to upload.
     * @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 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.
     */
    @DoesServiceRequest
    public void uploadPermissions(final QueuePermissions permissions, QueueRequestOptions options,
            OperationContext opContext) throws StorageException {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        opContext.initialize();
        options = QueueRequestOptions.populateAndApplyDefaults(options, this.queueServiceClient);

        ExecutionEngine.executeWithRetry(this.queueServiceClient, this,
                this.uploadPermissionsImpl(permissions, options), options.getRetryPolicyFactory(), opContext);
    }

    private StorageRequest<CloudQueueClient, CloudQueue, Void> uploadPermissionsImpl(
            final QueuePermissions permissions, final QueueRequestOptions options) throws StorageException {

        final StringWriter outBuffer = new StringWriter();

        try {
            SharedAccessPolicySerializer.writeSharedAccessIdentifiersToStream(permissions.getSharedAccessPolicies(),
                    outBuffer);

            final byte[] aclBytes = outBuffer.toString().getBytes(Constants.UTF8_CHARSET);

            final StorageRequest<CloudQueueClient, CloudQueue, Void> putRequest = new StorageRequest<CloudQueueClient, CloudQueue, Void>(
                    options, this.getStorageUri()) {

                @Override
                public HttpURLConnection buildRequest(CloudQueueClient client, CloudQueue queue,
                        OperationContext context) throws Exception {
                    this.setSendStream(new ByteArrayInputStream(aclBytes));
                    this.setLength((long) aclBytes.length);
                    return QueueRequest.setAcl(queue.getTransformedAddress(context).getUri(this.getCurrentLocation()),
                            options, context);
                }

                @Override
                public void signRequest(HttpURLConnection connection, CloudQueueClient client, OperationContext context)
                        throws Exception {
                    StorageRequest.signBlobQueueAndFileRequest(connection, client, aclBytes.length, context);
                }

                @Override
                public Void preProcessResponse(CloudQueue parentObject, CloudQueueClient client,
                        OperationContext context) throws Exception {
                    if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_NO_CONTENT) {
                        this.setNonExceptionedRetryableFailure(true);
                    }

                    return null;
                }
            };

            return putRequest;
        }
        catch (IllegalArgumentException e) {
            // The request was not even made. There was an error while trying to write the message. Just throw.
            StorageException translatedException = StorageException.translateClientException(e);
            throw translatedException;
        }
        catch (IllegalStateException e) {
            // The request was not even made. There was an error while trying to write the message. Just throw.
            StorageException translatedException = StorageException.translateClientException(e);
            throw translatedException;
        }
        catch (IOException e) {
            // The request was not even made. There was an error while trying to write the message. Just throw.
            StorageException translatedException = StorageException.translateClientException(e);
            throw translatedException;
        }
    }

    /**
     * Downloads the permission settings for the queue.
     * 
     * @return A {@link QueuePermissions} object that represents the queue's permissions.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public QueuePermissions downloadPermissions() throws StorageException {
        return this.downloadPermissions(null /* options */, null /* opContext */);
    }

    /**
     * Downloads the permissions settings for the queue using the specified request options and operation context.
     * 
     * @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 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.
     * 
     * @return A {@link QueuePermissions} object that represents the container's permissions.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public QueuePermissions downloadPermissions(QueueRequestOptions options, OperationContext opContext)
            throws StorageException {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        opContext.initialize();
        options = QueueRequestOptions.populateAndApplyDefaults(options, this.queueServiceClient);

        return ExecutionEngine.executeWithRetry(this.queueServiceClient, this, this.downloadPermissionsImpl(options),
                options.getRetryPolicyFactory(), opContext);
    }

    private StorageRequest<CloudQueueClient, CloudQueue, QueuePermissions> downloadPermissionsImpl(
            final QueueRequestOptions options) {

        final StorageRequest<CloudQueueClient, CloudQueue, QueuePermissions> getRequest = new StorageRequest<CloudQueueClient, CloudQueue, QueuePermissions>(
                options, this.getStorageUri()) {

            @Override
            public void setRequestLocationMode() {
                this.setRequestLocationMode(RequestLocationMode.PRIMARY_OR_SECONDARY);
            }

            @Override
            public HttpURLConnection buildRequest(CloudQueueClient client, CloudQueue queue, OperationContext context)
                    throws Exception {
                return QueueRequest.getAcl(queue.getTransformedAddress(context).getUri(this.getCurrentLocation()),
                        options, context);
            }

            @Override
            public void signRequest(HttpURLConnection connection, CloudQueueClient client, OperationContext context)
                    throws Exception {
                StorageRequest.signBlobQueueAndFileRequest(connection, client, -1L, context);
            }

            @Override
            public QueuePermissions preProcessResponse(CloudQueue parentObject, CloudQueueClient client,
                    OperationContext context) throws Exception {
                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_OK) {
                    this.setNonExceptionedRetryableFailure(true);
                    return null;
                }

                return new QueuePermissions();
            }

            @Override
            public QueuePermissions postProcessResponse(HttpURLConnection connection, CloudQueue queue,
                    CloudQueueClient client, OperationContext context, QueuePermissions queuePermissions)
                    throws Exception {
                HashMap<String, SharedAccessQueuePolicy> accessIds = SharedAccessPolicyHandler.getAccessIdentifiers(
                        this.getConnection().getInputStream(), SharedAccessQueuePolicy.class);

                for (final String key : accessIds.keySet()) {
                    queuePermissions.getSharedAccessPolicies().put(key, accessIds.get(key));
                }

                return queuePermissions;
            }
        };

        return getRequest;
    }

    /**
     * Returns a shared access signature for the queue.
     * 
     * @param policy
     *            The access policy for the shared access signature.
     * @param groupPolicyIdentifier
     *            A queue-level access policy.
     *            
     * @return A shared access signature for the queue.
     * 
     * @throws InvalidKeyException
     *             If an invalid key was passed.
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws IllegalArgumentException
     *             If an unexpected value is passed.
     */
    public String generateSharedAccessSignature(final SharedAccessQueuePolicy policy, final String groupPolicyIdentifier)
            throws InvalidKeyException, StorageException {

        return this.generateSharedAccessSignature(policy, groupPolicyIdentifier, null /* IP range */, null /* protocols */);
    }

    /**
     * Returns a shared access signature for the queue.
     * 
     * @param policy
     *            The access policy for the shared access signature.
     * @param groupPolicyIdentifier
     *            A queue-level access policy.
     * @param ipRange
     *            A {@link IPRange} object containing the range of allowed IP addresses.
     * @param protocols
     *            A {@link SharedAccessProtocols} representing the allowed Internet protocols.
     *            
     * @return A shared access signature for the queue.
     * 
     * @throws InvalidKeyException
     *             If an invalid key was passed.
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws IllegalArgumentException
     *             If an unexpected value is passed.
     */
    public String generateSharedAccessSignature(
            final SharedAccessQueuePolicy policy, final String groupPolicyIdentifier, final IPRange ipRange,
            final SharedAccessProtocols protocols)
            throws InvalidKeyException, StorageException {

        if (!StorageCredentialsHelper.canCredentialsSignRequest(this.queueServiceClient.getCredentials())) {
            final String errorMessage = SR.CANNOT_CREATE_SAS_WITHOUT_ACCOUNT_KEY;
            throw new IllegalArgumentException(errorMessage);
        }

        final String resourceName = this.getSharedAccessCanonicalName();

        final String signature = SharedAccessSignatureHelper.generateSharedAccessSignatureHashForQueue(
                policy, groupPolicyIdentifier, resourceName, ipRange, protocols, this.queueServiceClient);

        final UriQueryBuilder builder = SharedAccessSignatureHelper.generateSharedAccessSignatureForQueue(
                policy, groupPolicyIdentifier, ipRange, protocols, signature);

        return builder.toString();
    }

    /**
     * Returns the canonical name for shared access.
     * 
     * @return the canonical name for shared access.
     */
    private String getSharedAccessCanonicalName() {
        String accountName = this.getServiceClient().getCredentials().getAccountName();
        String queueName = this.getName();

        return String.format("/%s/%s/%s", SR.QUEUE, accountName, queueName);
    }

    /**
     * Returns the transformed URI for the resource if the given credentials require transformation.
     * 
     * @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 <code>java.net.URI</code> object that represents the transformed URI.
     * 
     * @throws IllegalArgumentException
     *             If the URI is not absolute.
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws URISyntaxException
     *             If the resource URI is invalid.
     */
    private final StorageUri getTransformedAddress(final OperationContext opContext) throws URISyntaxException,
            StorageException {
        return this.queueServiceClient.getCredentials().transformUri(this.getStorageUri(), opContext);
    }
    
    /**
     * Verifies the passed in URI. Then parses it and uses its components to populate this resource's properties.
     * 
     * @param completeUri
     *            A {@link StorageUri} object which represents the complete URI.
     * @param credentials
     *            A {@link StorageCredentials} object used to authenticate access.
     * @throws StorageException
     *             If a storage service error occurred.
     */
    private void parseQueryAndVerify(final StorageUri completeUri, final StorageCredentials credentials) 
            throws StorageException {
        Utility.assertNotNull("completeUri", completeUri);

        if (!completeUri.isAbsolute()) {
            throw new IllegalArgumentException(String.format(SR.RELATIVE_ADDRESS_NOT_PERMITTED, completeUri.toString()));
        }

        this.storageUri = PathUtility.stripURIQueryAndFragment(completeUri);
        
        final StorageCredentialsSharedAccessSignature parsedCredentials = 
                SharedAccessSignatureHelper.parseQuery(completeUri);

        if (credentials != null && parsedCredentials != null) {
            throw new IllegalArgumentException(SR.MULTIPLE_CREDENTIALS_PROVIDED);
        }

        try {
            final boolean usePathStyleUris = Utility.determinePathStyleFromUri(this.storageUri.getPrimaryUri());
            this.queueServiceClient = new CloudQueueClient(PathUtility.getServiceClientBaseAddress(
                    this.getStorageUri(), usePathStyleUris), credentials != null ? credentials : parsedCredentials);
            this.name = PathUtility.getContainerNameFromUri(storageUri.getPrimaryUri(), usePathStyleUris);
        }
        catch (final URISyntaxException e) {
            throw Utility.generateNewUnexpectedStorageException(e);
        }
    }
} public final class CloudTable {
    /**
     * The name of the table.
     */
    private String name;

    /**
     * Holds the list of URIs for all locations.
     */
    private StorageUri storageUri;

    /**
     * A reference to the associated service client.
     */
    private CloudTableClient tableServiceClient;

    /**
     * Gets the name of the table.
     *
     * @return A <code>String</code> object that represents the name of the table.
     */
    public String getName() {
        return this.name;
    }

    /**
     * Gets the table service client associated with this queue.
     *
     * @return A {@link CloudTableClient} object that represents the service client associated with this table.
     */
    public CloudTableClient getServiceClient() {
        return this.tableServiceClient;
    }

    /**
     * Returns the list of URIs for all locations.
     *
     * @return A {@link StorageUri} that represents the list of URIs for all locations..
     */
    public final StorageUri getStorageUri() {
        return this.storageUri;
    }

    /**
     * Gets the absolute URI for this table.
     *
     * @return A <code>java.net.URI</code> object that represents the URI for this table.
     */
    public URI getUri() {
        return this.storageUri.getPrimaryUri();
    }

    /**
     * Creates an instance of the <code>CloudTable</code> class using the specified table URI. The table URI must
     * include a SAS token.
     *
     * @param uri
     *            A <code>java.net.URI</code> object that represents the absolute URI of the table.
     *
     * @throws StorageException
     *             If a storage service error occurred.
     */
    public CloudTable(final URI uri) throws StorageException {
        this(new StorageUri(uri, null));
    }

    /**
     * Creates an instance of the <code>CloudTable</code> class using the specified table URI. The table URI must
     * include a SAS token.
     *
     * @param uri
     *            A {@link StorageUri} object that represents the absolute URI of the table.
     *
     * @throws StorageException
     *             If a storage service error occurred.
     */
    public CloudTable(final StorageUri uri) throws StorageException {
        this(uri, (StorageCredentials)null);
    }
    
    /**
     * Creates an instance of the <code>CloudTable</code> class using the specified table URI and credentials.
     *
     * @param uri
     *            A <code>java.net.URI</code> object that represents the absolute URI of the table.
     * @param credentials
     *            A {@link StorageCredentials} object used to authenticate access.
     *
     * @throws StorageException
     *             If a storage service error occurred.
     */
    public CloudTable(final URI uri, final StorageCredentials credentials) throws StorageException {
        this(new StorageUri(uri, null), credentials);
    }

    /**
     * Creates an instance of the <code>CloudTable</code> class using the specified table StorageUri and credentials.
     *
     * @param uri
     *            A {@link StorageUri} object that represents the absolute StorageUri of the table.
     * @param credentials
     *            A {@link StorageCredentials} object used to authenticate access.
     *
     * @throws StorageException
     *             If a storage service error occurred.
     */
    public CloudTable(final StorageUri uri, final StorageCredentials credentials) throws StorageException {
        this.parseQueryAndVerify(uri, credentials);
    }

    /**
     * Creates an instance of the <code>CloudTable</code> class using the specified name and client.
     *
     * @param tableName
     *            A <code>String</code> which represents the name of the table, which must adhere to table naming rules.
     *            The table name should not include any path separator characters (/).
     *            Table names are case insensitive, must be unique within an account and must be between 3-63 characters
     *            long. Table names must start with an cannot begin with a numeric character and may only contain
     *            alphanumeric characters. Some table names are reserved, including "table".
     * @param client
     *            A {@link CloudTableClient} object that represents the associated service client, and that specifies
     *            the endpoint for the Table service.
     *
     * @throws URISyntaxException
     *             If the resource URI constructed based on the tableName is invalid.
     * @throws StorageException
     *             If a storage service error occurred.
     * @see <a href="http://msdn.microsoft.com/library/azure/dd179338.aspx">Understanding the Table Service Data
     *      Model</a>
     */
    protected CloudTable(final String tableName, final CloudTableClient client) throws URISyntaxException,
    StorageException {
        Utility.assertNotNull("client", client);
        Utility.assertNotNull("tableName", tableName);

        this.storageUri = PathUtility.appendPathToUri(client.getStorageUri(), tableName);
        this.name = tableName;
        this.tableServiceClient = client;
    }

    /**
     * Creates the table in the storage service with default request options.
     * <p>
     * This method invokes the <a href="http://msdn.microsoft.com/en-us/library/azure/dd135729.aspx">Create Table</a>
     * REST API to create the specified table, using the Table service endpoint and storage account credentials of this
     * instance.
     *
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public void create() throws StorageException {
        this.create(null /* options */, null /* opContext */);
    }

    /**
     * Creates the table in the storage service, using the specified {@link TableRequestOptions} and
     * {@link OperationContext}.
     * <p>
     * This method invokes the <a href="http://msdn.microsoft.com/en-us/library/azure/dd135729.aspx">Create Table</a>
     * REST API to create the specified table, using the Table service endpoint and storage account credentials of this
     * instance.
     *
     * Use the {@link TableRequestOptions} to override execution options such as the timeout or retry policy for the
     * operation.
     *
     * @param options
     *            A {@link TableRequestOptions} object that specifies execution options such as retry policy and timeout
     *            settings for the operation. Specify <code>null</code> to use the request options specified on the
     *            {@link CloudTableClient}.
     * @param opContext
     *            An {@link OperationContext} object for tracking the current operation. Specify <code>null</code> to
     *            safely ignore operation context.
     *
     * @throws StorageException
     *             If an error occurs accessing the storage service, or because the table cannot be
     *             created, or already exists.
     */
    @DoesServiceRequest
    public void create(TableRequestOptions options, OperationContext opContext) throws StorageException {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        opContext.initialize();
        options = TableRequestOptions.populateAndApplyDefaults(options, this.tableServiceClient);

        Utility.assertNotNullOrEmpty("tableName", this.name);

        final DynamicTableEntity tableEntry = new DynamicTableEntity();
        tableEntry.getProperties().put(TableConstants.TABLE_NAME, new EntityProperty(this.name));

        TableOperation.insert(tableEntry).execute(this.tableServiceClient, TableConstants.TABLES_SERVICE_TABLES_NAME,
                options, opContext);
    }

    /**
     * Creates the table in the storage service using default request options if it does not already exist.
     *
     * @return <code>true</code> if the table is created in the storage service; otherwise <code>false</code>.
     *
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public boolean createIfNotExists() throws StorageException {
        return this.createIfNotExists(null /* options */, null /* opContext */);
    }

    /**
     * Creates the table in the storage service with the specified request options and operation context, if it does not
     * already exist.
     *
     * @param options
     *            A {@link TableRequestOptions} 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 CloudTableClient}).
     * @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 <code>true</code> if the table did not already exist and was created; otherwise <code>false</code> .
     *
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public boolean createIfNotExists(TableRequestOptions options, OperationContext opContext) throws StorageException {
        options = TableRequestOptions.populateAndApplyDefaults(options, this.tableServiceClient);

        boolean exists = this.exists(true, options, opContext);
        if (exists) {
            return false;
        }
        else {
            try {
                this.create(options, opContext);
                return true;
            }
            catch (StorageException e) {
                if (e.getHttpStatusCode() == HttpURLConnection.HTTP_CONFLICT
                        && StorageErrorCodeStrings.TABLE_ALREADY_EXISTS.equals(e.getErrorCode())) {
                    return false;
                }
                else {
                    throw e;
                }
            }
        }
    }

    /**
     * Deletes the table from the storage service.
     *
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public void delete() throws StorageException {
        this.delete(null /* options */, null /* opContext */);
    }

    /**
     * Deletes the table from the storage service, using the specified request options and operation context.
     *
     * @param options
     *            A {@link TableRequestOptions} 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 CloudTableClient}).
     * @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
    public void delete(TableRequestOptions options, OperationContext opContext) throws StorageException {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        opContext.initialize();
        options = TableRequestOptions.populateAndApplyDefaults(options, this.tableServiceClient);

        Utility.assertNotNullOrEmpty("tableName", this.name);
        final DynamicTableEntity tableEntry = new DynamicTableEntity();
        tableEntry.getProperties().put(TableConstants.TABLE_NAME, new EntityProperty(this.name));

        TableOperation deleteOp = new TableOperation(tableEntry, TableOperationType.DELETE);
        deleteOp.execute(this.tableServiceClient, TableConstants.TABLES_SERVICE_TABLES_NAME, options, opContext);
    }

    /**
     * Deletes the table from the storage service, if it exists.
     *
     * @return <code>true</code> if the table existed in the storage service and has been deleted; otherwise
     *         <code>false</code>.
     *
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public boolean deleteIfExists() throws StorageException {
        return this.deleteIfExists(null /* options */, null /* opContext */);
    }

    /**
     * Deletes the table from the storage service using the specified request options and operation context, if it
     * exists.
     *
     * @param options
     *            A {@link TableRequestOptions} 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 CloudTableClient}).
     * @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 value of <code>true</code> if the table existed in the storage service and has been deleted, otherwise
     *         <code>false</code>.
     *
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public boolean deleteIfExists(TableRequestOptions options, OperationContext opContext) throws StorageException {
        options = TableRequestOptions.populateAndApplyDefaults(options, this.tableServiceClient);

        if (this.exists(true, options, opContext)) {
            try {
                this.delete(options, opContext);
            }
            catch (StorageException ex) {
                if (ex.getHttpStatusCode() == HttpURLConnection.HTTP_NOT_FOUND
                        && StorageErrorCodeStrings.RESOURCE_NOT_FOUND.equals(ex.getErrorCode())) {
                    return false;
                }
                else {
                    throw ex;
                }
            }
            return true;
        }
        else {
            return false;
        }
    }

    /**
     * Executes the specified batch operation on a table as an atomic operation. A batch operation may contain up to 100
     * individual table operations, with the requirement that each operation entity must have same partition key. Only
     * one retrieve operation is allowed per batch. Note that the total payload of a batch operation is limited to 4MB.
     * <p>
     * This method invokes an <a href="http://msdn.microsoft.com/en-us/library/azure/dd894038.aspx">Entity Group
     * Transaction</a> on the REST API to execute the specified batch operation on the table as an atomic unit, using
     * the Table service endpoint and storage account credentials of this instance.
     *
     * @param batch
     *            The {@link TableBatchOperation} object representing the operations to execute on the table.
     *
     * @return
     *         A <code>java.util.ArrayList</code> of {@link TableResult} that contains the results, in order, of
     *         each {@link TableOperation} in the {@link TableBatchOperation} on the named table.
     *
     * @throws StorageException
     *             if an error occurs accessing the storage service, or the operation fails.
     */
    @DoesServiceRequest
    public ArrayList<TableResult> execute(final TableBatchOperation batch) throws StorageException {
        return this.execute(batch, null /* options */, null /* opContext */);
    }

    /**
     * Executes the specified batch operation on a table as an atomic operation, using the specified
     * {@link TableRequestOptions} and {@link OperationContext}. A batch operation may contain up to 100 individual
     * table operations, with the requirement that each operation entity must have same partition key. Only one retrieve
     * operation is allowed per batch. Note that the total payload of a batch operation is limited to 4MB.
     * <p>
     * This method invokes an <a href="http://msdn.microsoft.com/en-us/library/azure/dd894038.aspx">Entity Group
     * Transaction</a> on the REST API to execute the specified batch operation on the table as an atomic unit, using
     * the Table service endpoint and storage account credentials of this instance.
     *
     * Use the {@link TableRequestOptions} to override execution options such as the timeout or retry policy for the
     * operation.
     *
     * @param batch
     *            The {@link TableBatchOperation} object representing the operations to execute on the table.
     * @param options
     *            A {@link TableRequestOptions} object that specifies execution options such as retry policy and timeout
     *            settings for the operation. Specify <code>null</code> to use the request options specified on the
     *            {@link CloudTableClient}.
     * @param opContext
     *            An {@link OperationContext} object for tracking the current operation. Specify <code>null</code> to
     *            safely ignore operation context.
     *
     * @return
     *         A <code>java.util.ArrayList</code> of {@link TableResult} that contains the results, in order, of
     *         each {@link TableOperation} in the {@link TableBatchOperation} on the named table.
     *
     * @throws StorageException
     *             if an error occurs accessing the storage service, or the operation fails.
     */
    @DoesServiceRequest
    public ArrayList<TableResult> execute(final TableBatchOperation batch, TableRequestOptions options,
            OperationContext opContext) throws StorageException {
        Utility.assertNotNull("batch", batch);
        if (opContext == null) {
            opContext = new OperationContext();
        }

        opContext.initialize();
        options = TableRequestOptions.populateAndApplyDefaults(options, this.getServiceClient());
        return batch.execute(this.getServiceClient(), this.getName(), options, opContext);
    }

    /**
     * Executes the operation on a table.
     * <p>
     * This method will invoke the <a href="http://msdn.microsoft.com/en-us/library/azure/dd179423.aspx">Table Service
     * REST API</a> to execute the specified operation on the table, using the Table service endpoint and storage
     * account credentials of this instance.
     *
     * @param operation
     *            The {@link TableOperation} object representing the operation to execute on the table.
     *
     * @return
     *         A {@link TableResult} containing the result of executing the {@link TableOperation} on the table.
     *
     * @throws StorageException
     *             if an error occurs accessing the storage service, or the operation fails.
     */
    @DoesServiceRequest
    public TableResult execute(final TableOperation operation) throws StorageException {
        return this.execute(operation, null /* options */, null /* opContext */);
    }

    /**
     * Executes the operation on a table, using the specified {@link TableRequestOptions} and {@link OperationContext}.
     * <p>
     * This method will invoke the <a href="http://msdn.microsoft.com/en-us/library/azure/dd179423.aspx">Table Service
     * REST API</a> to execute the specified operation on the table, using the Table service endpoint and storage
     * account credentials of this instance.
     *
     * Use the {@link TableRequestOptions} to override execution options such as the timeout or retry policy for the
     * operation.
     *
     * @param operation
     *            The {@link TableOperation} object representing the operation to execute on the table.
     * @param options
     *            A {@link TableRequestOptions} object that specifies execution options such as retry policy and timeout
     *            settings for the operation. Specify <code>null</code> to use the request options specified on the
     *            {@link CloudTableClient}.
     * @param opContext
     *            An {@link OperationContext} object for tracking the current operation. Specify <code>null</code> to
     *            safely ignore operation context.
     *
     * @return
     *         A {@link TableResult} containing the result of executing the {@link TableOperation} on the table.
     *
     * @throws StorageException
     *             if an error occurs accessing the storage service, or the operation fails.
     */
    @DoesServiceRequest
    public TableResult execute(final TableOperation operation, final TableRequestOptions options,
            final OperationContext opContext) throws StorageException {
        Utility.assertNotNull("operation", operation);
        return operation.execute(this.getServiceClient(), this.getName(), options, opContext);
    }

    /**
     * Executes a query, applying the specified {@link EntityResolver} to the result.
     * <p>
     * This method will invoke a <a href="http://msdn.microsoft.com/en-us/library/azure/dd179421.aspx">Query
     * Entities</a> operation on the <a href="http://msdn.microsoft.com/en-us/library/azure/dd179423.aspx">Table Service
     * REST API</a> to query the table, using the Table service endpoint and storage account credentials of this
     * instance.
     *
     * @param query
     *            A {@link TableQuery} instance specifying the table to query and the query parameters to use.
     * @param resolver
     *            An {@link EntityResolver} instance which creates a projection of the table query result entities into
     *            the specified type <code>R</code>.
     *
     * @return
     *         A collection implementing the <code>Iterable</code> interface containing the projection into type
     *         <code>R</code> of the results of executing the query.
     */
    @DoesServiceRequest
    public <R> Iterable<R> execute(final TableQuery<?> query, final EntityResolver<R> resolver) {
        return this.execute(query, resolver, null /* options */, null /* opContext */);
    }

    /**
     * Executes a query, applying the specified {@link EntityResolver} to the result, using the
     * specified {@link TableRequestOptions} and {@link OperationContext}.
     * <p>
     * This method will invoke a <a href="http://msdn.microsoft.com/en-us/library/azure/dd179421.aspx">Query
     * Entities</a> operation on the <a href="http://msdn.microsoft.com/en-us/library/azure/dd179423.aspx">Table Service
     * REST API</a> to query the table, using the Table service endpoint and storage account credentials of this
     * instance.
     *
     * Use the {@link TableRequestOptions} to override execution options such as the timeout or retry policy for the
     * operation.
     *
     * @param query
     *            A {@link TableQuery} instance specifying the table to query and the query parameters to use.
     * @param resolver
     *            An {@link EntityResolver} instance which creates a projection of the table query result entities into
     *            the specified type <code>R</code>.
     * @param options
     *            A {@link TableRequestOptions} object that specifies execution options such as retry policy and timeout
     *            settings for the operation. Specify <code>null</code> to use the request options specified on the
     *            {@link CloudTableClient}.
     * @param opContext
     *            An {@link OperationContext} object for tracking the current operation. Specify <code>null</code> to
     *            safely ignore operation context.
     *
     * @return
     *         A collection implementing the <code>Iterable</code> interface containing the projection into type
     *         <code>R</code> of the results of executing the query.
     */
    @DoesServiceRequest
    @SuppressWarnings({ "unchecked" })
    public <R> Iterable<R> execute(final TableQuery<?> query, final EntityResolver<R> resolver,
            final TableRequestOptions options, final OperationContext opContext) {
        Utility.assertNotNull("query", query);
        Utility.assertNotNull(SR.QUERY_REQUIRES_VALID_CLASSTYPE_OR_RESOLVER, resolver);
        query.setSourceTableName(this.getName());
        return (Iterable<R>) this.getServiceClient().generateIteratorForQuery(query, resolver, options, opContext);
    }

    /**
     * Executes a query.
     * <p>
     * This method will invoke a <a href="http://msdn.microsoft.com/en-us/library/azure/dd179421.aspx">Query
     * Entities</a> operation on the <a href="http://msdn.microsoft.com/en-us/library/azure/dd179423.aspx">Table Service
     * REST API</a> to query the table, using the Table service endpoint and storage account credentials of this
     * instance.
     *
     * @param query
     *            A {@link TableQuery} instance specifying the table to query and the query parameters to use,
     *            specialized for a type T implementing {@link TableEntity}.
     *
     * @return
     *         A collection implementing the <code>Iterable</code> interface specialized for type T of the results of
     *         executing the query.
     */
    @DoesServiceRequest
    public <T extends TableEntity> Iterable<T> execute(final TableQuery<T> query) {
        return this.execute(query, null /* options */, null /* opContext */);
    }

    /**
     * Executes a query, using the specified {@link TableRequestOptions} and {@link OperationContext}.
     * <p>
     * This method will invoke a <a href="http://msdn.microsoft.com/en-us/library/azure/dd179421.aspx">Query
     * Entities</a> operation on the <a href="http://msdn.microsoft.com/en-us/library/azure/dd179423.aspx">Table Service
     * REST API</a> to query the table, using the Table service endpoint and storage account credentials of this
     * instance.
     *
     * Use the {@link TableRequestOptions} to override execution options such as the timeout or retry policy for the
     * operation.
     *
     * @param query
     *            A {@link TableQuery} instance specifying the table to query and the query parameters to use,
     *            specialized for a type T implementing {@link TableEntity}.
     * @param options
     *            A {@link TableRequestOptions} object that specifies execution options such as retry policy and timeout
     *            settings for the operation. Specify <code>null</code> to use the request options specified on the
     *            {@link CloudTableClient}.
     * @param opContext
     *            An {@link OperationContext} object for tracking the current operation. Specify <code>null</code> to
     *            safely ignore operation context.
     *
     * @return
     *         A collection implementing the <code>Iterable</code> interface specialized for type T of the results of
     *         executing the query.
     */
    @SuppressWarnings({ "unchecked" })
    @DoesServiceRequest
    public <T extends TableEntity> Iterable<T> execute(final TableQuery<T> query, final TableRequestOptions options,
            final OperationContext opContext) {
        Utility.assertNotNull("query", query);
        Utility.assertNotNull(SR.QUERY_REQUIRES_VALID_CLASSTYPE_OR_RESOLVER, query.getClazzType());
        query.setSourceTableName(this.getName());
        return (Iterable<T>) this.getServiceClient().generateIteratorForQuery(query, null, options, opContext);
    }

    /**
     * Executes a query in segmented mode with the specified {@link ResultContinuation} continuation token,
     * applying the {@link EntityResolver} to the result.
     * Executing a query with <code>executeSegmented</code> allows the query to be resumed after returning partial
     * results, using information returned by the server in the {@link ResultSegment} object.
     * <p>
     * This method will invoke a <a href="http://msdn.microsoft.com/en-us/library/azure/dd179421.aspx">Query
     * Entities</a> operation on the <a href="http://msdn.microsoft.com/en-us/library/azure/dd179423.aspx">Table Service
     * REST API</a> to query the table, using the Table service endpoint and storage account credentials of this
     * instance.
     *
     * @param query
     *            A {@link TableQuery} instance specifying the table to query and the query parameters to use.
     * @param resolver
     *            An {@link EntityResolver} instance which creates a projection of the table query result entities into
     *            the specified type <code>R</code>.
     * @param continuationToken
     *            A {@link ResultContinuation} object representing a continuation token from the server when the
     *            operation returns a partial result. Specify <code>null</code> on the initial call. Call the
     *            {@link ResultSegment#getContinuationToken()} method on the result to obtain the
     *            {@link ResultContinuation} object to use in the next call to resume the query.
     *
     * @return
     *         A {@link ResultSegment} containing the projection into type <code>R</code> of the results of executing
     *         the query.
     *
     * @throws StorageException
     *             if a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public <R> ResultSegment<R> executeSegmented(final TableQuery<?> query, final EntityResolver<R> resolver,
            final ResultContinuation continuationToken) throws StorageException {
        return this.executeSegmented(query, resolver, continuationToken, null /* options */, null /* opContext */);
    }

    /**
     * Executes a query in segmented mode with the specified {@link ResultContinuation} continuation token,
     * using the specified {@link TableRequestOptions} and {@link OperationContext}, applying the {@link EntityResolver}
     * to the result.
     * Executing a query with <code>executeSegmented</code> allows the query to be resumed after returning partial
     * results, using information returned by the server in the {@link ResultSegment} object.
     * <p>
     * This method will invoke a <a href="http://msdn.microsoft.com/en-us/library/azure/dd179421.aspx">Query
     * Entities</a> operation on the <a href="http://msdn.microsoft.com/en-us/library/azure/dd179423.aspx">Table Service
     * REST API</a> to query the table, using the Table service endpoint and storage account credentials of this
     * instance.
     *
     * Use the {@link TableRequestOptions} to override execution options such as the timeout or retry policy for the
     * operation.
     *
     * @param query
     *            A {@link TableQuery} instance specifying the table to query and the query parameters to use.
     * @param resolver
     *            An {@link EntityResolver} instance which creates a projection of the table query result entities into
     *            the specified type <code>R</code>.
     * @param continuationToken
     *            A {@link ResultContinuation} object representing a continuation token from the server when the
     *            operation returns a partial result. Specify <code>null</code> on the initial call. Call the
     *            {@link ResultSegment#getContinuationToken()} method on the result to obtain the
     *            {@link ResultContinuation} object to use in the next call to resume the query.
     * @param options
     *            A {@link TableRequestOptions} object that specifies execution options such as retry policy and timeout
     *            settings for the operation. Specify <code>null</code> to use the request options specified on the
     *            {@link CloudTableClient}.
     * @param opContext
     *            An {@link OperationContext} object for tracking the current operation. Specify <code>null</code> to
     *            safely ignore operation context.
     *
     * @return
     *         A {@link ResultSegment} containing the projection into type <code>R</code> of the results of executing
     *         the query.
     *
     * @throws StorageException
     *             if a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    @SuppressWarnings({ "unchecked" })
    public <R> ResultSegment<R> executeSegmented(final TableQuery<?> query, final EntityResolver<R> resolver,
            final ResultContinuation continuationToken, final TableRequestOptions options,
            final OperationContext opContext) throws StorageException {
        Utility.assertNotNull(SR.QUERY_REQUIRES_VALID_CLASSTYPE_OR_RESOLVER, resolver);
        query.setSourceTableName(this.getName());
        return (ResultSegment<R>) this.getServiceClient().executeQuerySegmentedImpl(query, resolver, continuationToken,
                options, opContext);
    }

    /**
     * Executes a query in segmented mode with a {@link ResultContinuation} continuation token.
     * Executing a query with <code>executeSegmented</code> allows the query to be resumed after returning partial
     * results, using information returned by the server in the {@link ResultSegment} object.
     * <p>
     * This method will invoke a <a href="http://msdn.microsoft.com/en-us/library/azure/dd179421.aspx">Query
     * Entities</a> operation on the <a href="http://msdn.microsoft.com/en-us/library/azure/dd179423.aspx">Table Service
     * REST API</a> to query the table, using the Table service endpoint and storage account credentials of this
     * instance.
     *
     * @param query
     *            A {@link TableQuery} instance specifying the table to query and the query parameters to use,
     *            specialized for a type T implementing {@link TableEntity}.
     * @param continuationToken
     *            A {@link ResultContinuation} object representing a continuation token from the server when the
     *            operation returns a partial result. Specify <code>null</code> on the initial call. Call the
     *            {@link ResultSegment#getContinuationToken()} method on the result to obtain the
     *            {@link ResultContinuation} object to use in the next call to resume the query.
     *
     * @return
     *         A {@link ResultSegment} specialized for type T of the results of executing the query.
     *
     * @throws StorageException
     *             if a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public <T extends TableEntity> ResultSegment<T> executeSegmented(final TableQuery<T> query,
            final ResultContinuation continuationToken) throws StorageException {
        return this.executeSegmented(query, continuationToken, null /* options */, null /* opContext */);
    }

    /**
     * Executes a query in segmented mode with a {@link ResultContinuation} continuation token,
     * using the specified {@link TableRequestOptions} and {@link OperationContext}.
     * Executing a query with <code>executeSegmented</code> allows the query to be resumed after returning partial
     * results, using information returned by the server in the {@link ResultSegment} object.
     * <p>
     * This method will invoke a <a href="http://msdn.microsoft.com/en-us/library/azure/dd179421.aspx">Query
     * Entities</a> operation on the <a href="http://msdn.microsoft.com/en-us/library/azure/dd179423.aspx">Table Service
     * REST API</a> to query the table, using the Table service endpoint and storage account credentials of this
     * instance.
     *
     * Use the {@link TableRequestOptions} to override execution options such as the timeout or retry policy for the
     * operation.
     *
     * @param query
     *            A {@link TableQuery} instance specifying the table to query and the query parameters to use,
     *            specialized for a type T implementing {@link TableEntity}.
     * @param continuationToken
     *            A {@link ResultContinuation} object representing a continuation token from the server when the
     *            operation returns a partial result. Specify <code>null</code> on the initial call. Call the
     *            {@link ResultSegment#getContinuationToken()} method on the result to obtain the
     *            {@link ResultContinuation} object to use in the next call to resume the query.
     * @param options
     *            A {@link TableRequestOptions} object that specifies execution options such as retry policy and timeout
     *            settings for the operation. Specify <code>null</code> to use the request options specified on the
     *            {@link CloudTableClient}.
     * @param opContext
     *            An {@link OperationContext} object for tracking the current operation. Specify <code>null</code> to
     *            safely ignore operation context.
     *
     * @return
     *         A {@link ResultSegment} specialized for type T of the results of executing the query.
     *
     * @throws StorageException
     *             if a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    @SuppressWarnings({ "unchecked" })
    public <T extends TableEntity> ResultSegment<T> executeSegmented(final TableQuery<T> query,
            final ResultContinuation continuationToken, final TableRequestOptions options,
            final OperationContext opContext) throws StorageException {
        Utility.assertNotNull("query", query);
        query.setSourceTableName(this.getName());
        return (ResultSegment<T>) this.getServiceClient().executeQuerySegmentedImpl(query, null, continuationToken,
                options, opContext);
    }

    /**
     * Returns a value that indicates whether the table exists in the storage service.
     *
     * @return <code>true</code> if the table exists in the storage service; otherwise <code>false</code>.
     *
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public boolean exists() throws StorageException {
        return this.exists(null /* options */, null /* opContext */);
    }

    /**
     * Returns a value that indicates whether the table exists in the storage service, using the specified request
     * options and operation context.
     *
     * @param options
     *            A {@link TableRequestOptions} 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 CloudTableClient}).
     * @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 <code>true</code> if the table exists in the storage service, otherwise <code>false</code>.
     *
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public boolean exists(TableRequestOptions options, OperationContext opContext) throws StorageException {
        return this.exists(false, options, opContext);
    }

    /**
     * Returns a value that indicates whether the table exists in the storage service, using the specified request
     * options and operation context.
     *
     * @param options
     *            A {@link TableRequestOptions} 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 CloudTableClient}).
     * @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 <code>true</code> if the table exists in the storage service, otherwise <code>false</code>.
     *
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    private boolean exists(final boolean primaryOnly, TableRequestOptions options, OperationContext opContext)
            throws StorageException {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        opContext.initialize();
        options = TableRequestOptions.populateAndApplyDefaults(options, this.tableServiceClient);

        Utility.assertNotNullOrEmpty("tableName", this.name);

        QueryTableOperation operation = (QueryTableOperation) TableOperation.retrieve(this.name /* Used As PK */,
                null/* Row Key */, DynamicTableEntity.class);
        operation.setPrimaryOnlyRetrieve(primaryOnly);

        final TableResult result = operation.execute(this.tableServiceClient,
                TableConstants.TABLES_SERVICE_TABLES_NAME, options, opContext);

        if (result.getHttpStatusCode() == HttpURLConnection.HTTP_OK) {
            return true;
        }
        else if (result.getHttpStatusCode() == HttpURLConnection.HTTP_NOT_FOUND) {
            return false;
        }
        else {
            throw new StorageException(StorageErrorCodeStrings.OUT_OF_RANGE_INPUT, SR.UNEXPECTED_STATUS_CODE_RECEIVED,
                    result.getHttpStatusCode(), null /* extendedErrorInfo */, null /* innerException */);
        }
    }

    /**
     * Uploads the table's permissions.
     *
     * @param permissions
     *            A {@link TablePermissions} object that represents the permissions to upload.
     *
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public void uploadPermissions(final TablePermissions permissions) throws StorageException {
        this.uploadPermissions(permissions, null /* options */, null /* opContext */);
    }

    /**
     * Uploads the table's permissions using the specified request options and operation context.
     *
     * @param permissions
     *            A {@link TablePermissions} object that represents the permissions to upload.
     * @param options
     *            A {@link TableRequestOptions} 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 CloudTableClient}).
     * @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.
     */
    @DoesServiceRequest
    public void uploadPermissions(final TablePermissions permissions, TableRequestOptions options,
            OperationContext opContext) throws StorageException {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        opContext.initialize();
        options = TableRequestOptions.populateAndApplyDefaults(options, this.tableServiceClient);

        ExecutionEngine.executeWithRetry(this.tableServiceClient, this,
                this.uploadPermissionsImpl(permissions, options), options.getRetryPolicyFactory(), opContext);
    }

    private StorageRequest<CloudTableClient, CloudTable, Void> uploadPermissionsImpl(
            final TablePermissions permissions, final TableRequestOptions options) throws StorageException {
        final StringWriter outBuffer = new StringWriter();

        try {
            SharedAccessPolicySerializer.writeSharedAccessIdentifiersToStream(permissions.getSharedAccessPolicies(),
                    outBuffer);
            final byte[] aclBytes = outBuffer.toString().getBytes(Constants.UTF8_CHARSET);

            final StorageRequest<CloudTableClient, CloudTable, Void> putRequest = new StorageRequest<CloudTableClient, CloudTable, Void>(
                    options, this.getStorageUri()) {

                @Override
                public HttpURLConnection buildRequest(CloudTableClient client, CloudTable table,
                        OperationContext context) throws Exception {
                    this.setSendStream(new ByteArrayInputStream(aclBytes));
                    this.setLength((long) aclBytes.length);
                    return TableRequest.setAcl(table.getStorageUri().getUri(this.getCurrentLocation()), options,
                            context);
                }

                @Override
                public void signRequest(HttpURLConnection connection, CloudTableClient client, OperationContext context)
                        throws Exception {
                    StorageRequest.signTableRequest(connection, client, aclBytes.length, context);
                }

                @Override
                public Void preProcessResponse(CloudTable parentObject, CloudTableClient client,
                        OperationContext context) throws Exception {
                    if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_NO_CONTENT) {
                        this.setNonExceptionedRetryableFailure(true);
                    }

                    return null;
                }
                
                @Override
                public StorageExtendedErrorInformation parseErrorDetails() {
                    return TableStorageErrorDeserializer.parseErrorDetails(this);
                }
            };

            return putRequest;
        }
        catch (IllegalArgumentException e) {
            // The request was not even made. There was an error while trying to write the message. Just throw.
            StorageException translatedException = StorageException.translateClientException(e);
            throw translatedException;
        }
        catch (IllegalStateException e) {
            // The request was not even made. There was an error while trying to write the message. Just throw.
            StorageException translatedException = StorageException.translateClientException(e);
            throw translatedException;
        }
        catch (IOException e) {
            // The request was not even made. There was an error while trying to write the message. Just throw.
            StorageException translatedException = StorageException.translateClientException(e);
            throw translatedException;
        }
    }

    /**
     * Downloads the permission settings for the table.
     *
     * @return A {@link TablePermissions} object that represents the container's permissions.
     *
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public TablePermissions downloadPermissions() throws StorageException {
        return this.downloadPermissions(null /* options */, null /* opContext */);
    }

    /**
     * Downloads the permissions settings for the table using the specified request options and operation context.
     *
     * @param options
     *            A {@link TableRequestOptions} 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 CloudTableClient}).
     * @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 TablePermissions} object that represents the table's permissions.
     *
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public TablePermissions downloadPermissions(TableRequestOptions options, OperationContext opContext)
            throws StorageException {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        opContext.initialize();
        options = TableRequestOptions.populateAndApplyDefaults(options, this.tableServiceClient);

        return ExecutionEngine.executeWithRetry(this.tableServiceClient, this, this.downloadPermissionsImpl(options),
                options.getRetryPolicyFactory(), opContext);
    }

    private StorageRequest<CloudTableClient, CloudTable, TablePermissions> downloadPermissionsImpl(
            final TableRequestOptions options) {
        final StorageRequest<CloudTableClient, CloudTable, TablePermissions> getRequest = new StorageRequest<CloudTableClient, CloudTable, TablePermissions>(
                options, this.getStorageUri()) {

            @Override
            public void setRequestLocationMode() {
                this.setRequestLocationMode(RequestLocationMode.PRIMARY_OR_SECONDARY);
            }

            @Override
            public HttpURLConnection buildRequest(CloudTableClient client, CloudTable table, OperationContext context)
                    throws Exception {
                return TableRequest.getAcl(table.getStorageUri().getUri(this.getCurrentLocation()), options, context);
            }

            @Override
            public void signRequest(HttpURLConnection connection, CloudTableClient client, OperationContext context)
                    throws Exception {
                StorageRequest.signTableRequest(connection, client, -1L, context);
            }

            @Override
            public TablePermissions preProcessResponse(CloudTable parentObject, CloudTableClient client,
                    OperationContext context) throws Exception {
                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_OK) {
                    this.setNonExceptionedRetryableFailure(true);
                }

                return new TablePermissions();
            }

            @Override
            public TablePermissions postProcessResponse(HttpURLConnection connection, CloudTable table,
                    CloudTableClient client, OperationContext context, TablePermissions permissions) throws Exception {
                HashMap<String, SharedAccessTablePolicy> accessIds = SharedAccessPolicyHandler.getAccessIdentifiers(
                        this.getConnection().getInputStream(), SharedAccessTablePolicy.class);
                for (final String key : accessIds.keySet()) {
                    permissions.getSharedAccessPolicies().put(key, accessIds.get(key));
                }

                return permissions;
            }
            
            @Override
            public StorageExtendedErrorInformation parseErrorDetails() {
                return TableStorageErrorDeserializer.parseErrorDetails(this);
            }
        };

        return getRequest;
    }

    /**
     * Creates a shared access signature for the table.
     *
     * @param policy
     *            A {@link SharedAccessTablePolicy} object which represents the access policy for the shared access
     *            signature.
     * @param accessPolicyIdentifier
     *            A <code>String</code> which represents a table-level access policy.
     * @param startPartitionKey
     *            A <code>String</code> which represents the starting partition key.
     * @param startRowKey
     *            A <code>String</code> which represents the starting row key.
     * @param endPartitionKey
     *            A <code>String</code> which represents the ending partition key.
     * @param endRowKey
     *            A <code>String</code> which represents the ending end key.
     *
     * @return A <code>String</code> containing the shared access signature for the table.
     *
     * @throws InvalidKeyException
     *             If an invalid key was passed.
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws IllegalArgumentException
     *             If an unexpected value is passed.
     */
    public String generateSharedAccessSignature(final SharedAccessTablePolicy policy,
            final String accessPolicyIdentifier, final String startPartitionKey, final String startRowKey,
            final String endPartitionKey, final String endRowKey) throws InvalidKeyException, StorageException {
        
        return generateSharedAccessSignature(policy, accessPolicyIdentifier,
                startPartitionKey, startRowKey, endPartitionKey, endRowKey, null /* IP Range */, null /* Protocols */);
    }

    /**
     * Creates a shared access signature for the table.
     *
     * @param policy
     *            A {@link SharedAccessTablePolicy} object which represents the access policy for the shared access
     *            signature.
     * @param accessPolicyIdentifier
     *            A <code>String</code> which represents a table-level access policy.
     * @param startPartitionKey
     *            A <code>String</code> which represents the starting partition key.
     * @param startRowKey
     *            A <code>String</code> which represents the starting row key.
     * @param endPartitionKey
     *            A <code>String</code> which represents the ending partition key.
     * @param endRowKey
     *            A <code>String</code> which represents the ending end key.
     * @param ipRange
     *            A {@link IPRange} object containing the range of allowed IP addresses.
     * @param protocols
     *            A {@link SharedAccessProtocols} representing the allowed Internet protocols.
     *
     * @return A <code>String</code> containing the shared access signature for the table.
     *
     * @throws InvalidKeyException
     *             If an invalid key was passed.
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws IllegalArgumentException
     *             If an unexpected value is passed.
     */
    public String generateSharedAccessSignature(
            final SharedAccessTablePolicy policy, final String accessPolicyIdentifier, final String startPartitionKey,
            final String startRowKey, final String endPartitionKey, final String endRowKey, final IPRange ipRange,
            final SharedAccessProtocols protocols)
            throws InvalidKeyException, StorageException {

        if (!StorageCredentialsHelper.canCredentialsSignRequest(this.tableServiceClient.getCredentials())) {
            throw new IllegalArgumentException(SR.CANNOT_CREATE_SAS_WITHOUT_ACCOUNT_KEY);
        }

        final String resourceName = this.getSharedAccessCanonicalName();

        final String signature = SharedAccessSignatureHelper.generateSharedAccessSignatureHashForTable(
                policy, accessPolicyIdentifier, resourceName, ipRange, protocols,
                startPartitionKey, startRowKey, endPartitionKey, endRowKey, this.tableServiceClient);

        final UriQueryBuilder builder = SharedAccessSignatureHelper.generateSharedAccessSignatureForTable(
                policy, startPartitionKey, startRowKey, endPartitionKey, endRowKey, accessPolicyIdentifier,
                ipRange, protocols, this.name,  signature);

        return builder.toString();
    }

    /**
     * Returns the canonical name for shared access.
     *
     * @return A <code>String</code> containing the canonical name for shared access.
     */
    private String getSharedAccessCanonicalName() {
        String accountName = this.getServiceClient().getCredentials().getAccountName();
        String tableNameLowerCase = this.getName().toLowerCase(Locale.ENGLISH);

        return String.format("/%s/%s/%s", SR.TABLE, accountName, tableNameLowerCase);
    }

    /**
     * Verifies the passed in URI. Then parses it and uses its components to populate this resource's properties.
     * 
     * @param completeUri
     *            A {@link StorageUri} object which represents the complete URI.
     * @param credentials
     *            A {@link StorageCredentials} object used to authenticate access.
     * @throws StorageException
     *             If a storage service error occurred.
     */  
    private void parseQueryAndVerify(final StorageUri completeUri, final StorageCredentials credentials) 
            throws StorageException {
        Utility.assertNotNull("completeUri", completeUri);

        if (!completeUri.isAbsolute()) {
            throw new IllegalArgumentException(String.format(SR.RELATIVE_ADDRESS_NOT_PERMITTED, completeUri.toString()));
        }

        this.storageUri = PathUtility.stripURIQueryAndFragment(completeUri);
        
        final StorageCredentialsSharedAccessSignature parsedCredentials = 
                SharedAccessSignatureHelper.parseQuery(completeUri);

        if (credentials != null && parsedCredentials != null) {
            throw new IllegalArgumentException(SR.MULTIPLE_CREDENTIALS_PROVIDED);
        }

        try {
            final boolean usePathStyleUris = Utility.determinePathStyleFromUri(this.storageUri.getPrimaryUri());
            this.tableServiceClient = new CloudTableClient(PathUtility.getServiceClientBaseAddress(
                    this.getStorageUri(), usePathStyleUris), credentials != null ? credentials : parsedCredentials);
            this.name = PathUtility.getTableNameFromUri(storageUri.getPrimaryUri(), usePathStyleUris);
        }
        catch (final URISyntaxException e) {
            throw Utility.generateNewUnexpectedStorageException(e);
        }
    }
} public final class EntityProperty {
    private String value;
    private Class<?> type;
    private EdmType edmType = EdmType.NULL;
    
    /**
     * Flag that specifies whether the client should look to correct Date values stored on a {@link TableEntity}
     * that may have been written using versions of this library prior to 0.4.0.
     * See <a href="http://go.microsoft.com/fwlink/?LinkId=523753">here</a> for more details.
     */
    private boolean dateBackwardCompatibility = false;

    /**
     * Reserved for internal use. Constructs an {@link EntityProperty} instance from a <code>Object</code> value type,
     * and verifies that the value can be interpreted as the specified data type.
     * 
     * @param value
     *            The <code>Object</code> to convert to a string and store.
     */
    protected EntityProperty(final String value, final Class<?> type) {
        this.type = type;
        this.value = value;
        if (type.equals(byte[].class)) {
            this.getValueAsByteArray();
            this.edmType = EdmType.BINARY;
        }
        else if (type.equals(Byte[].class)) {
            this.getValueAsByteObjectArray();
            this.edmType = EdmType.BINARY;
        }
        else if (type.equals(String.class)) {
            this.edmType = EdmType.STRING;
        }
        else if (type.equals(boolean.class)) {
            this.getValueAsBoolean();
            this.edmType = EdmType.BOOLEAN;
        }
        else if (type.equals(Boolean.class)) {
            this.getValueAsBooleanObject();
            this.edmType = EdmType.BOOLEAN;
        }
        else if (type.equals(Date.class)) {
            this.getValueAsDate();
            this.edmType = EdmType.DATE_TIME;
        }
        else if (type.equals(double.class)) {
            this.getValueAsDouble();
            this.edmType = EdmType.DOUBLE;
        }
        else if (type.equals(Double.class)) {
            this.getValueAsDoubleObject();
            this.edmType = EdmType.DOUBLE;
        }
        else if (type.equals(UUID.class)) {
            this.getValueAsUUID();
            this.edmType = EdmType.GUID;
        }
        else if (type.equals(int.class)) {
            this.getValueAsInteger();
            this.edmType = EdmType.INT32;
        }
        else if (type.equals(Integer.class)) {
            this.getValueAsIntegerObject();
            this.edmType = EdmType.INT32;
        }
        else if (type.equals(long.class)) {
            this.getValueAsLong();
            this.edmType = EdmType.INT64;
        }
        else if (type.equals(Long.class)) {
            this.getValueAsLongObject();
            this.edmType = EdmType.INT64;
        }
        else {
            throw new IllegalArgumentException(String.format(SR.TYPE_NOT_SUPPORTED, type.toString()));
        }
    }

    /**
     * Reserved for internal use. Constructs an {@link EntityProperty} instance from a <code>Object</code> value and a
     * data type, and verifies that the value can be interpreted as the specified data type.
     * 
     * @param value
     *            The <code>Object</code> to convert to a string and store.
     * @param edmType
     *            The <code>Class<?></code> type of the value to construct.
     */
    protected EntityProperty(final Object value, final Class<?> type) {
        if (type.equals(byte[].class)) {
            setValue((byte[]) value);
            this.type = type;
        }
        else if (type.equals(Byte[].class)) {
            setValue((Byte[]) value);
            this.type = type;
        }
        else if (type.equals(String.class)) {
            setValue((String) value);
            this.type = type;
        }
        else if (type.equals(boolean.class)) {
            setValue(((Boolean) value).booleanValue());
            this.type = type;
        }
        else if (type.equals(Boolean.class)) {
            setValue((Boolean) value);
            this.type = type;
        }
        else if (type.equals(double.class)) {
            setValue(((Double) value).doubleValue());
            this.type = type;
        }
        else if (type.equals(Double.class)) {
            setValue((Double) value);
            this.type = type;
        }
        else if (type.equals(UUID.class)) {
            setValue((UUID) value);
            this.type = type;
        }
        else if (type.equals(int.class)) {
            setValue(((Integer) value).intValue());
            this.type = type;
        }
        else if (type.equals(Integer.class)) {
            setValue((Integer) value);
            this.type = type;
        }
        else if (type.equals(long.class)) {
            setValue(((Long) value).longValue());
            this.type = type;
        }
        else if (type.equals(Long.class)) {
            setValue((Long) value);
            this.type = type;
        }
        else if (type.equals(Date.class)) {
            setValue((Date) value);
            this.type = type;
        }
        else {
            throw new IllegalArgumentException(String.format(SR.TYPE_NOT_SUPPORTED, type.toString()));
        }
    }

    /**
     * Reserved for internal use. Constructs an {@link EntityProperty} instance from a <code>String</code> value and a
     * data type, and verifies that the value can be interpreted as the specified data type.
     * 
     * @param value
     *            The <code>String</code> representation of the value to construct.
     * @param edmType
     *            The {@link EdmType} data type of the value to construct.
     */
    protected EntityProperty(final String value, final EdmType edmType) {
        this.edmType = edmType;
        this.value = value;

        // validate data is encoded correctly
        if (edmType == EdmType.STRING) {
            this.type = String.class;
            return;
        }
        else if (edmType == EdmType.BINARY) {
            this.getValueAsByteArray();
            this.type = Byte[].class;
        }
        else if (edmType == EdmType.BOOLEAN) {
            this.getValueAsBoolean();
            this.type = Boolean.class;
        }
        else if (edmType == EdmType.DOUBLE) {
            this.getValueAsDouble();
            this.type = Double.class;
        }
        else if (edmType == EdmType.GUID) {
            this.getValueAsUUID();
            this.type = UUID.class;
        }
        else if (edmType == EdmType.INT32) {
            this.getValueAsInteger();
            this.type = Integer.class;
        }
        else if (edmType == EdmType.INT64) {
            this.getValueAsLong();
            this.type = Long.class;
        }
        else if (edmType == EdmType.DATE_TIME) {
            this.getValueAsDate();
            this.type = Date.class;
        }
        else {
            // these are overwritten when this is called from the table parser with a more informative message
            if (edmType == null) {
                throw new IllegalArgumentException(SR.EDMTYPE_WAS_NULL);
            }
            throw new IllegalArgumentException(String.format(SR.INVALID_EDMTYPE_VALUE, edmType.toString()));
        }
    }

    /**
     * Constructs an {@link EntityProperty} instance from a <code>boolean</code> value.
     * 
     * @param value
     *            The <code>boolean</code> value of the entity property to set.
     */
    public EntityProperty(final boolean value) {
        this.setValue(value);
    }

    /**
     * Constructs an {@link EntityProperty} instance from a <code>Boolean</code> value.
     * 
     * @param value
     *            The <code>Boolean</code> value of the entity property to set.
     */
    public EntityProperty(final Boolean value) {
        this.setValue(value);
    }

    /**
     * Constructs an {@link EntityProperty} instance from a <code>byte[]</code> value.
     * 
     * @param value
     *            The <code>byte[]</code> value of the entity property to set.
     */
    public EntityProperty(final byte[] value) {
        this.setValue(value);
    }

    /**
     * Constructs an {@link EntityProperty} instance from a <code>Byte[]</code>.
     * 
     * @param value
     *            The <code>Byte[]</code> to set as the entity property value.
     */
    public EntityProperty(final Byte[] value) {
        this.setValue(value);
    }

    /**
     * Constructs an {@link EntityProperty} instance from a <code>java.util.Date</code> value.
     * 
     * @param value
     *            The <code>java.util.Date</code> to set as the entity property value.
     */
    public EntityProperty(final Date value) {
        this.setValue(value);
    }

    /**
     * Constructs an {@link EntityProperty} instance from a <code>double</code> value.
     * 
     * @param value
     *            The <code>double</code> value of the entity property to set.
     */
    public EntityProperty(final double value) {
        this.setValue(value);
    }

    /**
     * Constructs an {@link EntityProperty} instance from a <code>Double</code> value.
     * 
     * @param value
     *            The <code>Double</code> value of the entity property to set.
     */
    public EntityProperty(final Double value) {
        this.setValue(value);
    }

    /**
     * Constructs an {@link EntityProperty} instance from an <code>int</code> value.
     * 
     * @param value
     *            The <code>int</code> value of the entity property to set.
     */
    public EntityProperty(final int value) {
        this.setValue(value);
    }

    /**
     * Constructs an {@link EntityProperty} instance from an <code>Integer</code> value.
     * 
     * @param value
     *            The <code>Integer</code> value of the entity property to set.
     */
    public EntityProperty(final Integer value) {
        this.setValue(value);
    }

    /**
     * Constructs an {@link EntityProperty} instance from a <code>long</code> value.
     * 
     * @param value
     *            The <code>long</code> value of the entity property to set.
     */
    public EntityProperty(final long value) {
        this.setValue(value);
    }

    /**
     * Constructs an {@link EntityProperty} instance from a <code>Long</code> value.
     * 
     * @param value
     *            The <code>Long</code> value of the entity property to set.
     */
    public EntityProperty(final Long value) {
        this.setValue(value);
    }

    /**
     * Constructs an {@link EntityProperty} instance from a <code>String</code> value.
     * 
     * @param value
     *            The <code>String</code> to set as the entity property value.
     */
    public EntityProperty(final String value) {
        this.setValue(value);
    }

    /**
     * Constructs an {@link EntityProperty} instance from a <code>java.util.UUID</code> value.
     * 
     * @param value
     *            The <code>java.util.UUID</code> to set as the entity property value.
     */
    public EntityProperty(final UUID value) {
        this.setValue(value);
    }

    /**
     * Gets the {@link EdmType} storage data type for the {@link EntityProperty}.
     * 
     * @return
     *         The {@link EdmType} enumeration value for the data type of the {@link EntityProperty}.
     */
    public EdmType getEdmType() {
        return this.edmType;
    }

    /**
     * Gets a flag indicating that the {@link EntityProperty} value is <code>null</code>.
     * 
     * @return
     *         A <code>boolean</code> flag indicating that the {@link EntityProperty} value is <code>null</code>.
     */
    public boolean getIsNull() {
        return this.value == null;
    }

    /**
     * Gets the class type of the {@link EntityProperty}.
     * 
     * @return
     *         The <code>Class<?></code> of the {@link EntityProperty}.
     */
    public Class<?> getType() {
        return this.type;
    }
    
    /**
     * Gets the value of this {@link EntityProperty} as a <code>boolean</code>.
     * 
     * @return
     *         A <code>boolean</code> representation of the {@link EntityProperty} value.
     * 
     * @throws IllegalArgumentException
     *             If the value is <code>null</code> or cannot be parsed as a <code>Boolean</code>.
     */
    public boolean getValueAsBoolean() {
        if (this.getIsNull()) {
            throw new IllegalArgumentException(SR.ENTITY_PROPERTY_CANNOT_BE_NULL_FOR_PRIMITIVES);
        }
        return Boolean.parseBoolean(this.value);
    }

    /**
     * Gets the value of this {@link EntityProperty} as a <code>Boolean</code>.
     * 
     * @return
     *         A <code>Boolean</code> representation of the {@link EntityProperty} value.
     * 
     * @throws IllegalArgumentException
     *             If the value is <code>null</code> or cannot be parsed as a <code>Boolean</code>.
     */
    public Boolean getValueAsBooleanObject() {
        if (this.getIsNull()) {
            return null;
        }
        return Boolean.parseBoolean(this.value);
    }

    /**
     * Gets the value of this {@link EntityProperty} as a <code>byte</code> array.
     * 
     * @return
     *         A <code>byte[]</code> representation of the {@link EntityProperty} value, or <code>null</code>.
     */
    public byte[] getValueAsByteArray() {
        return this.getIsNull() ? null : Base64.decode(this.value);
    }

    /**
     * Gets the value of this {@link EntityProperty} as a <code>Byte</code> array.
     * 
     * @return
     *         A <code>Byte[]</code> representation of the {@link EntityProperty} value, or <code>null</code>.
     */
    public Byte[] getValueAsByteObjectArray() {
        return this.getIsNull() ? null : Base64.decodeAsByteObjectArray(this.value);
    }

    /**
     * Gets the value of this {@link EntityProperty} as a <code>java.util.Date</code>.
     * 
     * @return
     *         A <code>java.util.Date</code> representation of the {@link EntityProperty} value, or <code>null</code>.
     * 
     * @throws IllegalArgumentException
     *             If the value is not <code>null</code> and cannot be parsed as a <code>java.util.Date</code>.
     */
    public Date getValueAsDate() {
        return this.getIsNull() ? null : Utility.parseDate(this.value, this.dateBackwardCompatibility);
    }

    /**
     * Gets the value of this {@link EntityProperty} as a <code>double</code>.
     * 
     * @return
     *         A <code>double</code> representation of the {@link EntityProperty} value.
     * 
     * @throws IllegalArgumentException
     *             If the value is <code>null</code> or cannot be parsed as a <code>double</code>.
     */
    public double getValueAsDouble() {
        if (this.getIsNull()) {
            throw new IllegalArgumentException(SR.ENTITY_PROPERTY_CANNOT_BE_NULL_FOR_PRIMITIVES);
        }
        else if (this.value.equals("Infinity") || this.value.equals("INF")) {
            return Double.POSITIVE_INFINITY;
        }
        else if (this.value.equals("-Infinity") || this.value.equals("-INF")) {
            return Double.NEGATIVE_INFINITY;
        }
        else if (this.value.equals("NaN")) {
            return Double.NaN;
        }
        else {
            return Double.parseDouble(this.value);
        }
    }

    /**
     * Gets the value of this {@link EntityProperty} as a <code>double</code>.
     * 
     * @return
     *         A <code>double</code> representation of the {@link EntityProperty} value.
     * 
     * @throws IllegalArgumentException
     *             If the value is <code>null</code> or cannot be parsed as a <code>double</code>.
     */
    public Double getValueAsDoubleObject() {
        if (this.getIsNull()) {
            return null;
        }
        else if (this.value.equals("Infinity") || this.value.equals("INF")) {
            return Double.POSITIVE_INFINITY;
        }
        else if (this.value.equals("-Infinity") || this.value.equals("-INF")) {
            return Double.NEGATIVE_INFINITY;
        }
        else if (this.value.equals("NaN")) {
            return Double.NaN;
        }
        else {
            return Double.parseDouble(this.value);
        }
    }

    /**
     * Gets the value of this {@link EntityProperty} as an <code>int</code>.
     * 
     * @return
     *         An <code>int</code> representation of the {@link EntityProperty} value.
     * 
     * @throws IllegalArgumentException
     *             If the value is <code>null</code> or cannot be parsed as an <code>int</code>.
     */
    public int getValueAsInteger() {
        if (this.getIsNull()) {
            throw new IllegalArgumentException(SR.ENTITY_PROPERTY_CANNOT_BE_NULL_FOR_PRIMITIVES);
        }
        return Integer.parseInt(this.value);
    }

    /**
     * Gets the value of this {@link EntityProperty} as an <code>Integer</code>.
     * 
     * @return
     *         An <code>Integer</code> representation of the {@link EntityProperty} value.
     * 
     * @throws IllegalArgumentException
     *             If the value is <code>null</code> or cannot be parsed as an <code>int</code>.
     */
    public Integer getValueAsIntegerObject() {
        return this.getIsNull() ? null : Integer.parseInt(this.value);
    }

    /**
     * Gets the value of this {@link EntityProperty} as a <code>long</code>.
     * 
     * @return
     *         A <code>long</code> representation of the {@link EntityProperty} value.
     * 
     * @throws IllegalArgumentException
     *             If the value is <code>null</code> or cannot be parsed as a <code>long</code>.
     */
    public long getValueAsLong() {
        if (this.getIsNull()) {
            throw new IllegalArgumentException(SR.ENTITY_PROPERTY_CANNOT_BE_NULL_FOR_PRIMITIVES);
        }
        return Long.parseLong(this.value);
    }

    /**
     * Gets the value of this {@link EntityProperty} as a <code>Long</code>.
     * 
     * @return
     *         A <code>long</code> representation of the {@link EntityProperty} value.
     * 
     * @throws IllegalArgumentException
     *             If the value is <code>null</code> or cannot be parsed as a <code>long</code>.
     */
    public Long getValueAsLongObject() {
        return this.getIsNull() ? null : Long.parseLong(this.value);
    }

    /**
     * Gets the value of this {@link EntityProperty} as a <code>String</code>.
     * 
     * @return
     *         A <code>String</code> representation of the {@link EntityProperty} value, or <code>null</code>.
     */
    public String getValueAsString() {
        return this.getIsNull() ? null : this.value;
    }

    /**
     * Gets the value of this {@link EntityProperty} as a <code>java.util.UUID</code>.
     * 
     * @return
     *         A <code>java.util.UUID</code> representation of the {@link EntityProperty} value, or <code>null</code>.
     * 
     * @throws IllegalArgumentException
     *             If the value cannot be parsed as a <code>java.util.UUID</code>.
     */
    public UUID getValueAsUUID() {
        return this.getIsNull() ? null : UUID.fromString(this.value);
    }

    /**
     * Sets this {@link EntityProperty} using the serialized <code>boolean</code> value.
     * 
     * @param value
     *            The <code>boolean</code> value to set as the {@link EntityProperty} value.
     */
    public synchronized final void setValue(final boolean value) {
        this.edmType = EdmType.BOOLEAN;
        this.type = boolean.class;
        this.value = value ? Constants.TRUE : Constants.FALSE;
    }

    /**
     * Sets this {@link EntityProperty} using the serialized <code>Boolean</code> value.
     * 
     * @param value
     *            The <code>Boolean</code> value to set as the {@link EntityProperty} value.
     */
    public synchronized final void setValue(final Boolean value) {
        this.edmType = EdmType.BOOLEAN;
        this.type = Boolean.class;
        if (value == null) {
            this.value = null;
        }
        else {
            this.value = value ? Constants.TRUE : Constants.FALSE;
        }
    }

    /**
     * Sets this {@link EntityProperty} using the serialized <code>byte[]</code> value.
     * 
     * @param value
     *            The <code>byte[]</code> value to set as the {@link EntityProperty} value. This value may be
     *            <code>null</code>.
     */
    public synchronized final void setValue(final byte[] value) {
        this.edmType = EdmType.BINARY;
        this.type = byte[].class;
        this.value = value == null ? null : Base64.encode(value);
    }

    /**
     * Sets this {@link EntityProperty} using the serialized <code>Byte[]</code> value.
     * 
     * @param value
     *            The <code>Byte[]</code> value to set as the {@link EntityProperty} value. This value may be
     *            <code>null</code>.
     */
    public synchronized final void setValue(final Byte[] value) {
        this.edmType = EdmType.BINARY;
        this.type = Byte[].class;
        this.value = value == null ? null : Base64.encode(value);
    }

    /**
     * Sets this {@link EntityProperty} using the serialized <code>java.util.Date</code> value.
     * 
     * @param value
     *            The <code>java.util.Date</code> value to set as the {@link EntityProperty} value. This value may be
     *            <code>null</code>.
     */
    public synchronized final void setValue(final Date value) {
        this.edmType = EdmType.DATE_TIME;
        this.type = Date.class;
        this.value = value == null ? null : Utility.getJavaISO8601Time(value);
    }

    /**
     * Sets this {@link EntityProperty} using the serialized <code>double</code> value.
     * 
     * @param value
     *            The <code>double</code> value to set as the {@link EntityProperty} value.
     */
    public synchronized final void setValue(final double value) {
        this.edmType = EdmType.DOUBLE;
        this.type = double.class;
        this.value = Double.toString(value);
    }

    /**
     * Sets this {@link EntityProperty} using the serialized <code>Double</code> value.
     * 
     * @param value
     *            The <code>Double</code> value to set as the {@link EntityProperty} value.
     */
    public synchronized final void setValue(final Double value) {
        this.edmType = EdmType.DOUBLE;
        this.type = Double.class;
        this.value = value == null ? null : Double.toString(value);
    }

    /**
     * Sets this {@link EntityProperty} using the serialized <code>int</code> value.
     * 
     * @param value
     *            The <code>int</code> value to set as the {@link EntityProperty} value.
     */
    public synchronized final void setValue(final int value) {
        this.edmType = EdmType.INT32;
        this.type = int.class;
        this.value = Integer.toString(value);
    }

    /**
     * Sets this {@link EntityProperty} using the serialized <code>Integer</code> value.
     * 
     * @param value
     *            The <code>Integer</code> value to set as the {@link EntityProperty} value.
     */
    public synchronized final void setValue(final Integer value) {
        this.edmType = EdmType.INT32;
        this.type = Integer.class;
        this.value = value == null ? null : Integer.toString(value);
    }

    /**
     * Sets this {@link EntityProperty} using the serialized <code>long</code> value.
     * 
     * @param value
     *            The <code>long</code> value to set as the {@link EntityProperty} value.
     */
    public synchronized final void setValue(final long value) {
        this.edmType = EdmType.INT64;
        this.type = long.class;
        this.value = Long.toString(value);
    }

    /**
     * Sets this {@link EntityProperty} using the serialized <code>Long</code> value.
     * 
     * @param value
     *            The <code>Long</code> value to set as the {@link EntityProperty} value.
     */
    public synchronized final void setValue(final Long value) {
        this.edmType = EdmType.INT64;
        this.type = Long.class;
        this.value = value == null ? null : Long.toString(value);
    }

    /**
     * Sets this {@link EntityProperty} using the <code>String</code> value.
     * 
     * @param value
     *            The <code>String</code> value to set as the {@link EntityProperty} value. This value may be
     *            <code>null</code>.
     */
    public synchronized final void setValue(final String value) {
        this.edmType = EdmType.STRING;
        this.type = String.class;
        this.value = value;   
    }

    /**
     * Sets this {@link EntityProperty} using the serialized <code>java.util.UUID</code> value.
     * 
     * @param value
     *            The <code>java.util.UUID</code> value to set as the {@link EntityProperty} value.
     *            This value may be <code>null</code>.
     */
    public synchronized final void setValue(final UUID value) {
        this.edmType = EdmType.GUID;
        this.type = UUID.class;
        this.value = value == null ? null : value.toString();
    }

    /**
     * Sets whether the client should look to correct Date values stored on a {@link TableEntity}
     * that may have been written using versions of this library prior to 0.4.0.
     * <p>
     * {@link #dateBackwardCompatibility} is by default <code>false</code>, indicating a post 0.4.0 version or
     * mixed-platform usage.
     * <p>
     * See <a href="http://go.microsoft.com/fwlink/?LinkId=523753">here</a> for more details.
     * 
     * @param dateBackwardCompatibility
     *        <code>true</code> to enable <code>dateBackwardCompatibility</code>; otherwise, <code>false</code>
     */
    void setDateBackwardCompatibility(boolean dateBackwardCompatibility) {
        this.dateBackwardCompatibility = dateBackwardCompatibility;
    }
} public class TableOperation {
    /**
     * A static factory method returning a {@link TableOperation} instance to delete the specified entity from Microsoft
     * Azure storage. To execute this {@link TableOperation} on a given table, call the
     * {@link CloudTable#execute(TableOperation)} method on a {@link CloudTableClient} instance with the
     * 
     * @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);
    }

    /**
     * A static factory method returning a {@link TableOperation} instance to insert the specified entity into
     * Microsoft Azure storage. To execute this {@link TableOperation} on a given table, call the
     * {@link CloudTable#execute(TableOperation)} method on a {@link CloudTableClient} instance with the

     * 
     * @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 insert(final TableEntity entity) {
        return insert(entity, false);
    }

    /**
     * A static factory method returning a {@link TableOperation} instance to insert the specified entity into
     * Microsoft Azure storage. To execute this {@link TableOperation} on a given table, call the
     * {@link CloudTable#execute(TableOperation)} method on a {@link CloudTableClient} instance with the
     * 
     * @param entity
     *            The object instance implementing {@link TableEntity} to associate with the operation.
     * @param echoContent
     *            The boolean representing whether the message payload should be returned in the response.
     * @return
     *         A new {@link TableOperation} instance to insert the table entity.
     */
    public static TableOperation insert(final TableEntity entity, boolean echoContent) {
        Utility.assertNotNull("entity", entity);
        return new TableOperation(entity, TableOperationType.INSERT, echoContent);
    }

    /**
     * A static factory method returning a {@link TableOperation} instance to merge the specified entity into
     * Microsoft Azure storage, or insert it if it does not exist. To execute this {@link TableOperation} on a given
     * table, call
     * the {@link CloudTable#execute(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 replace the specified entity in
     * Microsoft Azure storage, or insert it if it does not exist. To execute this {@link TableOperation} on a given
     * table, call
     * the {@link CloudTable#execute(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 table entity into
     * Microsoft Azure storage. To execute this {@link TableOperation} on a given table, call the
     * {@link CloudTable#execute(TableOperation)} method on a {@link CloudTableClient} instance with the
     * 
     * @param entity
     *            The object instance implementing {@link TableEntity} to associate with the operation.
     * @return
     *         A new {@link TableOperation} instance for merging the table entity.
     */
    public static TableOperation merge(final TableEntity entity) {
        Utility.assertNotNull("entity", entity);
        Utility.assertNotNullOrEmpty("entity etag", entity.getEtag());
        return new TableOperation(entity, TableOperationType.MERGE);
    }

    /**
     * A static factory method returning a {@link TableOperation} instance to retrieve the specified table entity and
     * return it as the specified type. To execute this {@link TableOperation} on a given table, call the
     * {@link CloudTable#execute(TableOperation)} method on a {@link CloudTableClient} instance with the
     * 
     * @param partitionKey
     *            A <code>String</code> which specifies the PartitionKey value for the entity to retrieve.
     * @param rowKey
     *            A <code>String</code> which specifies the RowKey value for the entity to retrieve.
     * @param clazzType
     *            The class type of the table entity object to retrieve.
     * @return
     *         A new {@link TableOperation} instance for retrieving the table entity.
     */
    public static TableOperation retrieve(final String partitionKey, final String rowKey,
            final Class<? extends TableEntity> clazzType) {
        final QueryTableOperation retOp = new QueryTableOperation(partitionKey, rowKey);
        retOp.setClazzType(clazzType);
        return retOp;
    }

    /**
     * A static factory method returning a {@link TableOperation} instance to retrieve the specified table entity and
     * return a projection of it using the specified resolver. To execute this {@link TableOperation} on a given table,
     * call the {@link CloudTable#execute(TableOperation)} method on a {@link CloudTableClient} instance
     * with the table name and the {@link TableOperation} as arguments.
     * 
     * @param partitionKey
     *            A <code>String</code> which specifies the PartitionKey value for the entity to retrieve.
     * @param rowKey
     *            A <code>String</code> which specifies the RowKey value for the entity to retrieve.
     * @param resolver
     *            The implementation of {@link EntityResolver} to use to project the result entity as type T.
     * @return
     *         A new {@link TableOperation} instance for retrieving the table entity.
     */
    public static TableOperation retrieve(final String partitionKey, final String rowKey,
            final EntityResolver<?> resolver) {
        final QueryTableOperation retOp = new QueryTableOperation(partitionKey, rowKey);
        retOp.setResolver(resolver);
        return retOp;
    }

    /**
     * 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 CloudTable#execute(TableOperation)} method.
     * 
     * @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);
    }

    /**
     * The table entity instance associated with the operation.
     */
    private TableEntity entity;

    /**
     * The {@link TableOperationType} enumeration value for the operation type.
     */
    private TableOperationType opType = null;

    /**
     * The value that represents whether the message payload should be returned in the response.
     */
    private boolean echoContent;

    /**
     * Nullary Default Constructor.
     */
    protected TableOperation() {
        // Empty constructor.
    }

    /**
     * Reserved for internal use. Constructs a {@link TableOperation} with the specified table entity and operation
     * type.
     * 
     * @param entity
     *            The object instance implementing {@link TableEntity} to associate with the operation.
     * @param opType
     *            The {@link TableOperationType} enumeration value for the operation type.
     */
    protected TableOperation(final TableEntity entity, final TableOperationType opType) {
        this(entity, opType, false);
    }

    /**
     * Reserved for internal use. Constructs a {@link TableOperation} with the specified table entity and operation
     * type.
     * 
     * @param entity
     *            The object instance implementing {@link TableEntity} to associate with the operation.
     * @param opType
     *            The {@link TableOperationType} enumeration value for the operation type.
     * @param echoContent
     *            The boolean representing whether the message payload should be returned in the response.
     */
    protected TableOperation(final TableEntity entity, final TableOperationType opType, final boolean echoContent) {
        this.entity = entity;
        this.opType = opType;
        this.echoContent = echoContent;
    }

    /**
     * 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/azure/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> which specifies 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} which represents 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 {

        return ExecutionEngine.executeWithRetry(client, this, this.deleteImpl(client, tableName, options, opContext),
                options.getRetryPolicyFactory(), opContext);
    }

    private StorageRequest<CloudTableClient, TableOperation, TableResult> deleteImpl(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(SR.ETAG_INVALID_FOR_DELETE, this.getEntity().getEtag());
            Utility.assertNotNull(SR.PARTITIONKEY_MISSING_FOR_DELETE, this.getEntity().getPartitionKey());
            Utility.assertNotNull(SR.ROWKEY_MISSING_FOR_DELETE, this.getEntity().getRowKey());
        }

        final StorageRequest<CloudTableClient, TableOperation, TableResult> deleteRequest = new StorageRequest<CloudTableClient, TableOperation, TableResult>(
                options, client.getStorageUri()) {

            @Override
            public HttpURLConnection buildRequest(CloudTableClient client, TableOperation operation,
                    OperationContext context) throws Exception {
                return TableRequest.delete(client.getTransformedEndPoint(context).getUri(this.getCurrentLocation()),
                        options, null, context, tableName, generateRequestIdentity(isTableEntry, tableIdentity),
                        operation.getEntity().getEtag());
            }

            @Override
            public void signRequest(HttpURLConnection connection, CloudTableClient client, OperationContext context)
                    throws Exception {
                StorageRequest.signTableRequest(connection, client, -1L, context);
            }

            @Override
            public TableResult preProcessResponse(TableOperation operation, CloudTableClient client,
                    OperationContext context) throws Exception {
                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_NO_CONTENT) {
                    throw TableServiceException.generateTableServiceException(this.getResult(), operation, 
                            this.getConnection().getErrorStream(), options.getTablePayloadFormat());
                }

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

            @Override
            public StorageExtendedErrorInformation parseErrorDetails() {
                return TableStorageErrorDeserializer.parseErrorDetails(this);
            }
        };

        return deleteRequest;
    }

    /**
     * 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> which specifies 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} which represents 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 {

        return ExecutionEngine.executeWithRetry(client, this, this.insertImpl(client, tableName, options, opContext),
                options.getRetryPolicyFactory(), opContext);
    }

    private StorageRequest<CloudTableClient, TableOperation, TableResult> insertImpl(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;

        // Inserts need row key and partition key
        if (!isTableEntry) {
            Utility.assertNotNull(SR.PARTITIONKEY_MISSING_FOR_INSERT, this.getEntity().getPartitionKey());
            Utility.assertNotNull(SR.ROWKEY_MISSING_FOR_INSERT, this.getEntity().getRowKey());
        }

        ByteArrayOutputStream entityStream = new ByteArrayOutputStream();
        try {
            TableEntitySerializer.writeSingleEntity(entityStream, this.entity,
                    isTableEntry, opContext);

            // We need to buffer once and use it for all retries instead of serializing the entity every single time. 
            // In the future, this could also be used to calculate transactional MD5 for table operations.
            final byte[] entityBytes = entityStream.toByteArray();

            final StorageRequest<CloudTableClient, TableOperation, TableResult> putRequest = new StorageRequest<CloudTableClient, TableOperation, TableResult>(
                    options, client.getStorageUri()) {

                @Override
                public HttpURLConnection buildRequest(CloudTableClient client, TableOperation operation,
                        OperationContext context) throws Exception {
                    this.setSendStream(new ByteArrayInputStream(entityBytes));
                    this.setLength((long) entityBytes.length);
                    return TableRequest.insert(
                            client.getTransformedEndPoint(opContext).getUri(this.getCurrentLocation()), options, null,
                            opContext, tableName, generateRequestIdentity(isTableEntry, tableIdentity),
                            operation.opType != TableOperationType.INSERT ? operation.getEntity().getEtag() : null,
                            operation.getEchoContent(), operation.opType.getUpdateType());
                }

                @Override
                public void signRequest(HttpURLConnection connection, CloudTableClient client, OperationContext context)
                        throws Exception {
                    StorageRequest.signTableRequest(connection, client, -1L, context);
                }

                @Override
                public TableResult preProcessResponse(TableOperation operation, CloudTableClient client,
                        OperationContext context) throws Exception {
                    if (operation.opType == TableOperationType.INSERT) {
                        if (operation.getEchoContent()
                                && this.getResult().getStatusCode() == HttpURLConnection.HTTP_CREATED) {
                            // Insert should receive created if echo content is on
                            return new TableResult();
                        }
                        else if (!operation.getEchoContent()
                                && this.getResult().getStatusCode() == HttpURLConnection.HTTP_NO_CONTENT) {
                            // Insert should receive no content if echo content is off
                            return operation.parseResponse(null, this.getResult().getStatusCode(), this.getConnection()
                                    .getHeaderField(TableConstants.HeaderConstants.ETAG), opContext, options);
                        }
                    }
                    else if (this.getResult().getStatusCode() == HttpURLConnection.HTTP_NO_CONTENT) {
                        // InsertOrMerge and InsertOrReplace should always receive no content
                        return operation.parseResponse(null, this.getResult().getStatusCode(), this.getConnection()
                                .getHeaderField(TableConstants.HeaderConstants.ETAG), opContext, options);
                    }

                    throw TableServiceException.generateTableServiceException(this.getResult(), operation, 
                            this.getConnection().getErrorStream(), options.getTablePayloadFormat());
                }

                @Override
                public TableResult postProcessResponse(HttpURLConnection connection, TableOperation operation,
                        CloudTableClient client, OperationContext context, TableResult result) throws Exception {
                    if (operation.opType == TableOperationType.INSERT && operation.getEchoContent()) {
                        result = operation.parseResponse(this.getConnection().getInputStream(), this.getResult()
                                .getStatusCode(),
                                this.getConnection().getHeaderField(TableConstants.HeaderConstants.ETAG), opContext,
                                options);
                    }
                    return result;
                }

                @Override
                public StorageExtendedErrorInformation parseErrorDetails() {
                    return TableStorageErrorDeserializer.parseErrorDetails(this);
                }
            };

            return putRequest;
        }
        catch (IOException e) {
            // The request was not even made. There was an error while trying to read the entity. Just throw.
            StorageException translatedException = StorageException.translateClientException(e);
            throw translatedException;
        }

    }

    /**
     * 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> which specifies 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} which represents the results of executing the operation.
     * 
     * @throws StorageException
     *             if an error occurs in the storage operation.
     * @throws IOException
     */
    private TableResult performMerge(final CloudTableClient client, final String tableName,
            final TableRequestOptions options, final OperationContext opContext) throws StorageException {

        return ExecutionEngine.executeWithRetry(client, this, this.mergeImpl(client, tableName, options, opContext),
                options.getRetryPolicyFactory(), opContext);
    }

    private StorageRequest<CloudTableClient, TableOperation, TableResult> mergeImpl(final CloudTableClient client,
            final String tableName, final TableRequestOptions options, final OperationContext opContext)
            throws StorageException {
        Utility.assertNotNullOrEmpty(SR.ETAG_INVALID_FOR_MERGE, this.getEntity().getEtag());
        Utility.assertNotNull(SR.PARTITIONKEY_MISSING_FOR_MERGE, this.getEntity().getPartitionKey());
        Utility.assertNotNull(SR.ROWKEY_MISSING_FOR_MERGE, this.getEntity().getRowKey());

        ByteArrayOutputStream entityStream = new ByteArrayOutputStream();
        try {
            TableEntitySerializer.writeSingleEntity(entityStream, this.getEntity(), false, opContext);
            // We need to buffer once and use it for all retries instead of serializing the entity every single time. 
            // In the future, this could also be used to calculate transactional MD5 for table operations.
            final byte[] entityBytes = entityStream.toByteArray();

            final StorageRequest<CloudTableClient, TableOperation, TableResult> putRequest = new StorageRequest<CloudTableClient, TableOperation, TableResult>(
                    options, client.getStorageUri()) {

                @Override
                public HttpURLConnection buildRequest(CloudTableClient client, TableOperation operation,
                        OperationContext context) throws Exception {
                    this.setSendStream(new ByteArrayInputStream(entityBytes));
                    this.setLength((long) entityBytes.length);
                    return TableRequest.merge(client.getTransformedEndPoint(opContext)
                            .getUri(this.getCurrentLocation()), options, null, opContext, tableName,
                            generateRequestIdentity(false, null), operation.getEntity().getEtag());
                }

                @Override
                public void signRequest(HttpURLConnection connection, CloudTableClient client, OperationContext context)
                        throws Exception {
                    StorageRequest.signTableRequest(connection, client, -1L, context);
                }

                @Override
                public TableResult preProcessResponse(TableOperation operation, CloudTableClient client,
                        OperationContext context) throws Exception {
                    if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_NO_CONTENT) {
                        throw TableServiceException.generateTableServiceException(this.getResult(), operation, 
                                this.getConnection().getErrorStream(), options.getTablePayloadFormat());
                    }

                    return operation.parseResponse(null, this.getResult().getStatusCode(), this.getConnection()
                            .getHeaderField(TableConstants.HeaderConstants.ETAG), opContext, options);
                }

                @Override
                public StorageExtendedErrorInformation parseErrorDetails() {
                    return TableStorageErrorDeserializer.parseErrorDetails(this);
                }
            };

            return putRequest;
        }
        catch (IOException e) {
            // The request was not even made. There was an error while trying to read the entity. Just throw.
            StorageException translatedException = StorageException.translateClientException(e);
            throw translatedException;
        }
    }

    /**
     * Reserved for internal use. Perform an update 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> which specifies 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} which represents the results of executing the operation.
     * 
     * @throws StorageException
     *             if an error occurs in the storage operation.
     */
    private TableResult performUpdate(final CloudTableClient client, final String tableName,
            final TableRequestOptions options, final OperationContext opContext) throws StorageException {

        return ExecutionEngine.executeWithRetry(client, this, this.updateImpl(client, tableName, options, opContext),
                options.getRetryPolicyFactory(), opContext);
    }

    private StorageRequest<CloudTableClient, TableOperation, TableResult> updateImpl(final CloudTableClient client,
            final String tableName, final TableRequestOptions options, final OperationContext opContext)
            throws StorageException {
        Utility.assertNotNullOrEmpty(SR.ETAG_INVALID_FOR_UPDATE, this.getEntity().getEtag());
        Utility.assertNotNull(SR.PARTITIONKEY_MISSING_FOR_UPDATE, this.getEntity().getPartitionKey());
        Utility.assertNotNull(SR.ROWKEY_MISSING_FOR_UPDATE, this.getEntity().getRowKey());

        ByteArrayOutputStream entityStream = new ByteArrayOutputStream();
        try {
            TableEntitySerializer.writeSingleEntity(entityStream, this.getEntity(), false, opContext);
            // We need to buffer once and use it for all retries instead of serializing the entity every single time. 
            // In the future, this could also be used to calculate transactional MD5 for table operations.
            final byte[] entityBytes = entityStream.toByteArray();

            final StorageRequest<CloudTableClient, TableOperation, TableResult> putRequest = new StorageRequest<CloudTableClient, TableOperation, TableResult>(
                    options, client.getStorageUri()) {

                @Override
                public HttpURLConnection buildRequest(CloudTableClient client, TableOperation operation,
                        OperationContext context) throws Exception {
                    this.setSendStream(new ByteArrayInputStream(entityBytes));
                    this.setLength((long) entityBytes.length);
                    return TableRequest.update(
                            client.getTransformedEndPoint(context).getUri(this.getCurrentLocation()), options, null,
                            context, tableName, generateRequestIdentity(false, null), operation.getEntity()
                                    .getEtag());
                }

                @Override
                public void signRequest(HttpURLConnection connection, CloudTableClient client, OperationContext context)
                        throws Exception {
                    StorageRequest.signTableRequest(connection, client, -1L, context);
                }

                @Override
                public TableResult preProcessResponse(TableOperation operation, CloudTableClient client,
                        OperationContext context) throws Exception {
                    if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_NO_CONTENT) {
                        throw TableServiceException.generateTableServiceException(this.getResult(), operation, 
                                this.getConnection().getErrorStream(), options.getTablePayloadFormat());
                    }

                    return operation.parseResponse(null, this.getResult().getStatusCode(), this.getConnection()
                            .getHeaderField(TableConstants.HeaderConstants.ETAG), opContext, options);
                }

                @Override
                public StorageExtendedErrorInformation parseErrorDetails() {
                    return TableStorageErrorDeserializer.parseErrorDetails(this);
                }
            };

            return putRequest;
        }
        catch (IOException e) {
            // The request was not even made. There was an error while trying to read the entity. Just throw.
            StorageException translatedException = StorageException.translateClientException(e);
            throw translatedException;
        }
    }

    /**
     * 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> which specifies 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} which represents 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();
        }

        opContext.initialize();
        options = TableRequestOptions.populateAndApplyDefaults(options, client);
        Utility.assertNotNullOrEmpty(TableConstants.TABLE_NAME, 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(SR.UNKNOWN_TABLE_OPERATION);
        }
    }

    /**
     * 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>.
     * @return
     *         A <code>String</code> which represents the formatted request identity string.
     * @throws StorageException
     *             If a storage service error occurred.
     */
    protected String generateRequestIdentity(boolean isSingleIndexEntry, final String entryName)
            throws StorageException {
        if (isSingleIndexEntry) {
            return String.format("'%s'", entryName.replace("'", "''"));
        }

        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, pk.replace("'", "''"), 
                    TableConstants.ROW_KEY, rk.replace("'", "''"));
        }
    }

    /**
     * Reserved for internal use. Generates the request identity string for the specified table. The request identity
     * string combines the table name with the PartitionKey and RowKey from the operation to identify specific table
     * entities. This request identity is already UrlEncoded.
     * 
     * @param tableName
     *            A <code>String</code> which specifies the name of the table.
     * @return
     *         A <code>String</code> which represents the formatted request identity string for the specified table.
     * @throws StorageException
     */
    protected String generateRequestIdentityWithTable(final String tableName) throws StorageException {
        return String.format("%s(%s)", tableName, generateRequestIdentity(false, null));
    }

    /**
     * Reserved for internal use. Gets the table entity associated with this operation.
     * 
     * @return
     *         The {@link TableEntity} instance associated with this operation.
     */
    protected synchronized final TableEntity getEntity() {
        return this.entity;
    }

    /**
     * Reserved for internal use. Gets the operation type for this operation.
     * 
     * @return the opType
     *         The {@link TableOperationType} instance associated with this operation.
     */
    protected synchronized final TableOperationType getOperationType() {
        return this.opType;
    }

    /**
     * Reserved for internal use. Parses the table operation response into a {@link TableResult} to return.
     * 
     * @param inStream
     *            An <code>InputStream</code> which specifies the response to an insert operation.
     * @param httpStatusCode
     *            An <code>int</code> which represents the HTTP status code returned from the operation request.
     * @param etagFromHeader
     *            The <code>String</code> which specifies the Etag returned with the operation response.
     * @param opContext
     *            An {@link OperationContext} object that represents the context for the current operation.
     * 
     * @return
     *         The {@link TableResult} representing the result of the operation.
     * 
     * @throws InstantiationException
     *             if an error occurs in object construction.
     * @throws IllegalAccessException
     *             if an error occurs in reflection on an object type.
     * @throws StorageException
     *             if an error occurs in the storage operation.
     * @throws IOException
     *             if an error occurs while accessing the {@link InputStream} with Json.
     * @throws JsonParseException
     *             if an error occurs while parsing the Json, if Json is used.
     */
    protected TableResult parseResponse(final InputStream inStream, final int httpStatusCode, String etagFromHeader,
            final OperationContext opContext, final TableRequestOptions options) throws 
            InstantiationException, IllegalAccessException, StorageException, JsonParseException, IOException {
        TableResult resObj;

        if (this.opType == TableOperationType.INSERT && this.echoContent) {
            // Sending null for class type and resolver will ignore parsing the return payload.
            resObj = TableDeserializer.parseSingleOpResponse(inStream, options, httpStatusCode, null /* clazzType */,
                    null /*resolver */, opContext);
            resObj.setEtag(etagFromHeader);
            resObj.updateResultObject(this.getEntity());
        }
        else {
            resObj = new TableResult(httpStatusCode);
            resObj.setResult(this.getEntity());

            if (this.opType != TableOperationType.DELETE && etagFromHeader != null) {
                resObj.setEtag(etagFromHeader);
                resObj.updateResultObject(this.getEntity());
            }
        }

        return resObj;
    }

    /**
     * Reserved for internal use. Sets the {@link TableEntity} instance for the table operation.
     * 
     * @param entity
     *            The {@link TableEntity} instance to set.
     */
    protected synchronized final void setEntity(final TableEntity entity) {
        this.entity = entity;
    }

    /**
     * Gets the boolean representing whether the message payload should be returned in the response.
     * 
     * @return <code>true</code> if the message payload should be returned in the response; otherwise <code>false</code>
     */
    protected boolean getEchoContent() {
        return this.echoContent;
    }

    /**
     * Sets the boolean representing whether the message payload should be returned in the response.
     * 
     * @param echoContent
     *            <code>true</code> if the message payload should be returned in the response; otherwise
     *            <code>false</code>.
     */
    protected void setEchoContent(boolean echoContent) {
        this.echoContent = echoContent;
    }

} public class TableTestHelper extends TestHelper {

    public static String generateRandomTableName() {
        String tableName = "table" + UUID.randomUUID().toString().toLowerCase();
        return tableName.replace("-", "");
    }

    public static String generateRandomQueueName() {
        String queueName = "queue" + UUID.randomUUID().toString();
        return queueName.replace("-", "");
    }
    
    public static String generateRandomKeyName() {
        return "key" + UUID.randomUUID().toString();
    }

    public static CloudTable getRandomTableReference() throws URISyntaxException, StorageException {
        String tableName = generateRandomTableName();
        CloudTableClient tClient = createCloudTableClient();
        CloudTable table = tClient.getTableReference(tableName);

        return table;
    }

    public static Class1 generateRandomEntity(String pk) {
        Class1 ref = new Class1();

        ref.setA("foo_A");
        ref.setB("foo_B");
        ref.setC("foo_C");
        ref.setD(new byte[] { 0, 1, 2 });
        ref.setPartitionKey(pk);
        ref.setRowKey(UUID.randomUUID().toString());
        return ref;
    }

    public static class Class1 extends TableServiceEntity implements PropertyResolver {

        public String A;

        public String B;

        public String C;

        public byte[] D;

        public Class1() {
            // empty ctor
        }

        public Class1(String pk, String rk) {
            super(pk, rk);
        }

        public synchronized String getA() {
            return this.A;
        }

        public synchronized String getB() {
            return this.B;
        }

        public synchronized String getC() {
            return this.C;
        }

        public synchronized byte[] getD() {
            return this.D;
        }

        public synchronized void setA(final String a) {
            this.A = a;
        }

        public synchronized void setB(final String b) {
            this.B = b;
        }

        public synchronized void setC(final String c) {
            this.C = c;
        }

        public synchronized void setD(final byte[] d) {
            this.D = d;
        }

        @Override
        public EdmType propertyResolver(String pk, String rk, String key, String value) {
            if (key.equals("A")) {
                return EdmType.STRING;
            }
            else if (key.equals("B")) {
                return EdmType.STRING;
            }
            else if (key.equals("C")) {
                return EdmType.STRING;
            }
            else if (key.equals("D")) {
                return EdmType.BINARY;
            }
            return null;
        }
    }

    public static class Class2 extends TableServiceEntity implements PropertyResolver {
        private String L;
        private String M;

        private String N;

        private String O;

        /**
         * @return the l
         */
        public String getL() {
            return this.L;
        }

        /**
         * @return the m
         */
        public String getM() {
            return this.M;
        }

        /**
         * @return the n
         */
        public String getN() {
            return this.N;
        }

        /**
         * @return the o
         */
        public String getO() {
            return this.O;
        }

        /**
         * @param l
         *            the l to set
         */
        public void setL(String l) {
            this.L = l;
        }

        /**
         * @param m
         *            the m to set
         */
        public void setM(String m) {
            this.M = m;
        }

        /**
         * @param n
         *            the n to set
         */
        public void setN(String n) {
            this.N = n;
        }

        /**
         * @param o
         *            the o to set
         */
        public void setO(String o) {
            this.O = o;
        }

        @Override
        public EdmType propertyResolver(String pk, String rk, String key, String value) {
            if (key.equals("L")) {
                return EdmType.STRING;
            }
            else if (key.equals("M")) {
                return EdmType.STRING;
            }
            else if (key.equals("N")) {
                return EdmType.STRING;
            }
            else if (key.equals("O")) {
                return EdmType.STRING;
            }
            return null;
        }
    }

    public static class EmptyClass extends TableServiceEntity implements PropertyResolver {

        @Override
        public EdmType propertyResolver(String pk, String rk, String key, String value) {
            return null;
        }
    }

    public static class EmptyClassDynamic extends DynamicTableEntity implements PropertyResolver {

        @Override
        public EdmType propertyResolver(String pk, String rk, String key, String value) {
            return null;
        }
    }

    public static class class1class2PropertyResolver implements PropertyResolver {

        @Override
        public EdmType propertyResolver(String pk, String rk, String key, String value) {
            Class1 class1Reference = new Class1();
            Class2 class2Reference = new Class2();
            EdmType type = class1Reference.propertyResolver(pk, rk, key, value);
            if (type == null) {
                type = class2Reference.propertyResolver(pk, rk, key, value);
            }
            return type;
        }
    }

    public static class StrangeDoubles extends TableServiceEntity implements PropertyResolver {
        private double regularPrimDouble = -1;
        private double nanPrimDouble = -1;
        private double positiveInfinityPrimDouble = -1;
        private double negativeInfinityPrimDouble = -1;
        private double minValuePrimDouble = -1;
        private double maxValuePrimDouble = -1;
        private double minExponentValuePrimDouble = -1;
        private double maxExponentValuePrimDouble = -1;
        private double minNormalValuePrimDouble = -1;
        private double zeroValuePrimDouble = -1;
        private double negativeZeroValuePrimDouble = -1;
        private Double regularDouble = null;
        private Double nanDouble = null;
        private Double positiveInfinityDouble = null;
        private Double negativeInfinityDouble = null;
        private Double minValueDouble = null;
        private Double maxValueDouble = null;
        private Double minExponentValueDouble = null;
        private Double maxExponentValueDouble = null;
        private Double minNormalValueDouble = null;
        private Double zeroValueDouble = null;
        private Double negativeZeroValueDouble = null;

        public void populateEntity() {
            // set primitives
            this.setRegularPrimDouble(5);
            this.setNanPrimDouble(Double.NaN);
            this.setPositiveInfinityPrimDouble(Double.POSITIVE_INFINITY);
            this.setNegativeInfinityPrimDouble(Double.NEGATIVE_INFINITY);
            this.setMinValuePrimDouble(Double.MIN_VALUE);
            this.setMaxValuePrimDouble(Double.MAX_VALUE);
            this.setMinExponentValuePrimDouble(Double.MIN_EXPONENT);
            this.setMaxExponentValuePrimDouble(Double.MAX_EXPONENT);
            this.setMinNormalValuePrimDouble(Double.MIN_NORMAL);
            this.setZeroValuePrimDouble(0.0);
            // this.setNegativeZeroValuePrimDouble(-0.0);

            // set objects
            this.setRegularDouble(new Double(5));
            this.setNanDouble(Double.NaN);
            this.setPositiveInfinityDouble(Double.POSITIVE_INFINITY);
            this.setNegativeInfinityDouble(Double.NEGATIVE_INFINITY);
            this.setMinValueDouble(Double.MIN_VALUE);
            this.setMaxValueDouble(Double.MAX_VALUE);
            this.setMinExponentValueDouble(new Double(Double.MIN_EXPONENT));
            this.setMaxExponentValueDouble(new Double(Double.MAX_EXPONENT));
            this.setMinNormalValueDouble(Double.MIN_NORMAL);
            this.setZeroValueDouble(0.0);
            // this.setNegativeZeroValueDouble(-0.0);
        }

        @Override
        public EdmType propertyResolver(String pk, String rk, String key, String value) {
            return EdmType.DOUBLE;
        }

        public void assertEquality(StrangeDoubles other) {
            Assert.assertEquals(this.getPartitionKey(), other.getPartitionKey());
            Assert.assertEquals(this.getRowKey(), other.getRowKey());

            // compare primitives
            Assert.assertEquals(this.getRegularPrimDouble(), other.getRegularPrimDouble(), 1.0e-10);
            Assert.assertEquals(this.getNanPrimDouble(), other.getNanPrimDouble(), 1.0e-10);
            Assert.assertEquals(this.getPositiveInfinityPrimDouble(), other.getPositiveInfinityPrimDouble(), 1.0e-10);
            Assert.assertEquals(this.getNegativeInfinityPrimDouble(), other.getNegativeInfinityPrimDouble(), 1.0e-10);
            Assert.assertEquals(this.getMinValuePrimDouble(), other.getMinValuePrimDouble(), 1.0e-10);
            Assert.assertEquals(this.getMaxValuePrimDouble(), other.getMaxValuePrimDouble(), 1.0e-10);
            Assert.assertEquals(this.getMinExponentValuePrimDouble(), other.getMinExponentValuePrimDouble(), 1.0e-10);
            Assert.assertEquals(this.getMaxExponentValuePrimDouble(), other.getMaxExponentValuePrimDouble(), 1.0e-10);
            Assert.assertEquals(this.getMinNormalValuePrimDouble(), other.getMinNormalValuePrimDouble(), 1.0e-10);
            Assert.assertEquals(this.getZeroValuePrimDouble(), other.getZeroValuePrimDouble(), 1.0e-10);

            // server sets -0.0 to 0.0
            // Assert.assertEquals(this.getNegativeZeroValuePrimDouble(), other.getNegativeZeroValuePrimDouble(), 1.0e-10);

            // compare objects
            Assert.assertEquals(this.getRegularDouble(), other.getRegularDouble());
            Assert.assertEquals(this.getNanDouble(), other.getNanDouble());
            Assert.assertEquals(this.getPositiveInfinityDouble(), other.getPositiveInfinityDouble());
            Assert.assertEquals(this.getNegativeInfinityDouble(), other.getNegativeInfinityDouble());
            Assert.assertEquals(this.getMinValueDouble(), other.getMinValueDouble());
            Assert.assertEquals(this.getMaxValueDouble(), other.getMaxValueDouble());
            Assert.assertEquals(this.getMinExponentValueDouble(), other.getMinExponentValueDouble());
            Assert.assertEquals(this.getMaxExponentValueDouble(), other.getMaxExponentValueDouble());
            Assert.assertEquals(this.getMinNormalValueDouble(), other.getMinNormalValueDouble());
            Assert.assertEquals(this.getZeroValueDouble(), other.getZeroValueDouble());

            // server sets -0.0 to 0.0
            // Assert.assertEquals(this.getNegativeZeroValueDouble(), other.getNegativeZeroValueDouble());
        }

        public StrangeDoubles() {
            // Empty Ctor
        }

        public double getRegularPrimDouble() {
            return this.regularPrimDouble;
        }

        public double getNanPrimDouble() {
            return this.nanPrimDouble;
        }

        public double getPositiveInfinityPrimDouble() {
            return this.positiveInfinityPrimDouble;
        }

        public double getNegativeInfinityPrimDouble() {
            return this.negativeInfinityPrimDouble;
        }

        public double getMinValuePrimDouble() {
            return this.minValuePrimDouble;
        }

        public double getMaxValuePrimDouble() {
            return this.maxValuePrimDouble;
        }

        public double getMinExponentValuePrimDouble() {
            return this.minExponentValuePrimDouble;
        }

        public double getMaxExponentValuePrimDouble() {
            return this.maxExponentValuePrimDouble;
        }

        public double getMinNormalValuePrimDouble() {
            return this.minNormalValuePrimDouble;
        }

        public Double getRegularDouble() {
            return this.regularDouble;
        }

        public Double getNanDouble() {
            return this.nanDouble;
        }

        public Double getPositiveInfinityDouble() {
            return this.positiveInfinityDouble;
        }

        public Double getNegativeInfinityDouble() {
            return this.negativeInfinityDouble;
        }

        public Double getMinValueDouble() {
            return this.minValueDouble;
        }

        public Double getMaxValueDouble() {
            return this.maxValueDouble;
        }

        public Double getMinExponentValueDouble() {
            return this.minExponentValueDouble;
        }

        public Double getMaxExponentValueDouble() {
            return this.maxExponentValueDouble;
        }

        public Double getMinNormalValueDouble() {
            return this.minNormalValueDouble;
        }

        public void setRegularPrimDouble(double regularPrimDouble) {
            this.regularPrimDouble = regularPrimDouble;
        }

        public void setNanPrimDouble(double nanPrimDouble) {
            this.nanPrimDouble = nanPrimDouble;
        }

        public void setPositiveInfinityPrimDouble(double positiveInfinityPrimDouble) {
            this.positiveInfinityPrimDouble = positiveInfinityPrimDouble;
        }

        public void setNegativeInfinityPrimDouble(double negativeInfinityPrimDouble) {
            this.negativeInfinityPrimDouble = negativeInfinityPrimDouble;
        }

        public void setMinValuePrimDouble(double minValuePrimDouble) {
            this.minValuePrimDouble = minValuePrimDouble;
        }

        public void setMaxValuePrimDouble(double maxValuePrimDouble) {
            this.maxValuePrimDouble = maxValuePrimDouble;
        }

        public void setMinExponentValuePrimDouble(double minExponentValuePrimDouble) {
            this.minExponentValuePrimDouble = minExponentValuePrimDouble;
        }

        public void setMaxExponentValuePrimDouble(double maxExponentValuePrimDouble) {
            this.maxExponentValuePrimDouble = maxExponentValuePrimDouble;
        }

        public void setMinNormalValuePrimDouble(double minNormalValuePrimDouble) {
            this.minNormalValuePrimDouble = minNormalValuePrimDouble;
        }

        public void setRegularDouble(Double regularDouble) {
            this.regularDouble = regularDouble;
        }

        public void setNanDouble(Double nanDouble) {
            this.nanDouble = nanDouble;
        }

        public void setPositiveInfinityDouble(Double positiveInfinityDouble) {
            this.positiveInfinityDouble = positiveInfinityDouble;
        }

        public void setNegativeInfinityDouble(Double negativeInfinityDouble) {
            this.negativeInfinityDouble = negativeInfinityDouble;
        }

        public void setMinValueDouble(Double minValueDouble) {
            this.minValueDouble = minValueDouble;
        }

        public void setMaxValueDouble(Double maxValueDouble) {
            this.maxValueDouble = maxValueDouble;
        }

        public void setMinExponentValueDouble(Double minExponentValueDouble) {
            this.minExponentValueDouble = minExponentValueDouble;
        }

        public void setMaxExponentValueDouble(Double maxExponentValueDouble) {
            this.maxExponentValueDouble = maxExponentValueDouble;
        }

        public void setMinNormalValueDouble(Double minNormalValueDouble) {
            this.minNormalValueDouble = minNormalValueDouble;
        }

        public double getZeroValuePrimDouble() {
            return this.zeroValuePrimDouble;
        }

        public double getNegativeZeroValuePrimDouble() {
            return this.negativeZeroValuePrimDouble;
        }

        public Double getZeroValueDouble() {
            return this.zeroValueDouble;
        }

        public Double getNegativeZeroValueDouble() {
            return this.negativeZeroValueDouble;
        }

        public void setZeroValuePrimDouble(double zeroValuePrimDouble) {
            this.zeroValuePrimDouble = zeroValuePrimDouble;
        }

        public void setNegativeZeroValuePrimDouble(double negativeZeroValuePrimDouble) {
            this.negativeZeroValuePrimDouble = negativeZeroValuePrimDouble;
        }

        public void setZeroValueDouble(Double zeroValueDouble) {
            this.zeroValueDouble = zeroValueDouble;
        }

        public void setNegativeZeroValueDouble(Double negativeZeroValueDouble) {
            this.negativeZeroValueDouble = negativeZeroValueDouble;
        }
    }

    public static class ComplexEntity extends TableServiceEntity implements PropertyResolver {
        private Date dateTime = null;
        private Boolean Bool = null;
        private boolean BoolPrimitive = false;
        private Byte[] Binary = null;
        private byte[] binaryPrimitive = null;
        private double DoublePrimitive = -1;
        private Double Double = null;
        private UUID Guid = null;
        private int IntegerPrimitive = -1;
        private Integer Int32 = null;
        private long LongPrimitive = -1L;
        private Long Int64 = null;
        private String String = null;

        @Override
        public EdmType propertyResolver(String pk, String rk, String key, String value) {
            if (key.equals("DateTime")) {
                return EdmType.DATE_TIME;
            }
            else if (key.equals("Bool")) {
                return EdmType.BOOLEAN;
            }
            else if (key.equals("BoolPrimitive")) {
                return EdmType.BOOLEAN;
            }
            else if (key.equals("Binary")) {
                return EdmType.BINARY;
            }
            else if (key.equals("BinaryPrimitive")) {
                return EdmType.BINARY;
            }
            else if (key.equals("DoublePrimitive")) {
                return EdmType.DOUBLE;
            }
            else if (key.equals("Double")) {
                return EdmType.DOUBLE;
            }
            else if (key.equals("Guid")) {
                return EdmType.GUID;
            }
            else if (key.equals("IntegerPrimitive")) {
                return EdmType.INT32;
            }
            else if (key.equals("Int32")) {
                return EdmType.INT32;
            }
            else if (key.equals("LongPrimitive")) {
                return EdmType.INT64;
            }
            else if (key.equals("Int64")) {
                return EdmType.INT64;
            }
            else if (key.equals("String")) {
                return EdmType.STRING;
            }
            return null;
        }

        public ComplexEntity() {
            // Empty Ctor
        }

        public void assertEquality(ComplexEntity other) {
            Assert.assertEquals(this.getPartitionKey(), other.getPartitionKey());
            Assert.assertEquals(this.getRowKey(), other.getRowKey());

            Assert.assertEquals(this.getDateTime().toString(), other.getDateTime().toString());

            Assert.assertEquals(this.getGuid(), other.getGuid());
            Assert.assertEquals(this.getString(), other.getString());

            Assert.assertEquals(this.getDouble(), other.getDouble(), 1.0e-10);
            Assert.assertEquals(this.getDoublePrimitive(), other.getDoublePrimitive(), 1.0e-10);
            Assert.assertEquals(this.getInt32(), other.getInt32());
            Assert.assertEquals(this.getIntegerPrimitive(), other.getIntegerPrimitive());
            Assert.assertEquals(this.getBool(), other.getBool());
            Assert.assertEquals(this.getBoolPrimitive(), other.getBoolPrimitive());
            Assert.assertEquals(this.getInt64(), other.getInt64());
            Assert.assertEquals(this.getIntegerPrimitive(), other.getIntegerPrimitive());
            Assert.assertTrue(Arrays.equals(this.getBinary(), other.getBinary()));
            Assert.assertTrue(Arrays.equals(this.getBinaryPrimitive(), other.getBinaryPrimitive()));
        }

        protected void assertDateApproxEquals(Date expected, Date actual, int deltaInMs) {
            if (expected == null || actual == null) {
                Assert.assertEquals(expected, actual);
            }
            else {
                long diffInMilliseconds = Math.abs(expected.getTime() - actual.getTime());
                if (diffInMilliseconds > deltaInMs) {
                    Assert.assertEquals(expected, actual);
                }
            }
        }

        /**
         * @return the binary
         */
        public Byte[] getBinary() {
            return this.Binary;
        }

        /**
         * @return the binaryPrimitive
         */
        public byte[] getBinaryPrimitive() {
            return this.binaryPrimitive;
        }

        /**
         * @return the bool
         */
        public Boolean getBool() {
            return this.Bool;
        }

        /**
         * @return the bool
         */
        public boolean getBoolPrimitive() {
            return this.BoolPrimitive;
        }

        /**
         * @return the dateTime
         */
        public Date getDateTime() {
            return this.dateTime;
        }

        /**
         * @return the double
         */
        public Double getDouble() {
            return this.Double;
        }

        /**
         * @return the doublePrimitive
         */
        public double getDoublePrimitive() {
            return this.DoublePrimitive;
        }

        /**
         * @return the guid
         */
        public UUID getGuid() {
            return this.Guid;
        }

        /**
         * @return the int32
         */
        public Integer getInt32() {
            return this.Int32;
        }

        /**
         * @return the int64
         */
        public Long getInt64() {
            return this.Int64;
        }

        /**
         * @return the integerPrimitive
         */
        public int getIntegerPrimitive() {
            return this.IntegerPrimitive;
        }

        /**
         * @return the longPrimitive
         */
        public long getLongPrimitive() {
            return this.LongPrimitive;
        }

        /**
         * @return the string
         */
        public String getString() {
            return this.String;
        }

        public void populateEntity() {
            this.setBinary(new Byte[] { 1, 2, 3, 4 });
            this.setBinaryPrimitive(new byte[] { 1, 2, 3, 4 });
            this.setBool(true);
            this.setBoolPrimitive(true);
            this.setDateTime(new Date());
            this.setDouble(2342.2342);
            this.setDoublePrimitive(2349879.2342);
            this.setInt32(2342);
            this.setInt64((long) 87987987);
            this.setIntegerPrimitive(2342);
            this.setLongPrimitive(87987987);
            this.setGuid(UUID.randomUUID());
            this.setString("foo");
        }

        /**
         * @param binary
         *            the binary to set
         */
        public void setBinary(final Byte[] binary) {
            this.Binary = binary;
        }

        /**
         * @param binaryPrimitive
         *            the binaryPrimitive to set
         */
        public void setBinaryPrimitive(byte[] binaryPrimitive) {
            this.binaryPrimitive = binaryPrimitive;
        }

        /**
         * @param bool
         *            the bool to set
         */
        public void setBool(final Boolean bool) {
            this.Bool = bool;
        }

        /**
         * @param boolPrimitive
         *            the boolPrimitive to set
         */
        public void setBoolPrimitive(boolean boolPrimitive) {
            this.BoolPrimitive = boolPrimitive;
        }

        /**
         * @param dateTime
         *            the dateTime to set
         */
        public void setDateTime(final Date dateTime) {
            this.dateTime = dateTime;
        }

        /**
         * @param d
         *            the double to set
         */
        public void setDouble(final Double d) {
            this.Double = d;
        }

        /**
         * @param doublePrimitive
         *            the doublePrimitive to set
         */
        public void setDoublePrimitive(double doublePrimitive) {
            this.DoublePrimitive = doublePrimitive;
        }

        /**
         * @param guid
         *            the guid to set
         */
        public void setGuid(final UUID guid) {
            this.Guid = guid;
        }

        /**
         * @param int32
         *            the int32 to set
         */
        public void setInt32(final Integer int32) {
            this.Int32 = int32;
        }

        /**
         * @param int64
         *            the int64 to set
         */
        public void setInt64(final Long int64) {
            this.Int64 = int64;
        }

        /**
         * @param integerPrimitive
         *            the integerPrimitive to set
         */
        public void setIntegerPrimitive(int integerPrimitive) {
            this.IntegerPrimitive = integerPrimitive;
        }

        /**
         * @param longPrimitive
         *            the longPrimitive to set
         */
        public void setLongPrimitive(long longPrimitive) {
            this.LongPrimitive = longPrimitive;
        }

        /**
         * @param string
         *            the string to set
         */
        public void setString(final String string) {
            this.String = string;
        }
    }

    public static class IgnoreOnGetter extends Class1 {
        private String tString = null;

        /**
         * @return the string
         */
        @Ignore
        public String getIgnoreString() {
            return this.tString;
        }

        /**
         * @param string
         *            the string to set
         */

        public void setIgnoreString(final String string) {
            this.tString = string;
        }
    }

    public static class IgnoreOnGetterAndSetter extends Class1 {
        private String tString = null;

        /**
         * @return the string
         */
        @Ignore
        public String getIgnoreString() {
            return this.tString;
        }

        /**
         * @param string
         *            the string to set
         */
        @Ignore
        public void setIgnoreString(final String string) {
            this.tString = string;
        }
    }

    public static class IgnoreOnSetter extends Class1 {
        private String tString = null;

        /**
         * @return the string
         */
        public String getIgnoreString() {
            return this.tString;
        }

        /**
         * @param string
         *            the string to set
         */
        @Ignore
        public void setIgnoreString(final String string) {
            this.tString = string;
        }
    }

    public static class StoreAsEntity extends ComplexEntity {
        private String storeAsString = null;

        /**
         * @return the string
         */
        @StoreAs(name = "String")
        public String getStoreAsString() {
            return this.storeAsString;
        }

        /**
         * @param string
         *            the string to set
         */
        @StoreAs(name = "String")
        public void setStoreAsString(final String string) {
            this.storeAsString = string;
        }
    }

    public static class InvalidStoreAsEntity extends ComplexEntity {
        private String storeAsString = null;

        /**
         * @return the string
         */
        @StoreAs(name = "PartitionKey")
        public String getStoreAsString() {
            return this.storeAsString;
        }

        /**
         * @param string
         *            the string to set
         */
        @StoreAs(name = "PartitionKey")
        public void setStoreAsString(final String string) {
            this.storeAsString = string;
        }
    }

}
import com.microsoft.azure.storage.TestRunners.CloudTests;
import com.microsoft.azure.storage.TestRunners.DevFabricTests;
import com.microsoft.azure.storage.TestRunners.DevStoreTests;
import com.microsoft.azure.storage.blob.BlobTestHelper;
import com.microsoft.azure.storage.blob.BlobType;
import com.microsoft.azure.storage.blob.CloudAppendBlob;
import com.microsoft.azure.storage.blob.CloudBlobClient;
import com.microsoft.azure.storage.blob.CloudBlobContainer;
import com.microsoft.azure.storage.core.SR;
import com.microsoft.azure.storage.file.CloudFile;
import com.microsoft.azure.storage.file.CloudFileClient;
import com.microsoft.azure.storage.file.CloudFileShare;
import com.microsoft.azure.storage.file.FileTestHelper;
import com.microsoft.azure.storage.queue.CloudQueue;
import com.microsoft.azure.storage.queue.CloudQueueClient;
import com.microsoft.azure.storage.queue.CloudQueueMessage;
import com.microsoft.azure.storage.queue.MessageUpdateFields;
import com.microsoft.azure.storage.queue.QueueTestHelper;
import com.microsoft.azure.storage.table.CloudTable;
import com.microsoft.azure.storage.table.CloudTableClient;
import com.microsoft.azure.storage.table.DynamicTableEntity;
import com.microsoft.azure.storage.table.EntityProperty;
import com.microsoft.azure.storage.table.TableOperation;
import com.microsoft.azure.storage.table.TableTestHelper;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.security.InvalidKeyException;
import java.util.Calendar;
import java.util.Date;
import java.util.EnumSet;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.NoSuchElementException;
import java.util.TimeZone;
import static org.junit.Assert.*;
/**
 * Copyright Microsoft Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.microsoft.azure.storage;





@Category({ DevFabricTests.class, DevStoreTests.class, CloudTests.class })
public class AccountSasTests {
    private static final String DOES_NOT_EXIST_ERROR_MESSAGE = "The specified resource does not exist.";
    private static final String ENUMERATION_ERROR_MESSAGE =
            "An error occurred while enumerating the result, check the original exception for details.";
    private static final String INVALID_PERMISSION_MESSAGE =
            "This request is not authorized to perform this operation using this permission.";
    private static final String INVALID_RESOURCE_TYPE_MESSAGE =
            "This request is not authorized to perform this operation using this resource type.";
    private static final String INVALID_SERVICE_MESSAGE =
            "This request is not authorized to perform this operation using this service.";
    private static final String QUERY_PARAM_MISSING_MESSAGE =
            "Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.";

    private static final int ADD_CODE =         0x1;
    private static final int CREATE_CODE =      0x2;
    private static final int DELETE_CODE =      0x4;
    private static final int LIST_CODE =        0x8;
    private static final int PROCESS_CODE =    0x10;
    private static final int READ_CODE =       0x20;
    private static final int UPDATE_CODE =     0x40;
    private static final int WRITE_CODE =      0x80;
    private static final int OBJECT_CODE =    0x100;
    private static final int CONTAINER_CODE = 0x200;
    private static final int SERVICE_CODE =   0x400;

    // 0x7ff
    private static final int FULL_PERMS_CODE =
            ADD_CODE | CREATE_CODE | DELETE_CODE | LIST_CODE | PROCESS_CODE | READ_CODE |
            UPDATE_CODE | WRITE_CODE | OBJECT_CODE | CONTAINER_CODE | SERVICE_CODE;
    private static final int EMPTY_PERMS_CODE = 0x0;

    private CloudBlobClient blobClient;
    private CloudBlobContainer blobContainer;

    private CloudFileClient fileClient;
    private CloudFileShare fileShare;

    private CloudQueueClient queueClient;
    private CloudQueue queueQueue;

    private CloudTableClient tableClient;
    private CloudTable tableTable;

    @Before
    public void accountSasTestMethodSetUp() throws URISyntaxException, StorageException, IOException {
        this.blobClient = TestHelper.createCloudBlobClient();
        this.blobContainer = BlobTestHelper.getRandomContainerReference();

        this.fileClient = TestHelper.createCloudFileClient();
        this.fileShare = FileTestHelper.getRandomShareReference();