3 // Sebastien Pouliot <sebastien@xamarin.com>
5 // Copyright 2013-2014 Xamarin Inc. http://www.xamarin.com
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:
15 // The above copyright notice and this permission notice shall be
16 // included in all copies or substantial portions of the Software.
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.
28 using System.Collections.Generic;
31 using System.Xml.Linq;
33 namespace Xamarin.ApiDiff {
35 public abstract class MemberComparer : Comparer {
37 // true if this is the first element being added or removed in the group being rendered
40 public abstract string GroupName { get; }
41 public abstract string ElementName { get; }
43 public void Compare (XElement source, XElement target)
45 var s = source.Element (GroupName);
46 var t = target.Element (GroupName);
47 if (XNode.DeepEquals (s, t))
51 Add (t.Elements (ElementName));
52 } else if (t == null) {
53 Remove (s.Elements (ElementName));
55 Compare (s.Elements (ElementName), t.Elements (ElementName));
59 public override void SetContext (XElement current)
63 public XElement Source { get; set; }
65 public virtual bool Find (XElement e)
67 return e.GetAttribute ("name") == Source.GetAttribute ("name");
70 XElement Find (IEnumerable<XElement> target)
72 return State.Lax ? target.FirstOrDefault (Find) : target.SingleOrDefault (Find);
75 public override void Compare (IEnumerable<XElement> source, IEnumerable<XElement> target)
80 foreach (var s in source) {
83 var t = Find (target);
85 // not in target, it was removed
90 if (IsNowObsoleted (s, t)) {
97 // still in target so will be part of Added
102 // delayed, that way we show "Modified", "Added" and then "Removed"
105 // remaining == newly added in target
108 // obsolete (considering as added, they were not obsolete before, wrt regex)
110 foreach (var item in obsoleted) {
112 if (State.IgnoreAdded.Any (re => re.IsMatch (GetDescription (item))))
115 BeforeObsoleting (obsoleted);
124 void Add (IEnumerable<XElement> elements)
127 foreach (var item in elements) {
129 if (State.IgnoreAdded.Any (re => re.IsMatch (GetDescription (item))))
132 BeforeAdding (elements);
141 void Remove (IEnumerable<XElement> elements)
144 foreach (var item in elements) {
145 if (State.IgnoreRemoved.Any (re => re.IsMatch (GetDescription (item))))
149 BeforeRemoving (elements);
158 public abstract string GetDescription (XElement e);
160 protected StringBuilder GetObsoleteMessage (XElement e)
162 var sb = new StringBuilder ();
163 string o = e.GetObsoleteMessage ();
165 sb.Append ("[Obsolete");
167 sb.Append (" (\"").Append (o).Append ("\")");
169 for (int i = 0; i < State.Indent + 1; i++)
175 public override bool Equals (XElement source, XElement target)
177 if (base.Equals (source, target))
180 string sourceDescription = GetDescription (source);
181 string targetDescription = GetDescription (target);
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));
194 bool IsNowObsoleted (XElement source, XElement target)
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);
202 public virtual void BeforeAdding (IEnumerable<XElement> list)
205 Output.WriteLine ("<p>Added {0}:</p><pre>", list.Count () > 1 ? GroupName : ElementName);
208 public override void Added (XElement target)
210 var o = GetObsoleteMessage (target);
211 if (!first && (o.Length > 0))
213 Indent ().WriteLine ("\t{0}{1}", o, GetDescription (target));
217 public virtual void AfterAdding ()
219 Output.WriteLine ("</pre>");
222 public virtual void BeforeObsoleting (IEnumerable<XElement> list)
224 Output.WriteLine ("<p>Obsoleted {0}:</p><pre>", list.Count () > 1 ? GroupName : ElementName);
227 public void Obsoleted (XElement target)
229 Indent ().WriteLine ("\t{0}{1}{2}", GetObsoleteMessage (target), GetDescription (target), Environment.NewLine);
232 public virtual void AfterObsoleting ()
234 Output.WriteLine ("</pre>");
237 public override void Modified (XElement source, XElement target)
241 public virtual void BeforeRemoving (IEnumerable<XElement> list)
244 Output.WriteLine ("<p>Removed {0}:</p><pre>", list.Count () > 1 ? GroupName : ElementName);
247 public override void Removed (XElement source)
249 var o = GetObsoleteMessage (source);
250 if (!first && (o.Length > 0))
252 Indent ().WriteLine ("\t{0}{1}", o, GetDescription (source));
256 public virtual void AfterRemoving ()
258 Output.WriteLine ("</pre>");