cc2e944fc010092d4de29b75e4bf84ae9644e3ef
[mono.git] / mcs / tools / corcompare / mono-api-html / MemberComparer.cs
1 // 
2 // Authors
3 //    Sebastien Pouliot  <sebastien@xamarin.com>
4 //
5 // Copyright 2013-2014 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.Linq;
30 using System.Text;
31 using System.Xml.Linq;
32
33 namespace Xamarin.ApiDiff {
34
35         public abstract class MemberComparer : Comparer {
36
37                 // true if this is the first element being added or removed in the group being rendered
38                 protected bool first;
39
40                 public abstract string GroupName { get; }
41                 public abstract string ElementName { get; }
42
43                 public void Compare (XElement source, XElement target)
44                 {
45                         var s = source.Element (GroupName);
46                         var t = target.Element (GroupName);
47                         if (XNode.DeepEquals (s, t))
48                                 return;
49
50                         if (s == null) {
51                                 Add (t.Elements (ElementName));
52                         } else if (t == null) {
53                                 Remove (s.Elements (ElementName));
54                         } else {
55                                 Compare (s.Elements (ElementName), t.Elements (ElementName));
56                         }
57                 }
58
59                 public override void SetContext (XElement current)
60                 {
61                 }
62
63                 public XElement Source { get; set; }
64
65                 public virtual bool Find (XElement e)
66                 {
67                         return e.GetAttribute ("name") == Source.GetAttribute ("name");
68                 }
69
70                 XElement Find (IEnumerable<XElement> target)
71                 {
72                         return State.Lax ? target.FirstOrDefault (Find) : target.SingleOrDefault (Find);
73                 }
74
75                 public override void Compare (IEnumerable<XElement> source, IEnumerable<XElement> target)
76                 {
77                         removed.Clear ();
78                         obsoleted.Clear ();
79
80                         foreach (var s in source) {
81                                 SetContext (s);
82                                 Source = s;
83                                 var t = Find (target);
84                                 if (t == null) {
85                                         // not in target, it was removed
86                                         removed.Add (s);
87                                 } else {
88                                         // possibly modified
89                                         if (Equals (s, t)) {
90                                                 if (IsNowObsoleted (s, t)) {
91                                                         obsoleted.Add (t);
92                                                 }
93                                                 t.Remove ();
94                                                 continue;
95                                         }
96
97                                         // still in target so will be part of Added
98                                         removed.Add (s);
99                                         Modified (s, t);
100                                 }
101                         }
102                         // delayed, that way we show "Modified", "Added" and then "Removed"
103                         Remove (removed);
104
105                         // remaining == newly added in target
106                         Add (target);
107
108                         // obsolete (considering as added, they were not obsolete before, wrt regex)
109                         bool o = false;
110                         foreach (var item in obsoleted) {
111                                 SetContext (item);
112                                 if (State.IgnoreAdded.Any (re => re.IsMatch (GetDescription (item))))
113                                         continue;
114                                 if (!o) {
115                                         BeforeObsoleting (obsoleted);
116                                         o = true;
117                                 }
118                                 Obsoleted (item);
119                         }
120                         if (o)
121                                 AfterObsoleting ();
122                 }
123
124                 void Add (IEnumerable<XElement> elements)
125                 {
126                         bool a = false;
127                         foreach (var item in elements) {
128                                 SetContext (item);
129                                 if (State.IgnoreAdded.Any (re => re.IsMatch (GetDescription (item))))
130                                         continue;
131                                 if (!a) {
132                                         BeforeAdding (elements);
133                                         a = true;
134                                 }
135                                 Added (item);
136                         }
137                         if (a)
138                                 AfterAdding ();
139                 }
140
141                 void Remove (IEnumerable<XElement> elements)
142                 {
143                         bool r = false;
144                         foreach (var item in elements) {
145                                 if (State.IgnoreRemoved.Any (re => re.IsMatch (GetDescription (item))))
146                                         continue;
147                                 SetContext (item);
148                                 if (!r) {
149                                         BeforeRemoving (elements);
150                                         r = true;
151                                 }
152                                 Removed (item);
153                         }
154                         if (r)
155                                 AfterRemoving ();
156                 }
157
158                 public abstract string GetDescription (XElement e);
159
160                 protected StringBuilder GetObsoleteMessage (XElement e)
161                 {
162                         var sb = new StringBuilder ();
163                         string o = e.GetObsoleteMessage ();
164                         if (o != null) {
165                                 sb.Append ("[Obsolete");
166                                 if (o.Length > 0)
167                                         sb.Append (" (\"").Append (o).Append ("\")");
168                                 sb.AppendLine ("]");
169                                 for (int i = 0; i < State.Indent + 1; i++)
170                                         sb.Append ('\t');
171                         }
172                         return sb;
173                 }
174
175                 public override bool Equals (XElement source, XElement target)
176                 {
177                         if (base.Equals (source, target))
178                                 return true;
179
180                         string sourceDescription = GetDescription (source);
181                         string targetDescription = GetDescription (target);
182
183                         return (sourceDescription == targetDescription) ||
184                                 // *adding* virtual or override to target is acceptable; *removing* them is NOT
185                                 (State.IgnoreVirtualChanges &&
186                                         // non-virtual to virtual is fine
187                                         (sourceDescription == targetDescription.Replace ("virtual ", "")) ||
188                                         // non-virtual to override is fine
189                                         (sourceDescription == targetDescription.Replace ("override ", "")) ||
190                                         // virtual to override is fine
191                                         (sourceDescription.Replace (" virtual ", " override ") == targetDescription));
192                 }
193
194                 bool IsNowObsoleted (XElement source, XElement target)
195                 {
196                         var s = GetObsoleteMessage (source).ToString ();
197                         var t = GetObsoleteMessage (target).ToString ();
198                         // true if it was no [Obsolete] in the source but now is [Obsolete] in the target
199                         return (s.Length == 0 && t.Length > 0);
200                 }
201
202                 public virtual void BeforeAdding (IEnumerable<XElement> list)
203                 {
204                         first = true;
205                         Output.WriteLine ("<p>Added {0}:</p><pre>", list.Count () > 1 ? GroupName : ElementName);
206                 }
207
208                 public override void Added (XElement target)
209                 {
210                         var o = GetObsoleteMessage (target);
211                         if (!first && (o.Length > 0))
212                                 Output.WriteLine ();
213                         Indent ().WriteLine ("\t{0}{1}", o, GetDescription (target));
214                         first = false;
215                 }
216
217                 public virtual void AfterAdding ()
218                 {
219                         Output.WriteLine ("</pre>");
220                 }
221
222                 public virtual void BeforeObsoleting (IEnumerable<XElement> list)
223                 {
224                         Output.WriteLine ("<p>Obsoleted {0}:</p><pre>", list.Count () > 1 ? GroupName : ElementName);
225                 }
226
227                 public void Obsoleted (XElement target)
228                 {
229                         Indent ().WriteLine ("\t{0}{1}{2}", GetObsoleteMessage (target), GetDescription (target), Environment.NewLine);
230                 }
231
232                 public virtual void AfterObsoleting ()
233                 {
234                         Output.WriteLine ("</pre>");
235                 }
236
237                 public override void Modified (XElement source, XElement target)
238                 {
239                 }
240
241                 public virtual void BeforeRemoving (IEnumerable<XElement> list)
242                 {
243                         first = true;
244                         Output.WriteLine ("<p>Removed {0}:</p><pre>", list.Count () > 1 ? GroupName : ElementName);
245                 }
246
247                 public override void Removed (XElement source)
248                 {
249                         var o = GetObsoleteMessage (source);
250                         if (!first && (o.Length > 0))
251                                 Output.WriteLine ();
252                         Indent ().WriteLine ("\t{0}{1}", o, GetDescription (source));
253                         first = false;
254                 }
255
256                 public virtual void AfterRemoving ()
257                 {
258                         Output.WriteLine ("</pre>");
259                 }
260         }
261 }