/// <summary>
        /// Initializes a new instance of the <see cref="SubdirectoryFileSystem"/> class
        /// </summary>
        /// <param name="parentFileSystem">The file system to represent a subdirectory of.</param>
        /// <param name="dataStorePath">The (data store) path of the directory to represent.</param>
        public SubdirectoryFileSystem( IFileSystem parentFileSystem, string dataStorePath )
        {
            try
            {
                if( parentFileSystem.NullReference() )
                    throw new ArgumentNullException().StoreFileLine();

                if( !DataStore.IsValidPath(dataStorePath) ) // empty string is acceptable!
                    throw new ArgumentException().StoreFileLine();

                this.parentFileSystem = parentFileSystem;
                this.subDirPath = dataStorePath;
            }
            catch( Exception ex )
            {
                ex.StoreFileLine();
                ex.Store("parentFileSystemType", parentFileSystem.NullReference() ? null : parentFileSystem.GetType());
                ex.Store("dataStorePath", dataStorePath);
                throw;
            }
        }
        /// <summary>
        /// Returns all files recently created by this app instance.
        /// The results are in descending order (according to creation time).
        /// </summary>
        /// <param name="fileSystem">The abstract file system to search the root directory of.</param>
        /// <param name="maxFileAge">The maximum age of the files returned, or <c>null</c>.</param>
        /// <param name="maxAppInstanceCount">The maximum number of app instances to include, or <c>null</c>.</param>
        /// <param name="maxTotalFileSize">The maximum total file size, or <c>null</c>.</param>
        /// <returns>All files recently created by this app instance.</returns>
        public static ProlificDataFileInfo[] FindLatestFiles( IFileSystem fileSystem, TimeSpan? maxFileAge, int? maxAppInstanceCount, long? maxTotalFileSize )
        {
            if( fileSystem.NullReference() )
                throw new ArgumentNullException().StoreFileLine();

            if( maxFileAge.HasValue
             && maxFileAge.Value.Ticks < 0 )
                throw new ArgumentOutOfRangeException().Store("maxFileAge", maxFileAge);

            if( maxAppInstanceCount.HasValue
             && maxAppInstanceCount.Value < 0 )
                throw new ArgumentOutOfRangeException().Store("maxAppInstanceCount", maxAppInstanceCount);

            if( maxTotalFileSize.HasValue )
            {
                if( maxTotalFileSize.Value < 0 )
                    throw new ArgumentOutOfRangeException().Store("maxTotalFileSize", maxTotalFileSize);

                if( !fileSystem.SupportsGetFileSize )
                    throw new NotSupportedException("The specified file system does not support GetFileSize!").StoreFileLine();
            }


            // sort in descending order by CreationTime
            var files = FindAllFiles(fileSystem).ToList();
            files.Sort(( x, y ) => y.CreationTime.CompareTo(x.CreationTime));

            // determine the minimum CreationTime (if there is one)
            DateTime? minTime = null;
            if( maxFileAge.HasValue )
                minTime = DateTime.UtcNow - maxFileAge.Value;

            HashSet<string> appInstancesAllowed = null;
            if( maxAppInstanceCount.HasValue )
                appInstancesAllowed = new HashSet<string>(AppInstanceIDComparer);

            long totalFileSize = 0;
            long currentFileSize;

            ProlificDataFileInfo fileInfo;
            for( int i = 0; i < files.Count; )
            {
                fileInfo = files[i];

                // too old?
                if( minTime.HasValue
                 && fileInfo.CreationTime < minTime )
                {
                    files.RemoveAt(i);
                    continue;
                }

                // too many app instances?
                if( maxAppInstanceCount.HasValue
                 && !appInstancesAllowed.Contains(fileInfo.AppInstanceID) )
                {
                    if( appInstancesAllowed.Count < maxAppInstanceCount.Value )
                    {
                        //// there is still room for one more app instance,
                        //// but we do not record the new app instance ID here,
                        //// since another rule may still throw this file out...
                    }
                    else
                    {
                        // a yet unseen app instance ID, but we have no more room for it
                        files.RemoveAt(i);
                        continue;
                    }
                }

                // too many bytes?
                if( maxTotalFileSize.HasValue )
                {
                    currentFileSize = fileSystem.GetFileSize(fileInfo.DataStoreName);
                    if( totalFileSize + currentFileSize > maxTotalFileSize.Value )
                    {
                        // adding this file would exceed the file size limit, so we'll skip it
                        files.RemoveAt(i);

                        // we will also raise the total file size recorded beyond the maximum:
                        // it could be possible that later there would be a file small
                        // enough to still fit in, but an unbroken time line seems more
                        // important than cramming as many bytes in as possible...
                        // It's just a feeling though, maybe this is stupid :)
                        totalFileSize = maxTotalFileSize.Value + 1;
                        continue;
                    }
                    else
                    {
                        //// there is still room for these bytes,
                        //// but we do not update the total file size here,
                        //// since another rule may still throw this file out...
                        //// (one added at a later time)
                    }
                }
                else
                    currentFileSize = 0;

                // no rule removed this file: update variables and continue
                appInstancesAllowed.Add(fileInfo.AppInstanceID);
                totalFileSize += currentFileSize;
                ++i;
            }

            return files.ToArray();
        }
        /// <summary>
        /// Finds all data files in the root of the specified file system. The result is unsorted.
        /// </summary>
        /// <param name="fileSystem">The abstract file system to search the root directory of.</param>
        /// <returns>All data files found.</returns>
        public static ProlificDataFileInfo[] FindAllFiles( IFileSystem fileSystem )
        {
            if( fileSystem.NullReference() )
                throw new ArgumentNullException().StoreFileLine();

            var fileDataStoreNames = fileSystem.GetFileNames();
            var results = new List<ProlificDataFileInfo>();
            ProlificDataFileInfo fileInfo;
            foreach( var name in fileDataStoreNames )
            {
                if( TryParseDataStoreName(name, fileSystem.EscapesNames, out fileInfo) )
                    results.Add(fileInfo);
            }

            return results.ToArray();
        }