119
static XElement GetXmlNodeForMemberName (Type t, XDocument xdoc, string name)
121
var field = xdoc.XPathSelectElement ("Type/Members/Member[@MemberName='" + name + "']");
123
if (!warnings_up_to_date.ContainsKey (t)){
124
Console.WriteLine ("Warning: {0} document is not up-to-date with the latest assembly (could not find Field <Member MemberName='{1}')", t, name);
125
warnings_up_to_date [t] = true;
131
static XElement GetXmlNodeFromExport (Type t, XDocument xdoc, string selector)
133
return (from m in xdoc.XPathSelectElements ("Type/Members/Member")
134
let a = m.XPathSelectElement ("Attributes/Attribute/AttributeName")
135
where a != null && a.Value.IndexOf ("MonoTouch.Foundation.Export(\"" + selector + "\"") != -1
136
select m).FirstOrDefault ();
110
140
// Handles fields, but perhaps this is better done in DocFixer to pull the definitions
111
141
// from the docs?
113
public static void ProcessField (Type t, XDocument xdoc, PropertyInfo pi)
143
public static void ProcessField (Type t, XDocument xdoc, PropertyInfo pi, bool isNotification)
115
145
var fieldAttr = pi.GetCustomAttributes (typeof (FieldAttribute), true);
116
146
if (fieldAttr.Length == 0)
119
149
var export = ((FieldAttribute) fieldAttr [0]).SymbolName;
151
var field = GetXmlNodeForMemberName (t, xdoc, pi.Name);
121
var field = xdoc.XPathSelectElement ("Type/Members/Member[@MemberName='" + pi.Name + "']");
123
if (!warnings_up_to_date.ContainsKey (t)){
124
Console.WriteLine ("Warning: {0} document is not up-to-date with the latest assembly (could not find Field <Member MemberName='{1}')", t, pi.Name);
125
warnings_up_to_date [t] = true;
129
155
var returnType = field.XPathSelectElement ("ReturnValue/ReturnType");
130
156
var summary = field.XPathSelectElement ("Docs/summary");
131
157
var remarks = field.XPathSelectElement ("Docs/remarks");
132
158
var example = field.XPathSelectElement ("Docs/remarks/example");
134
if (returnType.Value == "MonoMac.Foundation.NSString" && export.EndsWith ("Notification")){
159
if (isNotification || (returnType.Value.EndsWith (".Foundation.NSString") && export.EndsWith ("Notification"))){
135
161
var mdoc = docGenerator.GetAppleMemberDocs (ToCecilType (t), export);
136
162
if (mdoc == null){
137
163
Console.WriteLine ("Failed to load docs for {0} - {1}", t.Name, export);
201
if (remarks.Value == "To be added.")
204
remarks.AddFirst (XElement.Parse (String.Format ("<para id='tool-remark'>If you want to subscribe to this notification, you can use the convenience <see cref='T:{0}+Notifications'/>.<see cref='M:{0}+Notifications.Observe{1}'/> method which offers strongly typed access to the parameters of the notification.</para>", t.Name, mname)));
240
var evengArgsType = DocumentNotificationNestedType (t, pi, body.ToString ());
242
remarks.RemoveAll ();
243
remarks.Add (XElement.Parse ("<para id='tool-remark'>This constant can be used with the <see cref=\"T:MonoTouch.Foundation.NSNotificationCenter\"/> to register a listener for this notification. This is an NSString instead of a string, because these values can be used as tokens in some native libraries instead of being used purely for their actual string content. The 'notification' parameter to the callback contains extra information that is specific to the notification type.</para>"));
244
remarks.Add (XElement.Parse (String.Format ("<para id='tool-remark'>If you want to subscribe to this notification, you can use the convenience <see cref='T:{0}+Notifications'/>.<see cref='M:{0}+Notifications.Observe{1}'/> method which offers strongly typed access to the parameters of the notification.</para>", t.Name, name)));
205
245
remarks.Add (XElement.Parse ("<para>The following example shows how to use the strongly typed Notifications class, to take the guesswork out of the available properties in the notification:</para>"));
206
246
remarks.Add (XElement.Parse (String.Format ("<example><code lang=\"c#\">\n" +
207
247
"//\n// Lambda style\n//\n\n// listening\n" +
209
249
"// To stop listening:\n" +
210
250
"notification.Dispose ();\n\n" +
211
251
"//\n// Method style\n//\nNSObject notification;\n" +
212
"void Callback (object sender, {1} args)\n"+
252
"void Callback (object sender, {3} args)\n"+
213
253
"{{\n // Access strongly typed args\n{2}\n}}\n\n" +
214
254
"void Setup ()\n{{\n" +
215
255
" notification = {0}.Notifications.Observe{1} (Callback);\n}}\n\n" +
216
256
"void Teardown ()\n{{\n" +
217
" notification.Dispose ();\n}}</code></example>", t.Name, mname, body)));
257
" notification.Dispose ();\n}}</code></example>", t.Name, mname, body, evengArgsType)));
258
remarks.Add (XElement.Parse ("<para>The following example shows how to use the notification with the DefaultCenter API:</para>"));
259
remarks.Add (XElement.Parse (String.Format ("<example><code lang=\"c#\">\n" +
260
"// Lambda style\n" +
261
"NSNotificationCenter.DefaultCenter.AddObserver (\n {0}.{1}, (notification) => {{Console.WriteLine (\"Received the notification {0}\", notification); }}\n\n\n" +
262
"// Method style\n" +
263
"void Callback (NSNotification notification)\n{{\n" +
264
" Console.WriteLine (\"Received a notification {0}\", notification);\n}}\n\n" +
265
"void Setup ()\n{{\n" +
266
" NSNotificationCenter.DefaultCenter.AddObserver ({0}.{1}, Callback);\n}}\n</code></example>", t.Name, name)));
219
269
// Keep track of the uses, so we can list all of the observers.
220
270
if (notification_event_args != null){
226
276
list.Add (notification_event_args);
227
277
event_args_to_notification_uses [notification_event_args] = list;
229
DocumentNotificationNestedType (t, pi, body.ToString ());
232
public static void DocumentNotificationNestedType (Type t, PropertyInfo pi, string body)
281
public static string DocumentNotificationNestedType (Type t, PropertyInfo pi, string body)
234
var class_doc = GetDoc (t, true);
283
string handlerType = null;
284
var class_doc = GetDoc (t, notification: true);
236
286
if (class_doc == null){
237
287
Console.WriteLine ("Error, can not find Notification class for type {0}", t);
288
Environment.Exit (1);
241
290
var class_summary = class_doc.XPathSelectElement ("Type/Docs/summary");
242
291
var class_remarks = class_doc.XPathSelectElement ("Type/Docs/remarks");
244
class_summary.Value = "Notification posted by the <see cref =\"T:" + t.FullName + "\"/> class.";
245
class_remarks.Value = "";
293
class_summary.RemoveAll ();
294
class_summary.Add (XElement.Parse ("<para>Notification posted by the <see cref =\"T:" + t.FullName + "\"/> class.</para>"));
295
class_remarks.RemoveAll ();
246
296
class_remarks.Add (XElement.Parse ("<para>This is a static class which contains various helper methods that allow developers to observe events posted " +
247
297
"in the iOS notification hub (<see cref=\"T:MonoTouch.Foundation.NSNotificationCenter\"/>).</para>"));
248
298
class_remarks.Add (XElement.Parse ("<para>The methods defined in this class post events invoke the provided method or lambda with a " +
257
307
let convertedName = propName.EndsWith ("Notification") ? propName.Substring (0, propLen-("Notification".Length)) : propName
258
308
select new Tuple<string,string> (convertedName, ((FieldAttribute) fieldAttrs [0]).SymbolName) ;
310
// So the code below actually only executes once.
311
if (notifications.Count() > 1){
312
Console.WriteLine ("WHOA! DocumentNotificationNestedType got more than 1 notification");
260
315
foreach (var notification in notifications){
261
316
var mname = "Observe" + notification.Item1;
262
317
var method = class_doc.XPathSelectElement ("Type/Members/Member[@MemberName='" + mname + "']");
264
319
var handler = method.XPathSelectElement ("Docs/param");
321
handlerType = (string) method.XPathSelectElement ("Parameters/Parameter").Attribute ("Type");
322
if (handlerType.StartsWith ("System.EventHandler<"))
323
handlerType = handlerType.Substring (20, handlerType.Length-21);
325
// Turn System.EventHandler<Foo> into EventHandler<Foo>, looks prettier
326
if (handlerType.StartsWith ("System."))
327
handlerType = handlerType.Substring (7);
265
328
var summary = method.XPathSelectElement ("Docs/summary");
266
329
var remarks = method.XPathSelectElement ("Docs/remarks");
267
330
var returns = method.XPathSelectElement ("Docs/returns");
269
332
Console.WriteLine ("Looking for {0}, and this is the class\n{1}", notification.Item1, class_doc);
270
333
handler.Value = "Method to invoke when the notification is posted.";
271
334
summary.Value = "Registers a method to be notified when the " + notification.Item2 + " notification is posted.";
272
returns.Value = "The returned NSObject represents the registered notification. Either call Dispose on the object to stop receiving notifications, or pass it to <see cref=\"M:MonoTouch.Foundation.NSNotification.RemoveObserver\"/>";
335
returns.RemoveAll ();
336
returns.Add (XElement.Parse ("<para>The returned NSObject represents the registered notification. Either call Dispose on the object to stop receiving notifications, or pass it to <see cref=\"M:MonoTouch.Foundation.NSNotificationCenter.RemoveObserver\"/></para>"));
337
remarks.RemoveAll ();
274
338
remarks.Add (XElement.Parse ("<para>The following example shows how you can use this method in your code</para>"));
276
340
remarks.Add (XElement.Parse (String.Format ("<example><code lang=\"c#\">\n" +
277
341
"//\n// Lambda style\n//\n\n// listening\n" +
278
"notification = {0}.Notifications.Observe{1} ((sender, args) => {{\n /* Access strongly typed args */\n{2}\n}});\n\n" +
342
"notification = {0}.Notifications.{1} ((sender, args) => {{\n /* Access strongly typed args */\n{2}\n}});\n\n" +
279
343
"// To stop listening:\n" +
280
344
"notification.Dispose ();\n\n" +
281
345
"//\n//Method style\n//\nNSObject notification;\n" +
282
"void Callback (object sender, {1} args)\n"+
346
"void Callback (object sender, {3} args)\n"+
283
347
"{{\n // Access strongly typed args\n{2}\n}}\n\n" +
284
348
"void Setup ()\n{{\n" +
285
" notification = {0}.Notifications.Observe{1} (Callback);\n}}\n\n" +
349
" notification = {0}.Notifications.{1} (Callback);\n}}\n\n" +
286
350
"void Teardown ()\n{{\n" +
287
" notification.Dispose ();\n}}</code></example>", t.Name, mname, body)));
351
" notification.Dispose ();\n}}</code></example>", t.Name, mname, body, handlerType)));
290
354
Save (GetMdocPath (t, true), class_doc);
294
358
public static void PopulateEvents (XDocument xmldoc, BaseTypeAttribute bta, Type t)
385
// This prepares a node that contains text, like this:
386
// <foo>To be added.</foo>
388
// to wrap the text in a para.
389
// <foo><para>To be added.</para></foo>
391
static void PrepareNakedNode (XElement element, bool addStub = true)
393
if (element.HasElements)
395
var val = element.Value;
396
if (addStub && val == "To be added.")
397
val = "(More documentation for this node is coming)";
400
element.Add (XElement.Parse ("<para>" + val + "</para>"));
404
public static void AnnotateNullAllowedPropertyValue (Type t, XDocument xdoc, PropertyInfo pi)
406
var field = GetXmlNodeForMemberName (t, xdoc, pi.Name);
409
var value = field.XPathSelectElement ("Docs/value");
410
var toolgen_null_annotations = value.XPathSelectElement ("para[@tool='nullallowed']");
411
if (toolgen_null_annotations == null)
412
PrepareNakedNode (value);
414
toolgen_null_annotations.Remove ();
416
value.Add (XElement.Parse ("<para tool='nullallowed'>This value can be <see langword=\"null\"/>.</para>"));
320
419
public static void ProcessNSO (Type t, BaseTypeAttribute bta)
322
421
var xmldoc = GetDoc (t);
423
Console.WriteLine ("Can not find docs for {0}", t);
326
427
foreach (var pi in t.GatherProperties ()){
430
if (pi.GetCustomAttributes (typeof (InternalAttribute), true).Length > 0)
329
433
if (pi.GetCustomAttributes (typeof (FieldAttribute), true).Length > 0){
330
ProcessField (t, xmldoc, pi);
434
bool is_notification = pi.GetCustomAttributes (typeof (NotificationAttribute), true).Length != 0;
435
ProcessField (t, xmldoc, pi, is_notification);
332
if ((attrs = pi.GetCustomAttributes (typeof (NotificationAttribute), true)).Length > 0)
333
438
ProcessNotification (t, xmldoc, pi);
441
if (pi.GetCustomAttributes (typeof (NullAllowedAttribute), true).Length > 0){
442
AnnotateNullAllowedPropertyValue (t, xmldoc, pi);
445
// Propagate the [NullAllowed] from the WeakXXX to XXX
447
if (pi.Name.StartsWith ("Weak")){
448
var npi = t.GetProperty (pi.Name.Substring (4));
450
// Validate that the other property is actually a wrapper around this one.
451
if (npi != null && npi.GetCustomAttributes (typeof (WrapAttribute), true).Length > 0){
453
AnnotateNullAllowedPropertyValue (t, xmldoc, npi);
455
Console.WriteLine ("Did not find matching {0}", pi.Name);
459
if (pi.GetCustomAttributes (typeof (ThreadSafeAttribute), true).Length > 0){
460
var field = GetXmlNodeForMemberName (t, xmldoc, pi.Name);
463
var remarks = field.XPathSelectElement ("Docs/remarks");
465
AddThreadRemark (remarks, "This");
468
foreach (var mi in t.GatherMethods ()){
470
// Since it is a pain to go from a MethodInfo into an ECMA XML signature name
471
// we will lookup the method by the [Export] attribute
474
if (mi.GetCustomAttributes (typeof (InternalAttribute), true).Length > 0)
477
var attrs = mi.GetCustomAttributes (typeof (ExportAttribute), true);
478
if (attrs.Length == 0)
480
var ea = attrs [0] as ExportAttribute;
481
var node = GetXmlNodeFromExport (t, xmldoc, ea.Selector);
483
Console.WriteLine ("Did not find the selector {0} on type {1}", ea.Selector, t);
487
if (mi.GetCustomAttributes (typeof (ThreadSafeAttribute), true).Length > 0){
488
var remarks = node.XPathSelectElement ("Docs/remarks");
489
AddThreadRemark (remarks, "This");
491
foreach (var parameter in mi.GetParameters ()){
492
if (parameter.GetCustomAttributes (typeof (NullAllowedAttribute), true).Length > 0){
493
var par = node.XPathSelectElement ("Docs/param[@name='" + parameter.Name + "']");
495
Console.WriteLine ("Did not find parameter {0} in {1}.{2}\n{3}", parameter.Name, t, mi, node);
498
var toolgen_null_annotations = par.XPathSelectElement ("para[@tool='nullallowed']");
499
if (toolgen_null_annotations == null)
500
PrepareNakedNode (par, addStub: false);
502
toolgen_null_annotations.Remove ();
503
par.Add (XElement.Parse ("<para tool='nullallowed'>This parameter can be <see langword=\"null\"/>.</para>"));
339
508
if (bta != null && bta.Events != null){
340
509
PopulateEvents (xmldoc, bta, t);
513
static void AddThreadRemark (XElement node, string msg)
515
var threadNode = node.XPathSelectElement ("para[@tool='threads']");
516
if (threadNode == null)
517
PrepareNakedNode (node);
519
threadNode.Remove ();
520
node.Add (XElement.Parse ("<para tool='threads'>" + msg + " can be used from a background thread.</para>"));
523
public static void AnnotateThreadSafeType (Type t)
525
var xmldoc = GetDoc (t);
527
Console.WriteLine ("Can not find docs for {0}", t);
530
var typeRemarks = xmldoc.XPathSelectElement ("Type/Docs/remarks");
531
AddThreadRemark (typeRemarks, "The members of this class");
533
var memberRemarks = xmldoc.XPathSelectElements ("Type/Members/Member/Docs/remarks");
534
foreach (var mr in memberRemarks)
535
AddThreadRemark (mr, "This");
344
538
public static int Main (string [] args)
346
540
string dir = null;
381
575
assembly_dir = Path.Combine (dir, "en");
382
576
assembly = Assembly.LoadFrom (lib);
384
docGenerator = new AppleDocMerger (new AppleDocMerger.Options {
385
DocBase = Path.Combine (docBase, "Contents/Resources/Documents/documentation"),
386
DebugDocs = debugDoc,
387
MonodocArchive = new MDocDirectoryArchive (assembly_dir),
388
Assembly = Mono.Cecil.AssemblyDefinition.ReadAssembly (lib),
389
BaseAssemblyNamespace = ns,
390
ImportSamples = false
579
docGenerator = new AppleDocMerger (new AppleDocMerger.Options {
580
DocBase = Path.Combine (docBase, "Contents/Resources/Documents/documentation"),
581
DebugDocs = debugDoc,
582
MonodocArchive = new MDocDirectoryArchive (assembly_dir),
583
Assembly = Mono.Cecil.AssemblyDefinition.ReadAssembly (lib),
584
BaseAssemblyNamespace = ns,
585
ImportSamples = false
393
589
foreach (Type t in assembly.GetTypes ()){
590
if (t.GetCustomAttributes (typeof (ThreadSafeAttribute), true).Length > 0)
591
AnnotateThreadSafeType (t);
593
if (debugDoc && mergeAppledocs){
395
594
string str = docGenerator.GetAppleDocFor (ToCecilType (t));
396
595
if (str == null){
397
596
Console.WriteLine ("Could not find docs for {0}", t);