ExampleComponent.cs :  » Development » Sandcastle » Microsoft » Ddue » Tools » C# / CSharp Open Source

Home
C# / CSharp Open Source
1.2.6.4 mono .net core
2.2.6.4 mono core
3.Aspect Oriented Frameworks
4.Bloggers
5.Build Systems
6.Business Application
7.Charting Reporting Tools
8.Chat Servers
9.Code Coverage Tools
10.Content Management Systems CMS
11.CRM ERP
12.Database
13.Development
14.Email
15.Forum
16.Game
17.GIS
18.GUI
19.IDEs
20.Installers Generators
21.Inversion of Control Dependency Injection
22.Issue Tracking
23.Logging Tools
24.Message
25.Mobile
26.Network Clients
27.Network Servers
28.Office
29.PDF
30.Persistence Frameworks
31.Portals
32.Profilers
33.Project Management
34.RSS RDF
35.Rule Engines
36.Script
37.Search Engines
38.Sound Audio
39.Source Control
40.SQL Clients
41.Template Engines
42.Testing
43.UML
44.Web Frameworks
45.Web Service
46.Web Testing
47.Wiki Engines
48.Windows Presentation Foundation
49.Workflows
50.XML Parsers
C# / C Sharp
C# / C Sharp by API
C# / CSharp Tutorial
C# / CSharp Open Source » Development » Sandcastle 
Sandcastle » Microsoft » Ddue » Tools » ExampleComponent.cs
// Copyright  Microsoft Corporation.
// This source file is subject to the Microsoft Permissive License.
// See http://www.microsoft.com/resources/sharedsource/licensingbasics/sharedsourcelicenses.mspx.
// All other rights reserved.

using System;
using System.Configuration;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.XPath;

namespace Microsoft.Ddue.Tools{

  // a component to replace code references with snippets from a file

  public class ExampleComponent : BuildComponent {

    // instantiation logic

    public ExampleComponent(BuildAssembler assembler, XPathNavigator configuration) : base(assembler, configuration) {

            XPathNodeIterator contentNodes = configuration.Select("examples");
      foreach (XPathNavigator contentNode in contentNodes) {
        string file = contentNode.GetAttribute("file", String.Empty);
                file = Environment.ExpandEnvironmentVariables(file);
                if (String.IsNullOrEmpty(file)) WriteMessage(MessageLevel.Error, String.Format("Each examples element must contain a file attribute."));
        LoadContent(file);
      }

      WriteMessage(MessageLevel.Info, String.Format("Loaded {0} code snippets", snippets.Count));

      XPathNodeIterator colorsNodes = configuration.Select("colors");
      foreach (XPathNavigator colorsNode in colorsNodes) {
        string language = colorsNode.GetAttribute("language", String.Empty);
        List<ColorizationRule> rules = new List<ColorizationRule>();

        XPathNodeIterator colorNodes = colorsNode.Select("color");
        foreach (XPathNavigator colorNode in colorNodes) {
          string pattern = colorNode.GetAttribute("pattern", String.Empty);
          string region = colorNode.GetAttribute("region", String.Empty);
          string name = colorNode.GetAttribute("class", String.Empty);
          if (String.IsNullOrEmpty(region)) {
            rules.Add( new ColorizationRule(pattern, name) );            
          } else {
            rules.Add( new ColorizationRule(pattern, region, name) );
          }
        }

        colorization[language] = rules;
        WriteMessage(MessageLevel.Info, String.Format("Loaded {0} colorization rules for the language '{1}'.", rules.Count, language));
      }

      context.AddNamespace("ddue", "http://ddue.schemas.microsoft.com/authoring/2003/5");

      selector = XPathExpression.Compile("//ddue:codeReference");
      selector.SetContext(context);
    }

    // snippet loading logic

