/// <summary> /// Generate a javascript "class" /// </summary> private static void GenerateTypeDocumentation(TextWriter writer, Type type, XmlDocument xmlDoc, ConsoleParameters parms, List <Type> enumerationTypes, List <Type> alreadyGenerated) { if (alreadyGenerated.Contains(type)) { return; } else { alreadyGenerated.Add(type); } writer.WriteLine("// {0}", type.AssemblyQualifiedName); writer.WriteLine("/**"); writer.WriteLine(" * @class"); writer.WriteLine(" * @memberof {0}", parms.Namespace); writer.WriteLine(" * @public"); if (type.IsAbstract) { writer.WriteLine(" * @abstract"); } var jobject = type.GetCustomAttribute <JsonObjectAttribute>(); if (type.BaseType != typeof(Object)) { writer.WriteLine(" * @extends {0}.{1}", parms.Namespace, type.BaseType.GetCustomAttribute <JsonObjectAttribute>().Id); } // Lookup the summary information var typeDoc = xmlDoc.SelectSingleNode(String.Format("//*[local-name() = 'member'][@name = 'T:{0}']", type.FullName)); if (typeDoc != null) { if (typeDoc.SelectSingleNode(".//*[local-name() = 'summary']") != null) { writer.WriteLine(" * @summary {0}", typeDoc.SelectSingleNode(".//*[local-name() = 'summary']").InnerText.Replace("\r\n", "")); } if (typeDoc.SelectSingleNode(".//*[local-name() = 'remarks']") != null) { writer.WriteLine(" * @description {0}", typeDoc.SelectSingleNode(".//*[local-name() = 'remarks']").InnerText.Replace("\r\n", "\r\n * ").Replace("()", "")); } if (typeDoc.SelectSingleNode(".//*[local-name() = 'example']") != null) { writer.WriteLine(" * @example {0}", typeDoc.SelectSingleNode(".//*[local-name() = 'example']").InnerText.Replace("\r\n", "")); } } List <String> copyCommands = new List <string>(); // Get all properties and document them foreach (var itm in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)) { if (itm.GetCustomAttribute <JsonPropertyAttribute>() == null && itm.GetCustomAttribute <SerializationReferenceAttribute>() == null) { continue; } Type itmType = itm.PropertyType; if (itmType.IsGenericType) { itmType = itmType.GetGenericArguments()[0]; } var itmJobject = itmType.GetCustomAttribute <JsonObjectAttribute>(); if (itmJobject == null) { if (itmType.StripNullable().IsEnum) { itmJobject = new JsonObjectAttribute(String.Format("{0}.{1}", parms.Namespace, itmType.Name)); } else if (!primitives.TryGetValue(itmType, out itmJobject)) { itmJobject = new JsonObjectAttribute(itmType.Name); } } else { itmJobject = new JsonObjectAttribute(String.Format("{0}.{1}", parms.Namespace, itmJobject.Id)); } var simpleAtt = itmType.GetCustomAttribute <SimpleValueAttribute>(); if (simpleAtt != null) { var simpleProperty = itmType.GetProperty(simpleAtt.ValueProperty); if (!primitives.TryGetValue(simpleProperty.PropertyType, out itmJobject)) { itmJobject = new JsonObjectAttribute(simpleProperty.PropertyType.Name); } } var originalType = itmJobject.Id; // Is this a classified object? if so then the classifier values act as properties themselves var classAttr = itmType.GetCustomAttribute <ClassifierAttribute>(); if (classAttr != null && itm.PropertyType.IsGenericType) { itmJobject = new JsonObjectAttribute("object"); } writer.Write(" * @property {{{0}}} ", itmJobject.Id); var jprop = itm.GetCustomAttribute <JsonPropertyAttribute>(); var redir = itm.GetCustomAttribute <SerializationReferenceAttribute>(); if (jprop != null) { writer.Write(jprop.PropertyName); copyCommands.Add(jprop.PropertyName); } else if (redir != null) { var backingProperty = type.GetProperty(redir.RedirectProperty); jprop = backingProperty.GetCustomAttribute <JsonPropertyAttribute>(); writer.Write("{0}Model [Delay loaded from {0}], ", jprop.PropertyName); copyCommands.Add(jprop.PropertyName + "Model"); } else { writer.Write(itm.Name + "Model"); copyCommands.Add(itm.Name + "Model"); } // Output documentation typeDoc = xmlDoc.SelectSingleNode(String.Format("//*[local-name() = 'member'][@name = 'P:{0}.{1}']", itm.DeclaringType.FullName, itm.Name)); if (typeDoc != null) { if (typeDoc.SelectSingleNode(".//*[local-name() = 'summary']") != null) { writer.Write(typeDoc.SelectSingleNode(".//*[local-name() = 'summary']").InnerText.Replace("\r\n", "")); } } var bindAttr = itm.GetCustomAttribute <BindingAttribute>(); if (itmType.StripNullable().IsEnum) { bindAttr = new BindingAttribute(itmType.StripNullable()); } if (bindAttr != null) { enumerationTypes.Add(bindAttr.Binding); writer.Write("(see: {{@link {0}.{1}}} for values)", parms.Namespace, bindAttr.Binding.Name); } writer.WriteLine(); // Classified object? If so we need to clarify how the object is propogated if (classAttr != null && itm.PropertyType.IsGenericType) { // Does the classifier have a binding var classProperty = itmType.GetProperty(classAttr.ClassifierProperty); if (classProperty.GetCustomAttribute <SerializationReferenceAttribute>() != null) { classProperty = itmType.GetProperty(classProperty.GetCustomAttribute <SerializationReferenceAttribute>().RedirectProperty); } bindAttr = classProperty.GetCustomAttribute <BindingAttribute>(); if (bindAttr != null) { enumerationTypes.Add(bindAttr.Binding); // Binding attribute found so lets enumerate it foreach (var fi in bindAttr.Binding.GetFields(BindingFlags.Public | BindingFlags.Static)) { writer.Write(" * @property {{{0}}} {1}.{2} ", originalType, jprop.PropertyName, fi.Name, classProperty.GetCustomAttribute <JsonPropertyAttribute>()?.PropertyName); typeDoc = xmlDoc.SelectSingleNode(String.Format("//*[local-name() = 'member'][@name = 'F:{0}.{1}']", fi.DeclaringType.FullName, fi.Name)); if (typeDoc != null) { if (typeDoc.SelectSingleNode(".//*[local-name() = 'summary']") != null) { writer.Write(typeDoc.SelectSingleNode(".//*[local-name() = 'summary']").InnerText.Replace("\r\n", "")); } } writer.WriteLine(); } writer.WriteLine(" * @property {{{0}}} {1}.$other Unclassified", originalType, jprop.PropertyName); } else { writer.Write(" * @property {{{0}}} {1}.{2} ", originalType, jprop.PropertyName, "classifier"); writer.Write(" where classifier is from {{@link {0}.{1}}} {2}", parms.Namespace, classProperty.DeclaringType.GetCustomAttribute <JsonObjectAttribute>().Id, classProperty.GetCustomAttribute <JsonPropertyAttribute>()?.PropertyName); writer.WriteLine(); } } } writer.WriteLine(" * @param {{{0}.{1}}} copyData Copy constructor (if present)", parms.Namespace, jobject.Id); writer.WriteLine(" */"); writer.WriteLine("{0} : function(copyData) {{ ", jobject.Id); writer.WriteLine("\tthis.$type = '{0}';", jobject.Id); writer.WriteLine("\tif(copyData) {"); copyCommands.Reverse(); // Get all properties and document them foreach (var itm in copyCommands.Where(o => o != "$type")) { writer.WriteLine("\tthis.{0} = copyData.{0};", itm); } writer.WriteLine("\t}"); writer.WriteLine("}}, // {0} ", jobject.Id); }
/// <summary> /// Generate enumeration documentation /// </summary> private static void GenerateEnumerationDocumentation(TextWriter writer, Type type, XmlDocument xmlDoc, ConsoleParameters parms) { writer.WriteLine("// {0}", type.AssemblyQualifiedName); writer.WriteLine("/**"); writer.WriteLine(" * @enum {uuid}"); writer.WriteLine(" * @memberof {0}", parms.Namespace); writer.WriteLine(" * @public"); writer.WriteLine(" * @readonly"); var jobject = type.GetCustomAttribute <JsonObjectAttribute>(); if (jobject == null) { jobject = new JsonObjectAttribute(type.Name); } // Lookup the summary information var typeDoc = xmlDoc.SelectSingleNode(String.Format("//*[local-name() = 'member'][@name = 'T:{0}']", type.FullName)); if (typeDoc != null) { if (typeDoc.SelectSingleNode(".//*[local-name() = 'summary']") != null) { writer.WriteLine(" * @summary {0}", typeDoc.SelectSingleNode(".//*[local-name() = 'summary']").InnerText.Replace("\r\n", "")); } if (typeDoc.SelectSingleNode(".//*[local-name() = 'remarks']") != null) { writer.WriteLine(" * @description {0}", typeDoc.SelectSingleNode(".//*[local-name() = 'remarks']").InnerText.Replace("\r\n", "")); } if (typeDoc.SelectSingleNode(".//*[local-name() = 'example']") != null) { writer.WriteLine(" * @example {0}", typeDoc.SelectSingleNode(".//*[local-name() = 'example']").InnerText.Replace("\r\n", "")); } } writer.WriteLine(" */"); writer.WriteLine("{0} : {{ ", jobject.Id); // Enumerate fields foreach (var fi in type.GetFields(BindingFlags.Public | BindingFlags.Static)) { writer.WriteLine("\t/** "); writer.Write("\t * "); typeDoc = xmlDoc.SelectSingleNode(String.Format("//*[local-name() = 'member'][@name = 'F:{0}.{1}']", fi.DeclaringType.FullName, fi.Name)); if (typeDoc != null) { if (typeDoc.SelectSingleNode(".//*[local-name() = 'summary']") != null) { writer.Write(typeDoc.SelectSingleNode(".//*[local-name() = 'summary']").InnerText.Replace("\r\n", "")); } } writer.WriteLine(); writer.WriteLine("\t */"); writer.WriteLine("\t{0} : '{1}',", fi.Name, fi.GetValue(null)); } writer.WriteLine("}}, // {0} ", jobject.Id); }