File: SourceFormat.cs

Details

File: SourceFormat.cs
Date: Thu, May 26, 2011
Created by: Cynic

Source code for the CoDocker CSharp to HTML converter from OfItselfSo.com

// CoDocker Implementor Notes
//     This file contains the open source code supplied by Jean-Claude Manoli
//     on his website page: http://www.manoli.net/csharpformat/ 
//
//     The copyright remains with Jean-Claude - all future implementors
//     please respect the license agreement below. As is permitted by the license,
//     this file is a modified version of the original source code. Here are the 
//     updates made to the original source file
//
//     02-Jan-2011 Cynic  Changed the namespace from Manoli.Utils.CSharpFormat
//                        to CoDocker 
//     03-Jan-2011 Cynic  The name of the CSharp.css file has been changed to 
//                        CoDocker.css

#region Copyright � 2001-2003 Jean-Claude Manoli [jc@manoli.net]
/*
 * This software is provided 'as-is', without any express or implied warranty.
 * In no event will the author(s) be held liable for any damages arising from
 * the use of this software.
 * 
 * Permission is granted to anyone to use this software for any purpose,
 * including commercial applications, and to alter it and redistribute it
 * freely, subject to the following restrictions:
 * 
 *   1. The origin of this software must not be misrepresented; you must not
 *      claim that you wrote the original software. If you use this software
 *      in a product, an acknowledgment in the product documentation would be
 *      appreciated but is not required.
 * 
 *   2. Altered source versions must be plainly marked as such, and must not
 *      be misrepresented as being the original software.
 * 
 *   3. This notice may not be removed or altered from any source distribution.
 */ 
#endregion

using System;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Reflection;

namespace CoDocker
{
    /// <summary>
    ///    Provides a base implementation for all code formatters.
    /// </summary>
    /// <remarks>
    /// <para>
    /// To display the formatted code on your web site, the web page must 
    /// refer to a stylesheet that defines the formatting for the different 
    /// CSS classes generated by CSharpFormat:
    /// .csharpcode, pre, .rem, .kwrd, .str, .op, .preproc, .alt, .lnum.
    /// </para>
    /// <para>
    /// Note that if you have multi-line comments in your source code
    /// (like /* ... */), the "line numbers" or "alternate line background" 
    /// options will generate code that is not strictly HTML 4.01 compliant. 
    /// The code will still look good with IE5+ or Mozilla 0.8+. 
    /// </para>
    /// </remarks>
    public abstract class SourceFormat
    {
        /// <summary/>
        protected SourceFormat()
        {
            _tabSpaces = 4;
            _lineNumbers = false;
            _alternate = false;
            _embedStyleSheet = false;
        }

        private byte _tabSpaces;

        /// <summary>
        /// Gets or sets the tabs width.
        /// </summary>
        /// <value>The number of space characters to substitute for tab 
        /// characters. The default is <b>4</b>, unless overridden is a 
        /// derived class.</value>
        public byte TabSpaces
        {
            get { return _tabSpaces; }
            set { _tabSpaces = value; }
        }

        private bool _lineNumbers;

        /// <summary>
        /// Enables or disables line numbers in output.
        /// </summary>
        /// <value>When <b>true</b>, line numbers are generated. 
        /// The default is <b>false</b>.</value>
        public bool LineNumbers
        {
            get { return _lineNumbers; }
            set { _lineNumbers = value; }
        }

        private bool _alternate;

        /// <summary>
        /// Enables or disables alternating line background.
        /// </summary>
        /// <value>When <b>true</b>, lines background is alternated. 
        /// The default is <b>false</b>.</value>
        public bool Alternate
        {
            get { return _alternate; }
            set { _alternate = value; }
        }

        private bool _embedStyleSheet;

        /// <summary>
        /// Enables or disables the embedded CSS style sheet.
        /// </summary>
        /// <value>When <b>true</b>, the CSS &lt;style&gt; element is included 
        /// in the HTML output. The default is <b>false</b>.</value>
        public bool EmbedStyleSheet
        {
            get { return _embedStyleSheet; }
            set { _embedStyleSheet = value; }
        }