    private void LoadContent(string file) {

      SnippetIdentifier key = new SnippetIdentifier();
      string language;

      WriteMessage(MessageLevel.Info, String.Format("Loading code snippet file '{0}'.", file));
            try {
                XmlReaderSettings settings = new XmlReaderSettings();
                settings.CheckCharacters = false;
                XmlReader reader = XmlReader.Create(file, settings);

                try {
                    reader.MoveToContent();
                    while (!reader.EOF) {
                        if ((reader.NodeType == XmlNodeType.Element) && (reader.Name == "item")) {
                            key = new SnippetIdentifier(reader.GetAttribute("id"));
                            reader.Read();
                        } else if ((reader.NodeType == XmlNodeType.Element) && (reader.Name == "sampleCode")) {
                            language = reader.GetAttribute("language");

                            string content = reader.ReadString();

                            // If the element is empty, ReadString does not advance the reader, so we must do it manually
                            if (String.IsNullOrEmpty(content)) reader.Read();

                            if (!IsLegalXmlText(content)) {
                                Console.WriteLine("Snippet '{0}' language '{1}' contains illegal characters.", key, language);
                                throw new InvalidOperationException();
                            }

                            content = StripLeadingSpaces(content);

                            StoredSnippet snippet = new StoredSnippet(content, language);
                            List<StoredSnippet> values;
                            if (!snippets.TryGetValue(key, out values)) {
                                values = new List<StoredSnippet>();
                                snippets.Add(key, values);
                            }
                            values.Add(snippet);
                        } else {
                            reader.Read();
                        }
                    }
                } catch (XmlException e) {
                    WriteMessage(MessageLevel.Warn, String.Format("The contents of the snippet file '{0}' are not well-formed XML. The error message is: {1}. Some snippets may be lost.", file, e.Message));
                } finally {
            reader.Close();
          }

            } catch (IOException e) {
                WriteMessage(MessageLevel.Error, String.Format("An access error occured while attempting to read the snippet file '{0}'. The error message is: {1}", file, e.Message));
            }

    }

        // logic for checking XML

        private bool IsLegalXmlCharacter (char c) {
            if (c < 0x20) {
                return ( (c == 0x09) || (c == 0x0A) || (c == 0x0D) );
            } else {
                if (c < 0xD800) {
                    return(true);
                } else {
                    return( (c >= 0xE000) && (c <= 0xFFFD) );
                }
            }
        }

        private bool IsLegalXmlText (string text) {
            foreach (char c in text) {
                if (!IsLegalXmlCharacter(c)) return (false);
            }
            return (true);
        }

    // the snippet store

    private Dictionary<SnippetIdentifier,List<StoredSnippet>> snippets = new Dictionary<SnippetIdentifier,List<StoredSnippet>>();

        // the actual work of the component

    public override void Apply(XmlDocument document, string key) {
            XPathNodeIterator nodesIterator = document.CreateNavigator().Select(selector);
            XPathNavigator[] nodes = BuildComponentUtilities.ConvertNodeIteratorToArray(nodesIterator);
            foreach (XPathNavigator node in nodes) {

        string reference = node.Value;

        // check for validity of reference
        if (validSnippetReference.IsMatch(reference)) {
          

        SnippetIdentifier[] identifiers = SnippetIdentifier.ParseReference(reference);

        if (identifiers.Length == 1) {
          // one snippet referenced

          SnippetIdentifier identifier = identifiers[0];
          List<StoredSnippet> values;
          if (snippets.TryGetValue(identifier, out values)) {

            XmlWriter writer = node.InsertAfter();
            writer.WriteStartElement("snippets");
            writer.WriteAttributeString("reference", reference);

            foreach (StoredSnippet value in values) {
              writer.WriteStartElement("snippet");
              writer.WriteAttributeString("language", value.Language);

              if (colorization.ContainsKey(value.Language)) {
                WriteColorizedSnippet(ColorizeSnippet(value.Text, colorization[value.Language]), writer);

              } else {
                                writer.WriteString(value.Text);
                                //writer.WriteString(System.Web.HttpUtility.HtmlDecode(value.Text));
              }
              writer.WriteEndElement();
            }

            writer.WriteEndElement();
            writer.Close();

          } else {
            WriteMessage(MessageLevel.Warn, String.Format("No snippet with identifier '{0}' was found.", identifier));
          }
        } else {
          // multiple snippets referenced

          // create structure that maps language -> snippets
          Dictionary<string,List<StoredSnippet>> map = new Dictionary<string,List<StoredSnippet>>();
          foreach (SnippetIdentifier identifier in identifiers) {
            List<StoredSnippet> values;
            if (snippets.TryGetValue(identifier, out values)) {
              foreach (StoredSnippet value in values) {
                List<StoredSnippet> pieces;
                if (!map.TryGetValue(value.Language, out pieces)) {
                  pieces = new List<StoredSnippet>();
                  map.Add(value.Language, pieces);
                }
                pieces.Add(value);
              }  
            }
          }

          XmlWriter writer = node.InsertAfter();
          writer.WriteStartElement("snippets");
          writer.WriteAttributeString("reference", reference);
          
          foreach (KeyValuePair<string,List<StoredSnippet>> entry in map) {
            writer.WriteStartElement("snippet");
            writer.WriteAttributeString("language", entry.Key);

            List<StoredSnippet> values = entry.Value;
                        for (int i=0; i<values.Count; i++) {
              if (i>0) writer.WriteString("\n...\n\n\n");
                            writer.WriteString(values[i].Text);
                            // writer.WriteString(System.Web.HttpUtility.HtmlDecode(values[i].Text));
            }

            writer.WriteEndElement();
          }

          writer.WriteEndElement();
          writer.Close();
            
        }

        } else {
          WriteMessage(MessageLevel.Warn, String.Format("The code reference '{0}' is not well-formed", reference));
        }

        node.DeleteSelf();

      }
    }

