diff --git a/NAV Source Control Test Host/Program.cs b/NAV Source Control Test Host/Program.cs index d89e7c6..5a895db 100644 --- a/NAV Source Control Test Host/Program.cs +++ b/NAV Source Control Test Host/Program.cs @@ -44,7 +44,7 @@ namespace NavScm.TestHost log.DebugFormat("Row {6}/{7}: Type {0}, ID {1}, Name {2}, Modified {3} {4}, Version {5}", o.Type, o.ID, o.Name, o.Date.ToShortDateString(), o.Time.ToShortTimeString(), o.Version_List, i++, count); - foundObjects.Add(String.Format("{0}.{1}", o.Type, o.ID), o); + foundObjects.Add(o.CacheKey, o); } log.InfoFormat("Collection has {0} entries, writing to cache.xml", foundObjects.Count); diff --git a/NAVSCM Library/NavInterface/NavObject.cs b/NAVSCM Library/NavInterface/NavObject.cs index 2e04e4a..f2a0493 100644 --- a/NAVSCM Library/NavInterface/NavObject.cs +++ b/NAVSCM Library/NavInterface/NavObject.cs @@ -7,8 +7,141 @@ using System.Threading.Tasks; namespace NavScm.NavInterface { - partial class NavObject + /// + /// Adds a mapping between the NAV SQL Type field an enum to make its handling easier. + /// + public enum NavObjectType { - + TableData = 0, + Table = 1, + Report = 3, + Codeunit = 5, + XmlPort = 6, + MenuSuite = 7, + Page = 8, + Query = 9 + } + + /// + /// + /// This class wraps the actual NAV Object table data retrieved from the target database. + /// It adds additional helper functions to manage the cache accociated with it. + /// + /// + /// Note, that equality is defined by the DB primary key, which in turn includes + /// Type and ID. See CacheKey + /// for details. This sequence is also used for ordering of the object in case of + /// a sorted output. + /// + /// + /// Note, that object entries with a Company Name set are rejected at this point. They + /// result (probably) from a multi-tenancy/extension setup, which we do not support at + /// this time. As well, objects with unknown object types are rejected. + /// + /// + /// + /// + partial class NavObject : IEquatable, IComparable, IComparable + { + /// + /// Validate restrictions on supported objects as outlined in the class description. + /// + partial void OnLoaded() + { + if (Company_Name.Length > 0) + throw new InvalidOperationException($"The object {CacheKey} holds a variant with the company name {Company_Name}, which is unsupported"); + if (Type < 0 || Type == 2 || Type == 4 || Type > 9) + throw new InvalidOperationException($"The object type of {CacheKey} is unsupported"); + } + + /// + /// Casts the SQL type to the corresponding Enum. + /// + public NavObjectType NavType + { + get { return (NavObjectType)Type; } + } + + /// + /// + /// Constructs an object cache key to uniquely identify the object out of its type and ID. + /// Uses the string representation to make debugging easier. The equality operator maps to + /// this key as well. + /// + /// + /// Note, that the company Name is ignored here, as we do not support this scenario at this + /// time and throw an error just in case. + /// + /// + public string CacheKey + { + get { + return $"{Type}.{ID}"; + } + } + + /// + /// Converts the Date and Time fields to a combined Date/Time value. + /// + public DateTime Modified + { + get + { + return Date.Add(Time.TimeOfDay); + } + } + + /// + /// Constructs a hash key based on Type and ID. + /// + /// Take 4 Bits of object Type and 28 bits of the actual object ID and shuffle them around + /// to create the hash key. + public override int GetHashCode() + { + return + // lower 8 bits of ID first + (ID << 24) + // second byte of ID goes next + & ((ID << 8) ^ 0x00ff0000) + // third byte of ID goes next + & ((ID >> 8) ^ 0x0000ff00) + // lower 4 bits of fourth ID byte go next + & ((ID >> 20) ^ 0x000000f0) + // finally add the first four bits of the Type + & (Type ^ 0x0000000f) + ; + } + + public override bool Equals(object obj) + { + if (!(obj is NavObject)) return false; + return Equals((NavObject) obj); + } + + public bool Equals(NavObject other) + { + return Type == other.Type + && ID == other.ID; + } + + public int CompareTo(NavObject other) + { + if (Type < other.Type) + return -1; + if (Type > other.Type) + return +1; + if (ID < other.ID) + return -1; + if (ID > other.ID) + return +1; + return 0; + } + + public int CompareTo(object obj) + { + if (!(obj is NavObject)) + throw new InvalidOperationException("obj is not an NavObject"); + return this.CompareTo((NavObject)obj); + } } }