public string CompareDatabases(ReflectionDatabase oldApi, ReflectionDatabase newApi) { results.Clear(); // Diff Classes Dictionary <string, ClassDescriptor> oldClasses = createLookupTable(oldApi.Classes); Dictionary <string, ClassDescriptor> newClasses = createLookupTable(newApi.Classes); foreach (string className in newClasses.Keys) { if (!oldClasses.ContainsKey(className)) { // Add this class. ClassDescriptor classDesc = newClasses[className]; flagEntireClass(classDesc, Added, true); } } foreach (string className in oldClasses.Keys) { ClassDescriptor oldClass = oldClasses[className]; string classLbl = oldClass.ToString(); if (newClasses.ContainsKey(className)) { ClassDescriptor newClass = newClasses[className]; var classTagDiffs = DiffTags(classLbl, oldClass.Tags, newClass.Tags); DiffGeneric(classLbl, "superclass", oldClass.Superclass, newClass.Superclass); DiffNativeEnum(classLbl, "memory category", oldClass.MemoryCategory, newClass.MemoryCategory); // Diff the members Dictionary <string, MemberDescriptor> oldMembers = createLookupTable(oldClass.Members); Dictionary <string, MemberDescriptor> newMembers = createLookupTable(newClass.Members); foreach (string memberName in newMembers.Keys) { if (!oldMembers.ContainsKey(memberName)) { // Add New Member MemberDescriptor newMember = newMembers[memberName]; string memberType = Util.GetEnumName(newMember.MemberType); Added(memberType, newMember.Signature); } } foreach (string memberName in oldMembers.Keys) { MemberDescriptor oldMember = oldMembers[memberName]; string memberType = Util.GetEnumName(oldMember.MemberType); if (newMembers.ContainsKey(memberName)) { MemberDescriptor newMember = newMembers[memberName]; string memberLbl = newMember.Summary; // Diff Tags var memberTagDiffs = DiffTags(memberLbl, oldMember.Tags, newMember.Tags); // Check if any tags that were added to this member were // also added to its parent class. foreach (string classTag in classTagDiffs.Keys) { if (memberTagDiffs.ContainsKey(classTag)) { Diff classDiff = classTagDiffs[classTag]; Diff memberDiff = memberTagDiffs[classTag]; classDiff.AddChild(memberDiff); } } // Diff Specific Member Types if (newMember is PropertyDescriptor) { PropertyDescriptor oldProp = oldMember as PropertyDescriptor; PropertyDescriptor newProp = newMember as PropertyDescriptor; // If the read and write permissions are both changed to the same value, try to group them. if (oldProp.Security.ToString() != newProp.Security.ToString()) { if (oldProp.Security.ShouldMergeWith(newProp.Security)) { // Doesn't matter if we read from 'Read' or 'Write' in this case. SecurityType oldSecurity = oldProp.Security.Read; SecurityType newSecurity = newProp.Security.Read; DiffNativeEnum(memberLbl, "security", oldSecurity, newSecurity); } else { DiffNativeEnum(memberLbl, "read permissions", oldProp.Security.Read, newProp.Security.Read); DiffNativeEnum(memberLbl, "write permissions", oldProp.Security.Write, newProp.Security.Write); } } DiffGeneric(memberLbl, "serialization", oldProp.Serialization.ToString(), newProp.Serialization.ToString()); DiffGeneric(memberLbl, "value type", oldProp.ValueType.Name, newProp.ValueType.Name); } else if (newMember is FunctionDescriptor) { FunctionDescriptor oldFunc = oldMember as FunctionDescriptor; FunctionDescriptor newFunc = newMember as FunctionDescriptor; DiffNativeEnum(memberLbl, "security", oldFunc.Security, newFunc.Security); DiffGeneric(memberLbl, "return type", oldFunc.ReturnType.Name, newFunc.ReturnType.Name); DiffParameters(memberLbl, oldFunc.Parameters, newFunc.Parameters); } else if (newMember is CallbackDescriptor) { CallbackDescriptor oldCall = oldMember as CallbackDescriptor; CallbackDescriptor newCall = newMember as CallbackDescriptor; DiffNativeEnum(memberLbl, "security", oldCall.Security, newCall.Security); DiffGeneric(memberLbl, "expected return type", oldCall.ReturnType.Name, newCall.ReturnType.Name); DiffParameters(memberLbl, oldCall.Parameters, newCall.Parameters); } else if (newMember is EventDescriptor) { EventDescriptor oldEvent = oldMember as EventDescriptor; EventDescriptor newEvent = newMember as EventDescriptor; DiffNativeEnum(memberLbl, "permissions", oldEvent.Security, newEvent.Security); DiffParameters(memberLbl, oldEvent.Parameters, newEvent.Parameters); } } else { // Remove Old Member Removed(memberType, oldMember.Summary); } } } else { // Remove Old Class flagEntireClass(oldClass, Removed, false); } } // Diff Enums Dictionary <string, EnumDescriptor> oldEnums = createLookupTable(oldApi.Enums); Dictionary <string, EnumDescriptor> newEnums = createLookupTable(newApi.Enums); foreach (string enumName in newEnums.Keys) { if (!oldEnums.ContainsKey(enumName)) { // Add New Enum EnumDescriptor newEnum = newEnums[enumName]; flagEntireEnum(newEnum, Added, true); } } foreach (string enumName in oldEnums.Keys) { EnumDescriptor oldEnum = oldEnums[enumName]; string enumLbl = oldEnum.Summary; if (newEnums.ContainsKey(enumName)) { EnumDescriptor newEnum = newEnums[enumName]; // Diff Tags DiffTags(enumLbl, oldEnum.Tags, newEnum.Tags); // Diff Items Dictionary <string, EnumItemDescriptor> oldItems = createLookupTable(oldEnum.Items); Dictionary <string, EnumItemDescriptor> newItems = createLookupTable(newEnum.Items); foreach (var itemName in newItems.Keys) { if (!oldItems.ContainsKey(itemName)) { // Add New EnumItem EnumItemDescriptor item = newItems[itemName]; Added("EnumItem", item.Signature); } } foreach (var itemName in oldItems.Keys) { EnumItemDescriptor oldItem = oldItems[itemName]; string itemLbl = oldItem.Summary; if (newItems.ContainsKey(itemName)) { EnumItemDescriptor newItem = newItems[itemName]; DiffTags(itemLbl, oldItem.Tags, newItem.Tags); DiffGeneric(itemLbl, "value", oldItem.Value, newItem.Value); } else { // Remove Old EnumItem Removed("EnumItem", itemLbl); } } } else { // Remove Old Enum flagEntireEnum(oldEnum, Removed, false); } } // Finalize Diff results.Sort(); List <string> diffs = results .Where(diff => !diff.HasParent) .Select(diff => diff.ToString()) .ToList(); List <string> final = new List <string>(); string prevLead = ""; string lastLine = ""; foreach (string line in diffs) { string[] words = line.Split(' '); if (words.Length >= 2) { // Capture the first two words in this line. string first = words[0]; string second = words[1]; if (second.ToLower() == "the" && words.Length > 3) { second = words[2]; } string lead = (first + ' ' + second).Trim(); bool addBreak = false; bool lastLineNoBreak = !lastLine.EndsWith(Util.NewLine); // If the first two words of this line aren't the same as the last... if (lead != prevLead) { // Add a break if the last line doesn't have a break. // (and if there actually were two previous words) if (prevLead != "" && lastLineNoBreak) { addBreak = true; } prevLead = lead; } // If we didn't add a break, but this line has a break and the // previous line doesn't, then we will add a break. if (!addBreak && lastLineNoBreak && line.EndsWith(Util.NewLine)) { addBreak = true; } if (addBreak) { // Add a break between this line and the previous. // This will make things easier to read. final.Add(""); } final.Add(line); lastLine = line; } } return(string.Join(Util.NewLine, final.ToArray()).Trim()); }