    private XPathExpression selector;

    private XmlNamespaceManager context = new CustomContext();

    private static Regex validSnippetReference = new Regex(@"^[^#\a\b\f\n\r\t\v]+#(\w+,)*\w+$", RegexOptions.Compiled);

    // colorization logic

    private Dictionary<string,List<ColorizationRule>> colorization = new Dictionary<string,List<ColorizationRule>>();

    private static ICollection<Region> ColorizeSnippet (string text, List<ColorizationRule> rules) {

      // Console.WriteLine("colorizing: '{0}'", text);

      // create a linked list consiting entirely of one uncolored region
      LinkedList<Region> regions = new LinkedList<Region>();
      regions.AddFirst( new Region(text) );

      // loop over colorization rules
      foreach (ColorizationRule rule in rules) {

        // loop over regions

        LinkedListNode<Region> node = regions.First;        
        while(node != null) {

          // only try to colorize uncolored regions
          if (node.Value.ClassName != null) {
            node = node.Next;
            continue;
          }

          // find matches in the region
          string regionText = node.Value.Text;
          Capture[] matches = rule.Apply(regionText);

          // if no matches were found, continue to the next region
          if (matches.Length == 0) {
            node = node.Next;
            continue;            
          }

          // we found matches; break the region into colored and uncolered subregions

                    // index is where we are looking from; index-1 is the end of the last match
          int index = 0;

          LinkedListNode<Region> referenceNode = node;
          
          foreach (Capture match in matches) {

            // create a leading uncolored region 
            if (match.Index > index) {
                            //Console.WriteLine("uncolored: {0} '{1}' -> {2} '{3}'", index, regionText[index], match.Index - 1, regionText[match.Index - 1]); 
              Region uncoloredRegion = new Region(regionText.Substring(index, match.Index-index));
              referenceNode = regions.AddAfter(referenceNode, uncoloredRegion);
            }

            // create a colored region
            // Console.WriteLine("name = {0}", rule.ClassName);
                        //Console.WriteLine("colored: {0} '{1}' -> {2} '{3}'", match.Index, regionText[match.Index], match.Index + match.Length - 1, regionText[match.Index + match.Length - 1]);
                        Region coloredRegion = new Region(rule.ClassName, regionText.Substring(match.Index, match.Length));
            referenceNode = regions.AddAfter(referenceNode, coloredRegion);

            index = match.Index + match.Length;            

          }

          // create a trailing uncolored region
          if (index < regionText.Length) {
            Region uncoloredRegion = new Region(regionText.Substring(index));
            referenceNode = regions.AddAfter(referenceNode, uncoloredRegion);
          }

          // remove the original node
          regions.Remove(node);

          node = referenceNode.Next;
        }


      }
      return(regions);

    }

    private static void WriteColorizedSnippet (ICollection<Region> regions, XmlWriter writer) {
      foreach (Region region in regions) {
        // Console.WriteLine("writing {0}", region.ClassName);
        if (region.ClassName == null) {
          writer.WriteString(region.Text);
        } else {
          writer.WriteStartElement("span");
          writer.WriteAttributeString("class", region.ClassName);
          writer.WriteString(region.Text);
          writer.WriteEndElement();
        }
      }
    }

