Salient Solutions

wrasslin ones and nones for fun and profit - Sky Sanders' Blog
posts - 96, comments - 70, trackbacks - 0

using .net regex for javadocs to add xmldoc comments to XML for script 3.1.

I found myself using the cross browser xml library XML for <SCRIPT> and not liking that you get no intellisense love I whipped up a quick class to convert the javadoc comments into xmldoc format.

Luckily the main library has consistent and valid javadocs so the task was not so tedious.  Some of the other vitals scripts such as the xpath addon not so much. Will tackle those later, reluctantly. Will be an exercise in munging to be sure.

in any case here is the class that will eat xmlw3cdom.js and parse its subset of javadocs into valid xmldoc and add them inside the methods resulting in fully chained and typed intellisense support. noice.

I think the main regexs are general enough to be reused.

A snippet of the results...   the full file can be found here.

* @method DOMCharacterData.insertData - Insert a string at the specified character offset.
*
* @author Jon van Noort (jon@webarcana.com.au)
*
* @param  offset : int    - The character offset at which to insert
* @param  arg    : string - The string to insert
*
* @throws : DOMException - INDEX_SIZE_ERR: Raised if specified offset is negative or greater than the number of 16-bit units in data,
*   or if the specified count is negative.
* @throws : DOMException - NO_MODIFICATION_ALLOWED_ERR: Raised if this CharacterData is readonly.
*/
DOMCharacterData.prototype.insertData = function DOMCharacterData_insertData(offset, arg)
{
    /// <summary>DOMCharacterData.insertData - Insert a string at the specified character offset.</summary>
    /// <param name="offset" type="int">The character offset at which to insert</param>
    /// <param name="arg" type="String">The string to insert</param>
    /// <exception cref="DOMException">INDEX_SIZE_ERR: Raised if specified offset is negative or greater than the number of 16-bit units in data, or if the specified count is negative.</exception>
    /// <exception cref="DOMException">NO_MODIFICATION_ALLOWED_ERR: Raised if this CharacterData is readonly.</exception>
    /// <remarks>Jon van Noort (jon@webarcana.com.au)</remarks>


    // throw Exception if DOMCharacterData is readonly
    if (this.ownerDocument.implementation.errorChecking && this._readonly)
    {
        throw (new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR));
    }

    if (this.data)
    {

 

The class

 

// salientCS JavaScript Library v1.0
// http://skysanders.net/code/salientCS/
// 
// Copyright (c) 2009 Sky Sanders - sky@skysanders.net
// Dual licensed under the MIT and GPL licenses.
// http://skysanders.net/code/salientCS/license.txt
// 
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml.Linq;

namespace salientCS
{
    public static class jdoc2xmldoc
    {
        /// <summary>
        /// Created specifically to add xmldoc comments to xmlw3cdom.js from http://xmljs.sourceforge.net/index.html. 
        /// The file contains a subset of javadoc tags in valid format.
        /// You may be able to repurpose this code.
        /// </summary>
        /// <param name="script">script with javadocs</param>
        /// <returns>script with xmldoc comments addded</returns>
        public static string addXmlDocs(string script)
        {
            /// matches javadoc block AND the following content up until the first open brace
            /// so that i have a string to search for when adding the xmldocs.
            /// to generalize the regex just drop the method group.
            /// 
            Regex jdocAndMethodRE = new Regex(@"(?<jdoc>\n\s*/\*\*.*?\*/)(?<method>.*?\{)",
                                            RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline |
                                            RegexOptions.ExplicitCapture);

            MatchCollection jdocBlocks = jdocAndMethodRE.Matches(script);


            foreach (Match jdocBlock in jdocBlocks)
            {
                // store tags as found in discreet containers
                // so that we can arrange the doc nodes AND
                // keep params in order
                List<XElement> exceptionList = new List<XElement>();
                List<XElement> paramsList = new List<XElement>();
                XElement summaryNode = null;
                XElement returnsNode = null;
                // adding a non ms doc node for reference or future use
                XElement extendsNode = null;
                XElement remarksNode = null;


                Regex jdocTagRE = new Regex(@"\n\s*\*\s*(@(?<type>\w+)(?<content>.*?))(?=(\s*\*\s*@)|(\*/))",
                                         RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline |
                                         RegexOptions.ExplicitCapture);

                MatchCollection jdocTags = jdocTagRE.Matches(jdocBlock.Groups["jdoc"].Value);

                foreach (Match jdocTag in jdocTags)
                {
                    XElement node = null;

                    string tagType = jdocTag.Groups["type"].Value.ToLower().Trim();

                    // unformat content into single line of text. can reformat with <para> tags later
                    string tagContent = Regex.Replace(jdocTag.Groups["content"].Value, @"\s*\n*\s*\*\s*", " ",
                                                       RegexOptions.IgnoreCase | RegexOptions.Multiline |
                                                       RegexOptions.Singleline);
                    // suck slack space
                    tagContent =
                        Regex.Replace(tagContent, @"\s+", " ",
                                      RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline).Trim();


                    /// create tags
                    switch (tagType)
                    {
                        case "class":
                        case "function":
                        case "method":
                            summaryNode = new XElement("summary", tagContent);
                            break;

                        case "param":
                            Match paramMatch = Regex.Match(tagContent,
                                                           @"\s*(?<name>\w+)\s*:\s(?<type>\w+)\s*(-\s*(?<content>.*))?",
                                                           RegexOptions.IgnoreCase);

                            XElement paramNode = new XElement("param", paramMatch.Groups["content"].Value);

                            paramNode.SetAttributeValue("name", paramMatch.Groups["name"].Value);
                            paramNode.SetAttributeValue("type", fixType(paramMatch.Groups["type"].Value));

                            paramsList.Add(paramNode);
                            break;

                        case "return":
                            Match returnsMatch = Regex.Match(tagContent, @":\s(?<type>\w+)\s*(-\s*(?<content>.*))?",
                                                             RegexOptions.IgnoreCase);
                            returnsNode = new XElement("returns", returnsMatch.Groups["content"].Value);
                            returnsNode.SetAttributeValue("type", fixType(returnsMatch.Groups["type"].Value));
                            break;

                        case "extends":
                            // just for giggles
                            extendsNode = new XElement("extends", tagContent);
                            break;

                        case "throws":
                            Match exceptionMatch = Regex.Match(tagContent, @":\s(?<type>\w+)\s*(-\s*(?<content>.*))?",
                                                               RegexOptions.IgnoreCase);

                            XElement exceptionNode = new XElement("exception", exceptionMatch.Groups["content"].Value);
                            exceptionNode.SetAttributeValue("cref", exceptionMatch.Groups["type"].Value);

                            exceptionList.Add(exceptionNode);
                            break;

                        case "author":
                            remarksNode = new XElement("remarks", tagContent);
                            break;
                    }
                }

                StringBuilder xmlDocSB = new StringBuilder();

                // create the xmldoc block with tags in desired position

                if (summaryNode != null)
                {
                    xmlDocSB.AppendLine(" /// " + summaryNode);
                }

                paramsList.ForEach(x => xmlDocSB.AppendLine(" /// " + x.ToString()));

                if (returnsNode != null)
                {
                    xmlDocSB.AppendLine(" /// " + returnsNode);
                }

                exceptionList.ForEach(x => xmlDocSB.AppendLine(" /// " + x.ToString()));

                if (remarksNode != null)
                {
                    xmlDocSB.AppendLine(" /// " + remarksNode);
                }
                if (extendsNode != null)
                {
                    xmlDocSB.AppendLine(" /// " + extendsNode);
                }


                // append the xmldoc block inside the method.
                script = script.Replace(jdocBlock.Groups["method"].Value,
                                        jdocBlock.Groups["method"].Value + "\n" + xmlDocSB + "\n");
            }

            // spit it out
            return script;
        }

        /// <summary>
        /// Saw some inconsistant type names. This is just to be safe
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static string fixType(string value)
        {
            switch (value.ToLower())
            {
                case "string":
                    return "String";
                case "object":
                    return "Object";
                case "boolean":
                    return "Boolean";
                case "number":
                    return "Number";
                case "array":
                    return "Array";
                case "date":
                    return "Date";
                default:
                    return value;
            }
        }
    }
}

Print | posted on Friday, July 31, 2009 4:53 AM |

Feedback

No comments posted yet.

Post Comment

Title  
Name  
Email
Url
Comment   
Please add 3 and 1 and type the answer here:

Powered by: