f51dd231bb9297696a629b6590cd87974c1aeeb6
[mono.git] / mcs / tools / corcompare / mono-api-html / PropertyComparer.cs
1 // 
2 // Authors
3 //    Sebastien Pouliot  <sebastien@xamarin.com>
4 //
5 // Copyright 2013 Xamarin Inc. http://www.xamarin.com
6 // 
7 // Permission is hereby granted, free of charge, to any person obtaining
8 // a copy of this software and associated documentation files (the
9 // "Software"), to deal in the Software without restriction, including
10 // without limitation the rights to use, copy, modify, merge, publish,
11 // distribute, sublicense, and/or sell copies of the Software, and to
12 // permit persons to whom the Software is furnished to do so, subject to
13 // the following conditions:
14 //
15 // The above copyright notice and this permission notice shall be
16 // included in all copies or substantial portions of the Software.
17 //
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 //
26
27 using System;
28 using System.Collections.Generic;
29 using System.Reflection;
30 using System.Text;
31 using System.Xml.Linq;
32
33 namespace Xamarin.ApiDiff {
34
35         public class PropertyComparer : MemberComparer {
36
37                 public override string GroupName {
38                         get { return "properties"; }
39                 }
40
41                 public override string ElementName {
42                         get { return "property"; }
43                 }
44
45                 public override bool Find (XElement e)
46                 {
47                         if (!base.Find (e))
48                                 return false;
49                         // the same Item (indexer) property can have different parameters
50                         return e.GetAttribute ("params") == Source.GetAttribute ("params");
51                 }
52
53                 void GetAccessors (XElement element, out XElement getter, out XElement setter)
54                 {
55                         var methods = element.Element ("methods");
56
57                         getter = null;
58                         setter = null;
59
60                         if (methods == null)
61                                 return;
62                                 
63                         foreach (var m in methods.Elements ("method")) {
64                                 var n = m.GetAttribute ("name");
65                                 if (n.StartsWith ("get_", StringComparison.Ordinal)) {
66                                         getter = m;
67                                 } else if (n.StartsWith ("set_", StringComparison.Ordinal)) {
68                                         setter = m;
69                                 }
70                         }
71                 }
72
73                 MethodAttributes GetMethodAttributes (XElement getter, XElement setter)
74                 {
75                         if (getter == null)
76                                 return setter.GetMethodAttributes ();
77                         else if (setter == null)
78                                 return getter.GetMethodAttributes ();
79
80                         var gAttr = getter.GetMethodAttributes ();
81                         var sAttr = setter.GetMethodAttributes ();
82                         var g = gAttr & MethodAttributes.MemberAccessMask;
83                         var s = sAttr & MethodAttributes.MemberAccessMask;
84                         // Visibility is ordered numerically (higher value = more visible).
85                         // We want the most visible.
86                         var visibility = (MethodAttributes) Math.Max ((int) g, (int) s);
87                         // Do a bitwise or with the rest of the flags
88                         var g_no_visibility = gAttr & ~MethodAttributes.MemberAccessMask;
89                         var s_no_visibility = sAttr & ~MethodAttributes.MemberAccessMask;
90                         return g_no_visibility | s_no_visibility | visibility;
91                 }
92
93                 void RenderPropertyType (XElement source, XElement target, ApiChange change)
94                 {
95                         var srcType = source.GetTypeName ("ptype");
96                         var tgtType = target.GetTypeName ("ptype");
97
98                         if (srcType == tgtType) {
99                                 change.Append (tgtType);
100                         } else {
101                                 change.AppendModified (srcType, tgtType, true);
102                         }
103                         change.Append (" ");
104                 }
105
106                 void RenderAccessors (XElement srcGetter, XElement tgtGetter, XElement srcSetter, XElement tgtSetter, ApiChange change)
107                 {
108                         // FIXME: this doesn't render changes in the accessor visibility (a protected setter can become public for instance).
109                         change.Append (" {");
110                         if (tgtGetter != null) {
111                                 if (srcGetter != null) {
112                                         change.Append (" ").Append ("get;");
113                                 } else {
114                                         change.Append (" ").AppendAdded ("get;");
115                                 }
116                         } else if (srcGetter != null) {
117                                 change.Append (" ").AppendRemoved ("get;");
118                         }
119
120                         if (tgtSetter != null) {
121                                 if (srcSetter != null) {
122                                         change.Append (" ").Append ("set;");
123                                 } else {
124                                         change.Append (" ").AppendAdded ("set;");
125                                 }
126                         } else if (srcSetter != null) {
127                                 change.Append (" ").AppendRemoved ("set;");
128                         }
129
130                         change.Append (" }");
131
132                         // Ignore added property setters if asked to
133                         if (srcSetter == null && tgtSetter != null && State.IgnoreAddedPropertySetters && !change.Breaking) {
134                                 change.AnyChange = false;
135                                 change.HasIgnoredChanges = true;
136                         }
137                 }
138
139                 void RenderIndexers (List<XElement> srcIndexers, List<XElement> tgtIndexers, ApiChange change)
140                 {
141                         change.Append ("this [");
142                         for (int i = 0; i < srcIndexers.Count; i++) {
143                                 var source = srcIndexers [i];
144                                 var target = tgtIndexers [i];
145
146                                 if (i > 0)
147                                         change.Append (", ");
148
149                                 var srcType = source.GetTypeName ("type");
150                                 var tgtType = target.GetTypeName ("type");
151                                 if (srcType == tgtType) {
152                                         change.Append (tgtType);
153                                 } else {
154                                         change.AppendModified (srcType, tgtType, true);
155                                 }
156                                 change.Append (" ");
157
158                                 var srcName = source.GetAttribute ("name");
159                                 var tgtName = target.GetAttribute ("name");
160                                 if (srcName == tgtName) {
161                                         change.Append (tgtName);
162                                 } else {
163                                         change.AppendModified (srcName, tgtName, true);
164                                 }
165                         }
166                         change.Append ("]");
167                 }
168
169                 public override bool Equals (XElement source, XElement target, ApiChanges changes)
170                 {
171                         if (base.Equals (source, target, changes))
172                                 return true;
173
174                         XElement srcGetter, srcSetter;
175                         XElement tgtGetter, tgtSetter;
176                         GetAccessors (source, out srcGetter, out srcSetter);
177                         GetAccessors (target, out tgtGetter, out tgtSetter);
178
179                         List<XElement> srcIndexers = null;
180                         List<XElement> tgtIndexers = null;
181                         bool isIndexer = false;
182                         if (srcGetter != null) {
183                                 srcIndexers = srcGetter.DescendantList ("parameters", "parameter");
184                                 tgtIndexers = tgtGetter.DescendantList ("parameters", "parameter");
185                                 isIndexer = srcIndexers != null && srcIndexers.Count > 0;
186                         }
187
188                         var change = new ApiChange ();
189                         change.Header = "Modified " + GroupName;
190                         RenderMethodAttributes (GetMethodAttributes (srcGetter, srcSetter), GetMethodAttributes (tgtGetter, tgtSetter), change);
191                         RenderPropertyType (source, target, change);
192                         if (isIndexer) {
193                                 RenderIndexers (srcIndexers, tgtIndexers, change);
194                         } else {
195                                 RenderName (source, target, change);
196                         }
197                         RenderGenericParameters (source, target, change);
198                         RenderAccessors (srcGetter, tgtGetter, srcSetter, tgtSetter, change);
199
200                         changes.Add (source, target, change);
201
202                         return false;
203                 }
204
205                 void GetProperties (XElement e, out bool @virtual, out bool @override, out bool @static, out bool getter, out bool setter, out bool family)
206                 {
207                         @virtual = @override = @static = getter = setter = family = false;
208
209                         var methods = e.Element ("methods");
210                         if (methods != null) {
211                                 foreach (var m in methods.Elements ("method")) {
212                                         @virtual |= m.IsTrue ("virtual");
213                                         @static |= m.IsTrue ("static");
214                                         var n = m.GetAttribute ("name");
215                                         getter |= n.StartsWith ("get_", StringComparison.Ordinal);
216                                         setter |= n.StartsWith ("set_", StringComparison.Ordinal);
217                                         var attribs = (MethodAttributes) Int32.Parse (m.GetAttribute ("attrib"));
218                                         family = ((attribs & MethodAttributes.Public) != MethodAttributes.Public);
219                                         @override |= (attribs & MethodAttributes.NewSlot) == 0;
220                                 }
221                         }
222                 }
223
224                 public override string GetDescription (XElement e)
225                 {
226                         string name = e.Attribute ("name").Value;
227                         string ptype = e.GetTypeName ("ptype");
228
229                         bool virt = false;
230                         bool over = false;
231                         bool stat = false;
232                         bool getter = false;
233                         bool setter = false;
234                         bool family = false;
235                         GetProperties (e, out virt, out over, out stat, out getter, out setter, out family);
236
237                         var sb = new StringBuilder ();
238
239                         sb.Append (family ? "protected " : "public ");
240                         if (virt && !State.IgnoreVirtualChanges)
241                                 sb.Append (over ? "override " : "virtual ");
242                         else if (stat)
243                                 sb.Append ("static ");
244                         sb.Append (ptype).Append (' ').Append (name).Append (" { ");
245                         if (getter)
246                                 sb.Append ("get; ");
247                         if (setter)
248                                 sb.Append ("set; ");
249                         sb.Append ("}");
250
251                         return sb.ToString ();
252                 }
253         }
254 }