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());
        }