/// <summary> /// Set custom property value. /// </summary> /// <param name="name"> /// Property name. /// </param> /// <param name="value"> /// Property value. Specify null to remove property. /// </param> /// <remarks> /// Every repository folder can have an arbitrary set of named string properties. This should not be overused as the storage is not optimised for performance and /// large volumes of data. /// If property with the specified name is already set it will be overwritten. To remove a propert set its value to null. /// The value is immediately saved to disk. /// </remarks> /// <seealso cref="GetCustomProperty(string)"/> public void SetCustomProperty(string name, string value) { RepositoryFolder.CheckNotDetached(_folder); Check.RequireArgumentNotNull(name, "name"); NameValuePair pair = FindCustomProperty(name); if (pair != null) { if (value == null) { _folderConfig.Config.CustomParameters.Remove(pair); } else { pair.Value = value; } } else { _folderConfig.Config.CustomParameters.Add(new NameValuePair() { Name = name, Value = value }); } Save(); }
/// <summary> /// Initialise iteration position. /// </summary> /// <param name="seekTime"> /// Data timestamp; files containig data items with the timestamp will be iterated. /// </param> /// <param name="backwards"> /// Direction in which to search for data files if the <paramref name="seekTime"/> is not covered by any /// existing data file /// </param> /// <returns> /// First found data file; null if none found. /// </returns> /// <remarks> /// The data in a data file does not have to be entirely in the specified datetime range for the file to be iterated. If any data in the file /// falls in the sought range it will be returned by the iterator. /// </remarks> /// <exception cref="ObjectDisposedException"> /// The target repository instance is disposed. /// </exception> /// <exception cref="InvalidOperationException"> /// The <paramref name="targetFolder"/> is detached. /// </exception> public IRepositoryFile Seek(DateTime seekTime, bool backwards) { RepositoryFolder.CheckNotDetached(Folder); DateTime seekTimeCorrected; IRepositoryFile currentFile = _targetFolder.RootDataFolder.Seek(seekTime, backwards); if (currentFile != null) { seekTimeCorrected = currentFile.Name.FirstItemTimestamp; } else { seekTimeCorrected = seekTime; } SeekExact(seekTimeCorrected); Check.Ensure(Current == null || Current.Name == currentFile.Name, "Seek results differ"); Check.Ensure(Current == null || GetComparer(backwards).Compare( backwards ? Current.Name.FirstItemTimestamp : Current.Name.LastItemTimestamp , seekTime ) >= 0 ); Check.Ensure(NextBackwards == null || NextBackwards.Name.End <= seekTime); Check.Ensure(NextForward == null || NextForward.Name.FirstItemTimestamp > seekTime); return(Current); }
/// <summary> /// Get data file iterator. /// </summary> /// <param name="folder"> /// Folder whose data files to iterate over. /// </param> /// <param name="backwards"> /// Initial iteration direction. /// </param> /// <returns> /// <see cref="IDataFileIterator"/> /// </returns> /// <remarks> /// Note that you must call one of the Seek methods to start iteration after the iterator instance is created. /// </remarks> public IDataFileIterator GetDataFileIterator(IRepositoryFolder folder, bool backwards) { CheckHelper.CheckRepositoryNotDisposed(Repository); Check.DoRequireArgumentNotNull(folder, "folder"); RepositoryFolder.CheckNotDetached(folder); return(new DataFileIterator(folder, backwards)); }
/// <summary> /// Create new instance. /// </summary> /// <param name="targetFolder"> /// Target repository folder. /// </param> /// <param name="backwards"> /// Whether to iterate backwards. /// </param> /// <exception cref="ObjectDisposedException"> /// The target repository instance is disposed. /// </exception> /// <exception cref="InvalidOperationException"> /// The <paramref name="targetFolder"/> is detached. /// </exception> public DataFileIterator(IRepositoryFolder targetFolder, bool backwards) { RepositoryFolder.CheckNotDetached(targetFolder); _targetFolder = RepositoryFolder.CastFolder(targetFolder); _backwards = backwards; Current = NextForward = NextBackwards = Next = Previous = null; }
/// <summary> /// Only set current if owns the <paramref name="seekTime"/>. Set next and previous as per <paramref name="seekTime"/> /// </summary> /// <param name="seekTime"> /// Data timestamp /// </param> /// <returns> /// Existing <see cref="IRepositoryFile"/> if it covers the <paramref name="seekTime"/>. /// <see langword="null"/> otherwise /// </returns> /// <exception cref="ObjectDisposedException"> /// The target repository instance is disposed. /// </exception> /// <exception cref="InvalidOperationException"> /// The <paramref name="targetFolder"/> is detached. /// </exception> public IRepositoryFile SeekExact(DateTime seekTime) { RepositoryFolder.CheckNotDetached(Folder); IRepositoryFile owner; IRepositoryFile predecessor; IRepositoryFile successor; _targetFolder.RootDataFolder.CutDataFiles(seekTime, out predecessor, out owner, out successor); Current = owner; NextBackwards = predecessor; NextForward = successor; if (Backwards) { Next = NextBackwards; Previous = NextForward; } else { Next = NextForward; Previous = NextBackwards; } return(Current); }
/// <summary> /// Create data file accessor instance. /// </summary> /// <param name="folder"> /// Data folder containig the file. /// </param> /// <param name="file"> /// Data file to be accessed; may not exist on disk. /// </param> /// <returns> /// New <see cref="IDataFileAccessor"/> instance. /// </returns> public IDataFileAccessor GetDataFileAccessor(IDataFolder folder, IRepositoryFileName file) { Check.DoRequireArgumentNotNull(folder, "folder"); Check.DoRequireArgumentNotNull(file, "file"); RepositoryFolder.CheckNotDetached(folder.RepoFolder); return(new RepositoryFileAccessor(folder, file)); }
/// <summary> /// Create new instance. /// </summary> /// <param name="containingFolder"> /// Leaf data folder object reference, mandatory. /// </param> /// <param name="fileName"> /// Object representing data file name, optional. /// </param> public RepositoryFile(IDataFolder containingFolder, IRepositoryFileName fileName) { Check.DoRequireArgumentNotNull(containingFolder, "containingFolder"); RepositoryFolder.CheckNotDetached(containingFolder.RepoFolder); ContainingFolder = containingFolder; Name = fileName; }
/// <summary> /// Create regular folder object instance /// </summary> /// <param name="parent"> /// Parent folder /// </param> /// <param name="name"> /// Folder name /// </param> /// <returns> /// New instance of regular repository folder /// </returns> public virtual IFolder GetFolder(IFolder parent, string name) { CheckHelper.CheckRepositoryNotDisposed(Repository); Check.DoRequireArgumentNotNull(parent, "parent"); RepositoryFolder.CheckNotDetached(parent); Exceptions.DifferentRepositoriesExceptionHelper.Check(Repository, parent.Repository); return(new RepositoryFolder(parent, name)); }
/// <summary> /// Force save to disk. /// </summary> public void Save() { RepositoryFolder.CheckNotDetached(_folder); using (var scope = StorageTransactionScope.Create(_folder.Repository)) { _folderConfig.Save(this.ConfigFilePath, this.FileProvider); scope.Complete(); } }
/// <summary> /// Delete properties from disk. /// </summary> public void Delete() { RepositoryFolder.CheckNotDetached(_folder); using (var scope = StorageTransactionScope.Create(_folder.Repository)) { if (ConfigFileExists) { this.FileProvider.Delete(this.ConfigFilePath); } scope.Complete(); } }
/// <summary> /// Get custom property value. /// </summary> /// <param name="name"> /// Property name. /// </param> /// <returns> /// Custom property value or null if not set. /// </returns> /// <remarks> /// Every repository folder can have an arbitrary set of named string properties. This should not be overused as the storage is not optimised for performance and /// large volumes of data. /// </remarks> /// <seealso cref="SetCustomProperty(string, string)"/> public string GetCustomProperty(string name) { RepositoryFolder.CheckNotDetached(_folder); string retval = null; NameValuePair pair = FindCustomProperty(name); if (pair != null) { retval = pair.Value; } return(retval); }
/// <summary> /// Get writer for this repository /// </summary> /// <param name="targetFolder"> /// Target root folder to write. Data items may go to descendants folders, /// <see cref="bfs.Repository.Interfaces.IDataItem.RelativePath"/> /// </param> /// <returns> /// New instance of <see cref="IRepositoryWriter"/>. /// </returns> /// <remarks> /// Only 1 writer can be created for a particular folder. /// </remarks> public virtual IRepositoryWriter GetWriter(IRepositoryFolder targetFolder) { CheckHelper.CheckRepositoryNotDisposed(Repository); Check.RequireArgumentNotNull(targetFolder, "targetFolder"); RepositoryWriter retval; lock (targetFolder) { RepositoryFolder.CheckNotDetached(targetFolder); Check.DoAssertLambda(!Repository.IsDataBeingWrittenTo(targetFolder, false), () => new InvalidOperationException(string.Format(StorageResources.WriterAlreadyExistsForFolder, targetFolder.LogicalPath))); retval = new RepositoryWriter(RepositoryFolder.CastFolder(targetFolder)); Repository.RegisterWriter(retval); } Check.Ensure(Repository.IsDataBeingWrittenTo(targetFolder, false)); return(retval); }
/// <summary> /// /// </summary> /// <param name="dataFolder"> /// Leaf data folder in which the data files will be accessed. /// </param> /// <param name="repoFile"> /// Target repo file, optional. New instance will be created using object factory if null is supplied. /// </param> /// <param name="coder"> /// Compressor instance /// </param> /// <param name="equalTimestampedItemsComparer"> /// Comparer to use when sorting data items for items with equal timestamps. /// When timestamps are not equal the comparer has no effect. /// </param> internal RepositoryFileAccessor( IDataFolder dataFolder , IRepositoryFileName repoFile , ICoder coder , IComparer <IDataItem> equalTimestampedItemsComparer) { Check.DoRequireArgumentNotNull(dataFolder, "dataFolder"); Check.DoRequireArgumentNotNull(repoFile, "repoFile"); Check.DoRequireArgumentNotNull(coder, "coder"); RepositoryFolder.CheckNotDetached(dataFolder.RepoFolder); _folder = dataFolder.RepoFolder; _repoFile = repoFile; _dataFolder = dataFolder; if (_repoFile == null) { _repoFile = _folder.Repository.ObjectFactory.CreateNewFile(_folder); } this.Coder = coder; if (repoFile.Encrypted) { this.Encryptor = _folder.Repository.ObjectFactory.GetEncryptor(repoFile.EncryptorCode); } if (_folder.Repository.ObjectFactory.FileSystemProvider.FileProvider.Exists(this.FilePath)) { _existinglFilePath = this.FilePath; } else { _dataItems = new List <IDataItem>(); } _isSorted = true; this.EqualTimestampItemsComparer = equalTimestampedItemsComparer; FirstItemTimestamp = DateTime.MaxValue; LastItemTimestamp = DateTime.MinValue; }
/// <summary> /// Move current file to the next file. /// </summary> /// <param name="backwards"> /// The direction in which to move /// </param> /// <returns> /// New current file (either <see cref="NextForward"/> or <see cref="NextBackwards"/> prior to the call to this method) /// </returns> /// <remarks> /// Does nothing if <see cref="Current"/> and <see cref="Next"/> are already <see langword="null"/>. /// Otherwise the iterator will not be able to go back. /// </remarks> /// <exception cref="ObjectDisposedException"> /// The target repository instance is disposed. /// </exception> /// <exception cref="InvalidOperationException"> /// The <paramref name="targetFolder"/> is detached. /// </exception> public IRepositoryFile MoveNext(bool backwards) { RepositoryFolder.CheckNotDetached(Folder); if (Current != null || Next != null) { Previous = Current; if (backwards) { NextForward = Current; Current = NextBackwards; Next = NextBackwards = GetNextNullSafe(Current, backwards); } else { NextBackwards = Current; Current = NextForward; Next = NextForward = GetNextNullSafe(Current, backwards); } } return(Current); }
/// <summary> /// Create single folder writer and add it to the collection of writers. /// </summary> /// <param name="targetFolder"> /// Target folder for writer /// </param> /// <returns> /// New initialised instance. If the method succeeds (does not throw exception) /// </returns> /// <remarks> /// Locking sequence: target folder lock start, then repo manager's registry lock/release, then direct writers collection lock/release, /// then target folder released. /// </remarks> private DirectSingleFolderWriter CreateWriter(IRepositoryFolder targetFolder, string normalisedRelativePath) { Check.RequireArgumentNotNull(targetFolder, "targetFolder"); RepositoryFolder.CheckNotDetached(targetFolder); IFolder folder = RepositoryFolder.CastFolder(targetFolder); Exceptions.DifferentRepositoriesExceptionHelper.Check(Folder.Repository, targetFolder.Repository); DirectSingleFolderWriter writer = null; ICoder compressor = GetCompressor(targetFolder: targetFolder); ICoder encryptor = GetEncryptor(targetFolder: targetFolder); lock (targetFolder) { Check.DoAssertLambda( object.ReferenceEquals(targetFolder, _repoFolder) || // this writer has already seized this folder as target !Folder.Repository.IsDataBeingWrittenTo(targetFolder, false), () => new InvalidOperationException(string.Format(StorageResources.WriterAlreadyExistsForFolder, targetFolder.LogicalPath))); writer = new DirectSingleFolderWriter(folder, compressor, encryptor); writer.EqualTimestampedItemsComparer = this.EqualTimestampedItemsComparer; lock (_directWritersByRelativePath) { _directWritersByRelativePath.Add(normalisedRelativePath, writer); _directWritersByFolderKey.Add(targetFolder.FolderKey, writer); } } writer.TrackUnsavedItems = TrackUnsavedItems; Check.Ensure(Repository.IsDataBeingWrittenTo(targetFolder, false)); return(writer); }
/// <summary> /// Load properties from disk. /// </summary> public void Load() { RepositoryFolder.CheckNotDetached(_folder); _folderConfig = RepoFolderXmlConfig.Load(this.ConfigFilePath, this.FileProvider); }