using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using LibGit2Sharp.Core; using LibGit2Sharp.Core.Handles; namespace LibGit2Sharp { /// /// A collection of exposed in the . /// [DebuggerDisplay("{DebuggerDisplay,nq}")] public class NoteCollection : IEnumerable { internal readonly Repository repo; private readonly Lazy defaultNamespace; /// /// Needed for mocking purposes. /// protected NoteCollection() { } internal NoteCollection(Repository repo) { this.repo = repo; defaultNamespace = new Lazy(RetrieveDefaultNamespace); } #region Implementation of IEnumerable /// /// Returns an enumerator that iterates through the collection. /// /// An object that can be used to iterate through the collection. public virtual IEnumerator GetEnumerator() { return this[DefaultNamespace].GetEnumerator(); } /// /// Returns an enumerator that iterates through the collection. /// /// An object that can be used to iterate through the collection. IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } #endregion /// /// The default namespace for notes. /// public virtual string DefaultNamespace { get { return defaultNamespace.Value; } } /// /// The list of canonicalized namespaces related to notes. /// public virtual IEnumerable Namespaces { get { return NamespaceRefs.Select(UnCanonicalizeName); } } internal IEnumerable NamespaceRefs { get { return new[] { NormalizeToCanonicalName(DefaultNamespace) }.Concat( from reference in repo.Refs select reference.CanonicalName into refCanonical where refCanonical.StartsWith(Reference.NotePrefix, StringComparison.Ordinal) && refCanonical != NormalizeToCanonicalName(DefaultNamespace) select refCanonical); } } /// /// Gets the collection of associated with the specified . /// public virtual IEnumerable this[ObjectId id] { get { Ensure.ArgumentNotNull(id, "id"); return NamespaceRefs .Select(ns => this[ns, id]) .Where(n => n != null); } } /// /// Gets the collection of associated with the specified namespace. /// This is similar to the 'get notes list' command. /// public virtual IEnumerable this[string @namespace] { get { Ensure.ArgumentNotNull(@namespace, "@namespace"); string canonicalNamespace = NormalizeToCanonicalName(@namespace); return Proxy.git_note_foreach(repo.Handle, canonicalNamespace, (blobId,annotatedObjId) => this[canonicalNamespace, annotatedObjId]); } } /// /// Gets the associated with the specified objectId and the specified namespace. /// public virtual Note this[string @namespace, ObjectId id] { get { Ensure.ArgumentNotNull(id, "id"); Ensure.ArgumentNotNull(@namespace, "@namespace"); string canonicalNamespace = NormalizeToCanonicalName(@namespace); using (NoteSafeHandle noteHandle = Proxy.git_note_read(repo.Handle, canonicalNamespace, id)) { return noteHandle == null ? null : Note.BuildFromPtr(noteHandle, UnCanonicalizeName(canonicalNamespace), id); } } } private string RetrieveDefaultNamespace() { string notesRef = Proxy.git_note_default_ref(repo.Handle); return UnCanonicalizeName(notesRef); } internal static string NormalizeToCanonicalName(string name) { Ensure.ArgumentNotNullOrEmptyString(name, "name"); if (name.LooksLikeNote()) { return name; } return string.Concat(Reference.NotePrefix, name); } internal string UnCanonicalizeName(string name) { Ensure.ArgumentNotNullOrEmptyString(name, "name"); if (!name.LooksLikeNote()) { return name; } return name.Substring(Reference.NotePrefix.Length); } /// /// Creates or updates a on the specified object, and for the given namespace. /// /// The target , for which the note will be created. /// The note message. /// The author. /// The committer. /// The namespace on which the note will be created. It can be either a canonical namespace or an abbreviated namespace ('refs/notes/myNamespace' or just 'myNamespace'). /// The note which was just saved. public virtual Note Add(ObjectId targetId, string message, Signature author, Signature committer, string @namespace) { Ensure.ArgumentNotNull(targetId, "targetId"); Ensure.ArgumentNotNullOrEmptyString(message, "message"); Ensure.ArgumentNotNull(author, "author"); Ensure.ArgumentNotNull(committer, "committer"); Ensure.ArgumentNotNullOrEmptyString(@namespace, "@namespace"); string canonicalNamespace = NormalizeToCanonicalName(@namespace); Remove(targetId, author, committer, @namespace); Proxy.git_note_create(repo.Handle, author, committer, canonicalNamespace, targetId, message, true); return this[canonicalNamespace, targetId]; } /// /// Deletes the note on the specified object, and for the given namespace. /// /// The target , for which the note will be created. /// The author. /// The committer. /// The namespace on which the note will be removed. It can be either a canonical namespace or an abbreviated namespace ('refs/notes/myNamespace' or just 'myNamespace'). public virtual void Remove(ObjectId targetId, Signature author, Signature committer, string @namespace) { Ensure.ArgumentNotNull(targetId, "targetId"); Ensure.ArgumentNotNull(author, "author"); Ensure.ArgumentNotNull(committer, "committer"); Ensure.ArgumentNotNullOrEmptyString(@namespace, "@namespace"); string canonicalNamespace = NormalizeToCanonicalName(@namespace); Proxy.git_note_remove(repo.Handle, canonicalNamespace, author, committer, targetId); } private string DebuggerDisplay { get { return string.Format(CultureInfo.InvariantCulture, "Count = {0}", this.Count()); } } } }