    private static string StripLeadingSpaces (string text) {

      if (text == null) throw new ArgumentNullException("text");

      // Console.WriteLine("BEFORE:");
      // Console.WriteLine(text);

      // split the text into lines
      string[] lines = text.Split('\n');

      // no need to do this if there is only one line
      if (lines.Length == 1) return(lines[0]);

      // figure out how many leading spaces to delete
      int spaces = Int32.MaxValue;
      for (int i=0; i<lines.Length; i++) {

        string line = lines[i];

        // skip empty lines
        if (line.Length == 0) continue;

        // determine the number of leading spaces
        int index = 0;
        while (index < line.Length) {
          if (line[index] != ' ') break;
          index++;
        }

        if (index == line.Length) {
          // lines that are all spaces should just be treated as empty
          lines[i] = String.Empty;
        } else {
          // otherwise, keep track of the minimum number of leading spaces        
          if (index < spaces) spaces = index;
        }

      }

      // Console.WriteLine("SPACES = {0}", spaces);

      // re-form the string with leading spaces deleted
      StringBuilder result = new StringBuilder();
      foreach (string line in lines) {
        if (line.Length == 0) {
          result.AppendLine();
        } else {
          result.AppendLine(line.Substring(spaces));
        }
      }
      // Console.WriteLine("AFTER:");
      // Console.WriteLine(result.ToString());
      return(result.ToString());      

    }    
      
  }

  internal struct SnippetIdentifier {

    public SnippetIdentifier (string exampleId, string snippetId) {
      this.exampleId = exampleId.ToLower();
      this.snippetId = snippetId.ToLower();
    }

    public SnippetIdentifier (string identifier) {
      int index = identifier.LastIndexOf('#');
      exampleId = identifier.Substring(0,index).ToLower();
      snippetId = identifier.Substring(index+1).ToLower();
    }

    private string exampleId;

    private string snippetId;

    public string Example {
      get {
        return(exampleId);
      }
    }

    public string Snippet {
      get {
        return(snippetId);
      }
    }

    public override string ToString() {
      return(String.Format("{0}#{1}", exampleId, snippetId));
    }

    public static SnippetIdentifier[] ParseReference (string reference) {

      int index = reference.IndexOf('#');
      if (index < 0) return(new SnippetIdentifier[0]);

      string example = reference.Substring(0,index);
      string[] snippets = reference.Substring(index+1).Split(',');

      SnippetIdentifier[] identifiers = new SnippetIdentifier[snippets.Length];
      for (int i=0; i<snippets.Length; i++) {
        identifiers[i] = new SnippetIdentifier(example, snippets[i]);
      }
      return(identifiers);

    }

  }

  internal class StoredSnippet {

    public StoredSnippet (string text, string language) {
      this.text = text;
      this.language = language;
    }

    private string text;

    private string language;

    public string Text {
      get {
        return(text);
      }
    }

    public string Language {
      get {
        return(language);
      }
    }
  }

  internal class ColorizationRule {

    public ColorizationRule (string pattern, string className) : this(pattern, null, className) {}

    public ColorizationRule (string pattern, string region, string className) {
      this.pattern = new Regex(pattern, RegexOptions.Compiled|RegexOptions.Multiline);
      this.region = region;
      this.className = className;
    }    

    private Regex pattern;

    private string region;

    private string className;

    public string ClassName {
      get {
        return(className);
      }
    }

    public Capture[] Apply (string text) {

      MatchCollection matches = pattern.Matches(text);
      Capture[] captures = new Capture[matches.Count];

      if (region == null) {
        matches.CopyTo(captures, 0);
        return(captures);
      } else {
        for (int i=0; i<captures.Length; i++) {
          captures[i] = matches[i].Groups[region];
        }
        return(captures);
      }

    }

  }

  internal struct Region {

    public Region (string text) : this(null, text) {}

    public Region (string className, string text) {
      this.className = className;
      this.text = text;
    }

    private string className;

    private string text;


    public string ClassName {
      get {
        return(className);
      }
    }

    public string Text {
      get {
        return(text);
      }
    }

  }

}
www.java2v.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.