Xamarin Public Jenkins (auto-signing) 0abdbe5a7d Imported Upstream version 5.18.0.142
Former-commit-id: 7467d4b717762eeaf652d77f1486dd11ffb1ff1f
2018-10-09 08:20:59 +00:00

259 lines
8.1 KiB
C#

//
// Authors
// Sebastien Pouliot <sebastien@xamarin.com>
//
// 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 Mono.ApiTools {
class PropertyComparer : MemberComparer {
public PropertyComparer (State state)
: base (state)
{
}
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) System.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", State);
var tgtType = target.GetTypeName ("ptype", State);
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<XElement> srcIndexers, List<XElement> 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", State);
var tgtType = target.GetTypeName ("type", State);
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<XElement> srcIndexers = null;
List<XElement> 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 (GetDescription (source), State);
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", State);
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 ();
}
}
}