        /// <overloads>Transform source code to HTML 4.01.</overloads>
        /// 
        /// <summary>
        /// Transforms a source code stream to HTML 4.01.
        /// </summary>
        /// <param name="source">Source code stream.</param>
        /// <returns>A string containing the HTML formatted code.</returns>
        public string FormatCode(Stream source)
        {
            StreamReader reader = new StreamReader(source);
            string s = reader.ReadToEnd();
            reader.Close();
            return FormatCode(s, _lineNumbers, _alternate, _embedStyleSheet, false);
        }

        /// <summary>
        /// Transforms a source code string to HTML 4.01.
        /// </summary>
        /// <returns>A string containing the HTML formatted code.</returns>
        public string FormatCode(string source)
        {
            return FormatCode(source, _lineNumbers, _alternate, _embedStyleSheet, false);
        }

        /// <summary>
        /// Allows formatting a part of the code in a different language,
        /// for example a JavaScript block inside an HTML file.
        /// </summary>
        public string FormatSubCode(string source)
        {
            return FormatCode(source, false, false, false, true);
        }

        /// <summary>
        /// Gets the CSS stylesheet as a stream.
        /// </summary>
        /// <returns>A text <see cref="Stream"/> of the CSS definitions.</returns>
        public static Stream GetCssStream()
        {
            return Assembly.GetExecutingAssembly().GetManifestResourceStream(
                "CoDocker.CoDocker.css");
        }

        /// <summary>
        /// Gets the CSS stylesheet as a string.
        /// </summary>
        /// <returns>A string containing the CSS definitions.</returns>
        public static string GetCssString()
        {
            StreamReader reader = new StreamReader(GetCssStream());
            return reader.ReadToEnd();
        }

        private Regex codeRegex;

        /// <summary>
        /// The regular expression used to capture language tokens.
        /// </summary>
        protected Regex CodeRegex
        {
            get { return codeRegex; }
            set { codeRegex = value; }
        }

        /// <summary>
        /// Called to evaluate the HTML fragment corresponding to each 
        /// matching token in the code.
        /// </summary>
        /// <param name="match">The <see cref="Match"/> resulting from a 
        /// single regular expression match.</param>
        /// <returns>A string containing the HTML code fragment.</returns>
        protected abstract string MatchEval(Match match);

        //does the formatting job
        private string FormatCode(string source, bool lineNumbers, 
            bool alternate, bool embedStyleSheet, bool subCode)
        {
            //replace special characters
            StringBuilder sb = new StringBuilder(source);

            if(!subCode)
            {
                sb.Replace("&", "&amp;");
                sb.Replace("<", "&lt;");
                sb.Replace(">", "&gt;");
                sb.Replace("\t", string.Empty.PadRight(_tabSpaces));
            }
            
            //color the code
            source = codeRegex.Replace(sb.ToString(), new MatchEvaluator(this.MatchEval));

            sb = new StringBuilder();
            
            if (embedStyleSheet)
            {
                sb.Append("<style type=\"text/css\">\n");
                sb.Append(GetCssString());
                sb.Append("</style>\n");
            }

            if (lineNumbers || alternate) //we have to process the code line by line
            {
                if(!subCode)
                    sb.Append("<div class=\"csharpcode\">\n");
                StringReader reader = new StringReader(source);
                int i = 0;
                string spaces = "    ";
                int order;
                string line;
                while ((line = reader.ReadLine()) != null)
                {
                    i++;
                    if (alternate && ((i % 2) == 1))
                    {
                        sb.Append("<pre class=\"alt\">");
                    }
                    else
                    {
                        sb.Append("<pre>");
                    }

                    if(lineNumbers)
                    {
                        order = (int)Math.Log10(i);
                        sb.Append("<span class=\"lnum\">" 
                            + spaces.Substring(0, 3 - order) + i.ToString() 
                            + ":  </span>");
                    }
                    
                    if(line.Length == 0)
                        sb.Append("&nbsp;");
                    else
                        sb.Append(line);
                    sb.Append("</pre>\n");
                }
                reader.Close();
                if(!subCode)
                    sb.Append("</div>");
            }
            else
            {
                //have to use a <pre> because IE below ver 6 does not understand 
                //the "white-space: pre" CSS value
                if(!subCode)
                    sb.Append("<pre class=\"csharpcode\">\n");
                sb.Append(source);
                if(!subCode)
                    sb.Append("</pre>");
            }
            
            return sb.ToString();
        }

    }
}
HTML Code Generated by CoDocker v00.90 Beta