/// <summary>Returns whether object exists.</summary>
 public bool Exists()
 {
     if (string.IsNullOrEmpty(this.Key))
     {
         return(false);
     }
     this.FetchMetadata();
     return(ValidationHelper.GetBoolean(AbstractStockHelper <RequestStockHelper> .GetItem(STORAGE_KEY, this.Key + "|Exists", false), false));
 }
        /// <summary>Returns content length of the current object.</summary>
        private long GetLength()
        {
            string key = this.Key + "|Length";

            if (!AbstractStockHelper <RequestStockHelper> .Contains(STORAGE_KEY, key, false))
            {
                this.FetchMetadata();
            }
            return(ValidationHelper.GetLong(AbstractStockHelper <RequestStockHelper> .GetItem(STORAGE_KEY, key, false), 0L));
        }
        /// <summary>Returns E-tag of the current object.</summary>
        private string GetETag()
        {
            string key = this.Key + "|ETag";

            if (!AbstractStockHelper <RequestStockHelper> .Contains(STORAGE_KEY, key, false))
            {
                this.FetchMetadata();
            }
            return(ValidationHelper.GetString(AbstractStockHelper <RequestStockHelper> .GetItem(STORAGE_KEY, key, false), string.Empty));
        }
        /// <summary>Removes cached object's items from request cache.</summary>
        /// <param name="objectKey">Object key.</param>
        internal static void RemoveRequestCache(string objectKey)
        {
            string storageKey = S3ObjectInfo.STORAGE_KEY;

            AbstractStockHelper <RequestStockHelper> .Remove(storageKey, objectKey + "|Exists", false);

            AbstractStockHelper <RequestStockHelper> .Remove(storageKey, objectKey + "|Length", false);

            AbstractStockHelper <RequestStockHelper> .Remove(storageKey, objectKey + "|Metadata", false);

            AbstractStockHelper <RequestStockHelper> .Remove(storageKey, objectKey + "|ETag", false);

            AbstractStockHelper <RequestStockHelper> .DropStorage(STORAGE_KEY, false);
        }
        /// <summary>
        /// Downloads metadata from the cloud. It ensures that RequestStockHelper always contains the "Exists" key.
        /// </summary>
        private void FetchMetadata()
        {
            if (AbstractStockHelper <RequestStockHelper> .Contains(STORAGE_KEY, this.Key + "|Exists", false))
            {
                return;
            }
            GetObjectMetadataRequest request = new GetObjectMetadataRequest
            {
                BucketName = this.BucketName,
                Key        = this.Key
            };

            try
            {
                GetObjectMetadataResponse objectMetadata = this.S3Client.GetObjectMetadata(request);
                AbstractStockHelper <RequestStockHelper> .AddToStorage(STORAGE_KEY, this.Key + "|Length", objectMetadata.ContentLength, false);

                AbstractStockHelper <RequestStockHelper> .AddToStorage(STORAGE_KEY, this.Key + "|ETag", objectMetadata.ETag, false);

                AbstractStockHelper <RequestStockHelper> .AddToStorage(STORAGE_KEY, this.Key + "|Exists", true, false);

                this.Metadata = this.LoadMetadata(PathHelper.GetPathFromObjectKey(this.Key, true));
                if (!this.Metadata.ContainsKey(S3ObjectInfoProvider.LAST_WRITE_TIME))
                {
                    this.Metadata.Add(S3ObjectInfoProvider.LAST_WRITE_TIME, ValidationHelper.GetString(objectMetadata.LastModified, string.Empty, "en-us"));
                }
                FileDebug.LogFileOperation(PathHelper.GetPathFromObjectKey(this.Key, true), nameof(FetchMetadata), "Custom Amazon");
            }
            catch (AmazonS3Exception ex)
            {
                if (ex.StatusCode == HttpStatusCode.NotFound)
                {
                    AbstractStockHelper <RequestStockHelper> .AddToStorage(STORAGE_KEY, this.Key + "|Exists", false, false);
                }
                else
                {
                    throw;
                }
            }
        }
        /// <summary>
        /// Returns list with objects from given bucket and under given path.
        /// </summary>
        /// <param name="path">Path.</param>
        /// <param name="type">Specifies which objects are returned (files, directories, both).</param>
        /// <param name="useFlatListing">Whether flat listing is used (all files from all subdirectories all in the result).</param>
        /// <param name="lower">Specifies whether path should be lowered inside method.</param>
        /// <param name="useCache">Indicates if results should be primary taken from cache to get better performance</param>
        /// <remarks>
        /// In order to allow to distinguish between files and directories, directories are listed with a trailing backslash.
        /// </remarks>
        public List <string> GetObjectsList(string path, ObjectTypeEnum type, bool useFlatListing = false, bool lower = true, bool useCache = true)
        {
            string bucketName = GetBucketName(path);

            if (string.IsNullOrEmpty(bucketName))
            {
                return(null);
            }
            var request = new ListObjectsRequest
            {
                BucketName = bucketName
            };

            if (!string.IsNullOrEmpty(path))
            {
                request.Prefix = PathHelper.GetObjectKeyFromPath(path).TrimEnd('/') + "/";
            }
            if (!useFlatListing)
            {
                request.Delimiter = "/";
            }
            var    source1 = new HashSet <string>();
            string key     = $"{request.Prefix }|{type.ToString("F")}|{useFlatListing}|{lower}";

            if (useCache && AbstractStockHelper <RequestStockHelper> .Contains(STORAGE_KEY, key, false))
            {
                CMS.IO.Directory.LogDirectoryOperation(path, "GetObjectListFromCache", "Custom Amazon");
                var source2 = AbstractStockHelper <RequestStockHelper> .GetItem(STORAGE_KEY, key, false) as HashSet <string>;

                if (source2 == null)
                {
                    return(new List <string>());
                }
                return(source2.ToList());
            }
            ListObjectsResponse listObjectsResponse;

            do
            {
                listObjectsResponse = this.S3Client.ListObjects(request);
                if (type == ObjectTypeEnum.Directories && !useFlatListing)
                {
                    foreach (string commonPrefix in listObjectsResponse.CommonPrefixes)
                    {
                        source1.Add($"{path}\\{CMS.IO.Path.GetFileName(commonPrefix.TrimEnd('/'))}\\");
                    }
                }
                else
                {
                    bool isDirectory = type == ObjectTypeEnum.FilesAndDirectories || type == ObjectTypeEnum.Directories;
                    bool isFile      = type == ObjectTypeEnum.FilesAndDirectories || type == ObjectTypeEnum.Files;
                    foreach (S3Object s3Object in listObjectsResponse.S3Objects)
                    {
                        bool directory = s3Object.Key.EndsWith("/", StringComparison.Ordinal);
                        if (directory && isDirectory || !directory && isFile)
                        {
                            source1.Add(PathHelper.GetPathFromObjectKey(s3Object.Key, true, directory, lower));
                        }
                    }
                }
                if (listObjectsResponse.IsTruncated)
                {
                    request.Marker = listObjectsResponse.NextMarker;
                }
            }while (listObjectsResponse.IsTruncated);
            source1.Remove(PathHelper.GetPathFromObjectKey(request.Prefix, true, true, lower));
            CMS.IO.Directory.LogDirectoryOperation(path, "ListObjects", "Custom Amazon");
            AbstractStockHelper <RequestStockHelper> .AddToStorage(STORAGE_KEY, key, (object)source1, false);

            return(source1.ToList());
        }