// // Authors // Sebastien Pouliot // // Copyright 2013 Xamarin Inc. http://www.xamarin.com // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // using System; using System.Collections.Generic; using System.Reflection; using System.Text; using System.Xml.Linq; namespace Xamarin.ApiDiff { public class PropertyComparer : MemberComparer { public override string GroupName { get { return "properties"; } } public override string ElementName { get { return "property"; } } public override bool Find (XElement e) { if (!base.Find (e)) return false; // the same Item (indexer) property can have different parameters return e.GetAttribute ("params") == Source.GetAttribute ("params"); } void GetAccessors (XElement element, out XElement getter, out XElement setter) { var methods = element.Element ("methods"); getter = null; setter = null; if (methods == null) return; foreach (var m in methods.Elements ("method")) { var n = m.GetAttribute ("name"); if (n.StartsWith ("get_", StringComparison.Ordinal)) { getter = m; } else if (n.StartsWith ("set_", StringComparison.Ordinal)) { setter = m; } } } MethodAttributes GetMethodAttributes (XElement getter, XElement setter) { if (getter == null) return setter.GetMethodAttributes (); else if (setter == null) return getter.GetMethodAttributes (); var gAttr = getter.GetMethodAttributes (); var sAttr = setter.GetMethodAttributes (); var g = gAttr & MethodAttributes.MemberAccessMask; var s = sAttr & MethodAttributes.MemberAccessMask; // Visibility is ordered numerically (higher value = more visible). // We want the most visible. var visibility = (MethodAttributes) Math.Max ((int) g, (int) s); // Do a bitwise or with the rest of the flags var g_no_visibility = gAttr & ~MethodAttributes.MemberAccessMask; var s_no_visibility = sAttr & ~MethodAttributes.MemberAccessMask; return g_no_visibility | s_no_visibility | visibility; } void RenderPropertyType (XElement source, XElement target, ApiChange change) { var srcType = source.GetTypeName ("ptype"); var tgtType = target.GetTypeName ("ptype"); if (srcType == tgtType) { change.Append (tgtType); } else { change.AppendModified (srcType, tgtType, true); } change.Append (" "); } void RenderAccessors (XElement srcGetter, XElement tgtGetter, XElement srcSetter, XElement tgtSetter, ApiChange change) { // FIXME: this doesn't render changes in the accessor visibility (a protected setter can become public for instance). change.Append (" {"); if (tgtGetter != null) { if (srcGetter != null) { change.Append (" ").Append ("get;"); } else { change.Append (" ").AppendAdded ("get;"); } } else if (srcGetter != null) { change.Append (" ").AppendRemoved ("get;"); } if (tgtSetter != null) { if (srcSetter != null) { change.Append (" ").Append ("set;"); } else { change.Append (" ").AppendAdded ("set;"); } } else if (srcSetter != null) { change.Append (" ").AppendRemoved ("set;"); } change.Append (" }"); // Ignore added property setters if asked to if (srcSetter == null && tgtSetter != null && State.IgnoreAddedPropertySetters && !change.Breaking) { change.AnyChange = false; change.HasIgnoredChanges = true; } } void RenderIndexers (List srcIndexers, List tgtIndexers, ApiChange change) { change.Append ("this ["); for (int i = 0; i < srcIndexers.Count; i++) { var source = srcIndexers [i]; var target = tgtIndexers [i]; if (i > 0) change.Append (", "); var srcType = source.GetTypeName ("type"); var tgtType = target.GetTypeName ("type"); if (srcType == tgtType) { change.Append (tgtType); } else { change.AppendModified (srcType, tgtType, true); } change.Append (" "); var srcName = source.GetAttribute ("name"); var tgtName = target.GetAttribute ("name"); if (srcName == tgtName) { change.Append (tgtName); } else { change.AppendModified (srcName, tgtName, true); } } change.Append ("]"); } public override bool Equals (XElement source, XElement target, ApiChanges changes) { if (base.Equals (source, target, changes)) return true; XElement srcGetter, srcSetter; XElement tgtGetter, tgtSetter; GetAccessors (source, out srcGetter, out srcSetter); GetAccessors (target, out tgtGetter, out tgtSetter); List srcIndexers = null; List tgtIndexers = null; bool isIndexer = false; if (srcGetter != null) { srcIndexers = srcGetter.DescendantList ("parameters", "parameter"); tgtIndexers = tgtGetter.DescendantList ("parameters", "parameter"); isIndexer = srcIndexers != null && srcIndexers.Count > 0; } var change = new ApiChange (); change.Header = "Modified " + GroupName; RenderMethodAttributes (GetMethodAttributes (srcGetter, srcSetter), GetMethodAttributes (tgtGetter, tgtSetter), change); RenderPropertyType (source, target, change); if (isIndexer) { RenderIndexers (srcIndexers, tgtIndexers, change); } else { RenderName (source, target, change); } RenderGenericParameters (source, target, change); RenderAccessors (srcGetter, tgtGetter, srcSetter, tgtSetter, change); changes.Add (source, target, change); return false; } void GetProperties (XElement e, out bool @virtual, out bool @override, out bool @static, out bool getter, out bool setter, out bool family) { @virtual = @override = @static = getter = setter = family = false; var methods = e.Element ("methods"); if (methods != null) { foreach (var m in methods.Elements ("method")) { @virtual |= m.IsTrue ("virtual"); @static |= m.IsTrue ("static"); var n = m.GetAttribute ("name"); getter |= n.StartsWith ("get_", StringComparison.Ordinal); setter |= n.StartsWith ("set_", StringComparison.Ordinal); var attribs = (MethodAttributes) Int32.Parse (m.GetAttribute ("attrib")); family = ((attribs & MethodAttributes.Public) != MethodAttributes.Public); @override |= (attribs & MethodAttributes.NewSlot) == 0; } } } public override string GetDescription (XElement e) { string name = e.Attribute ("name").Value; string ptype = e.GetTypeName ("ptype"); bool virt = false; bool over = false; bool stat = false; bool getter = false; bool setter = false; bool family = false; GetProperties (e, out virt, out over, out stat, out getter, out setter, out family); var sb = new StringBuilder (); sb.Append (family ? "protected " : "public "); if (virt && !State.IgnoreVirtualChanges) sb.Append (over ? "override " : "virtual "); else if (stat) sb.Append ("static "); sb.Append (ptype).Append (' ').Append (name).Append (" { "); if (getter) sb.Append ("get; "); if (setter) sb.Append ("set; "); sb.Append ("}"); return sb.ToString (); } } }