context: // ***************************************************************
  // <copyright file="HtmlTokenBuilder.cs" company="Microsoft">
  //      Copyright (C) Microsoft Corporation.  All rights reserved.
  // </copyright>
  // <summary>
  //      ...
  // </summary>
  // ***************************************************************
  
  namespace Microsoft.Exchange.Data.TextConverters.Internal.Html
  {
      using System;
      using Data.Internal;
  
      
  
      internal class HtmlTokenBuilder : TokenBuilder
      {
          
  
          
          protected const byte BuildStateEndedHtml = BuildStateEnded + 1; 
          protected const byte BuildStateTagStarted = 20;
          protected const byte BuildStateTagText = 21;
          protected const byte BuildStateTagName = 22;
          protected const byte BuildStateTagBeforeAttr = 23;
          
          protected const byte BuildStateTagAttrName = 24;
          protected const byte BuildStateTagEndAttrName = 25;
          protected const byte BuildStateTagAttrValue = 26;
          protected const byte BuildStateTagEndAttrValue = 27;
  
          protected HtmlToken htmlToken;
  
          protected int maxAttrs;
  
          protected int numCarryOverRuns;        
          protected int carryOverRunsHeadOffset;
          protected int carryOverRunsLength;
  
          
  
          public HtmlTokenBuilder(
                      char[] buffer, 
                      int maxRuns, 
                      int maxAttrs, 
                      bool testBoundaryConditions) :
              base(new HtmlToken(), buffer, maxRuns, testBoundaryConditions)
          {
              htmlToken = (HtmlToken) base.Token;
  
              var initialAttrs = 8;
  
              if (maxAttrs != 0)
              {
                  if (!testBoundaryConditions)
                  {
                      this.maxAttrs = maxAttrs;
                  }
                  else
                  {
                      initialAttrs = 1;
                      this.maxAttrs = 5;
                  }
  
                  
  
                  htmlToken.attributeList = new HtmlToken.AttributeEntry[initialAttrs];
              }
  
              htmlToken.nameIndex = HtmlNameIndex._NOTANAME;
          }
  
          
  
          public new HtmlToken Token => htmlToken;
  
  
          public bool IncompleteTag => state >= FirstStarted && state != BuildStateText;
  
  
          public override void Reset()
          {
              if (state >= BuildStateEndedHtml)
              {
                  htmlToken.Reset();
  
                  numCarryOverRuns = 0;
              }
  
              base.Reset();
          }
  
          
  
          public HtmlTokenId MakeEmptyToken(HtmlTokenId tokenId)
          {
              return (HtmlTokenId)base.MakeEmptyToken((TokenId)tokenId);
          }
  
          
  
          public HtmlTokenId MakeEmptyToken(HtmlTokenId tokenId, int argument)
          {
              return (HtmlTokenId)base.MakeEmptyToken((TokenId)tokenId, argument);
          }
  
          
  
          public void StartTag(HtmlNameIndex nameIndex, int baseOffset)
          {
              InternalDebug.Assert(state == BuildStateInitialized && htmlToken.IsEmpty);
  
              state = BuildStateTagStarted;
  
              htmlToken.tokenId = (TokenId)HtmlTokenId.Tag;
              htmlToken.partMajor = HtmlToken.TagPartMajor.Begin;
              htmlToken.partMinor = HtmlToken.TagPartMinor.Empty;
              htmlToken.nameIndex = nameIndex;
              htmlToken.tagIndex = HtmlNameData.names[(int)nameIndex].tagIndex;
  
              htmlToken.whole.headOffset = baseOffset;
              tailOffset = baseOffset;
          }
  
          
  
          public void AbortConditional(bool comment)
          {
              InternalDebug.Assert(htmlToken.nameIndex == HtmlNameIndex._CONDITIONAL);
              InternalDebug.Assert(state == BuildStateTagStarted || state == BuildStateTagText);
  
              
              
              
              
              
              
  
              htmlToken.nameIndex = comment ? HtmlNameIndex._COMMENT : HtmlNameIndex._BANG;
          }
  
          
  
          public void SetEndTag()
          {
              htmlToken.flags |= HtmlToken.TagFlags.EndTag;
          }
  
          
  
          public void SetEmptyScope()
          {
              htmlToken.flags |= HtmlToken.TagFlags.EmptyScope;
          }
  
          
  
          public void StartTagText()
          {
              InternalDebug.Assert(state == BuildStateTagStarted);
  
              state = BuildStateTagText;
  
              htmlToken.unstructured.Initialize(htmlToken.whole.tail, tailOffset);
              htmlToken.unstructuredPosition.Rewind(htmlToken.unstructured);
          }
  
          
  
          public void EndTagText()
          {
              if (htmlToken.unstructured.head == htmlToken.whole.tail)
              {
                  InternalDebug.Assert(htmlToken.unstructured.headOffset == tailOffset);
  
                  AddNullRun(HtmlRunKind.TagText);
              }
  
              state = BuildStateTagStarted;
          }
  
          
  
          public void StartTagName()
          {
              InternalDebug.Assert(state == BuildStateTagStarted);
  
              state = BuildStateTagName;
  
              htmlToken.partMinor |= HtmlToken.TagPartMinor.BeginName;
  
              htmlToken.name.Initialize(htmlToken.whole.tail, tailOffset);
              htmlToken.localName.Initialize(htmlToken.whole.tail, tailOffset);
              htmlToken.namePosition.Rewind(htmlToken.name);
          }
  
          public void EndTagNamePrefix()
          {
              InternalDebug.Assert(state == BuildStateTagName);
  
              htmlToken.localName.Initialize(htmlToken.whole.tail, tailOffset);
          }
  
          
  
          public void EndTagName(int nameLength)
          {
              InternalDebug.Assert(state == BuildStateTagName);
  
              InternalDebug.Assert(htmlToken.partMinor == HtmlToken.TagPartMinor.BeginName || 
                                  htmlToken.partMinor == HtmlToken.TagPartMinor.ContinueName);
  
              if (htmlToken.localName.head == htmlToken.whole.tail)
              {
                  InternalDebug.Assert(htmlToken.localName.headOffset == tailOffset);
  
                  AddNullRun(HtmlRunKind.Name);
                  if (htmlToken.localName.head == htmlToken.name.head)
                  {
                      htmlToken.flags |= HtmlToken.TagFlags.EmptyTagName;
                  }
              }
  
              htmlToken.partMinor |= HtmlToken.TagPartMinor.EndName;
  
              if (htmlToken.IsTagBegin)
              {
                  AddSentinelRun();
                  htmlToken.nameIndex = LookupName(nameLength, htmlToken.name);
                  htmlToken.tagIndex = htmlToken.originalTagIndex = HtmlNameData.names[(int)htmlToken.nameIndex].tagIndex;
              }
  
              state = BuildStateTagBeforeAttr;
          }
  
          
  
          public void EndTagName(HtmlNameIndex resolvedNameIndex)
          {
              InternalDebug.Assert(state == BuildStateTagName);
  
              InternalDebug.Assert(htmlToken.partMinor == HtmlToken.TagPartMinor.BeginName || 
                                  htmlToken.partMinor == HtmlToken.TagPartMinor.ContinueName);
  
              if (htmlToken.localName.head == htmlToken.whole.tail)
              {
                  InternalDebug.Assert(htmlToken.localName.headOffset == tailOffset);
  
                  AddNullRun(HtmlRunKind.Name);
                  if (htmlToken.localName.head == htmlToken.name.head)
                  {
                      htmlToken.flags |= HtmlToken.TagFlags.EmptyTagName;
                  }
              }
  
              htmlToken.partMinor |= HtmlToken.TagPartMinor.EndName;
  
              if (htmlToken.IsTagBegin)
              {
                  htmlToken.nameIndex = resolvedNameIndex;
                  htmlToken.tagIndex = htmlToken.originalTagIndex = HtmlNameData.names[(int)resolvedNameIndex].tagIndex;
              }
  
              state = BuildStateTagBeforeAttr;
          }
  
          
  
          public bool CanAddAttribute()
          {
              InternalDebug.Assert(state == BuildStateTagBeforeAttr);
  
              return htmlToken.attributeTail < maxAttrs;
          }
  
          
  
          public void StartAttribute()
          {
              if (htmlToken.attributeTail == htmlToken.attributeList.Length)
              {
                  
  
                  
                  InternalDebug.Assert(htmlToken.attributeList.Length < maxAttrs);
  
                  int newSize;
  
                  if (maxAttrs / 2 > htmlToken.attributeList.Length)
                  {
                      newSize = htmlToken.attributeList.Length * 2;
                  }
                  else
                  {
                      newSize = maxAttrs;
                  }
  
                  var newAttrs = new HtmlToken.AttributeEntry[newSize];
  
                  Array.Copy(htmlToken.attributeList, 0, newAttrs, 0, htmlToken.attributeTail);
  
                  htmlToken.attributeList = newAttrs;
              }
  
              InternalDebug.Assert(htmlToken.attributeTail < htmlToken.attributeList.Length);
              InternalDebug.Assert(state == BuildStateTagBeforeAttr);
  
              InternalDebug.Assert(htmlToken.partMinor == HtmlToken.TagPartMinor.Empty ||
                                  htmlToken.partMinor == HtmlToken.TagPartMinor.EndName ||
                                  htmlToken.partMinor == HtmlToken.TagPartMinor.EndNameWithAttributes ||
                                  htmlToken.partMinor == HtmlToken.TagPartMinor.CompleteName ||
                                  htmlToken.partMinor == HtmlToken.TagPartMinor.CompleteNameWithAttributes ||
                                  htmlToken.partMinor == HtmlToken.TagPartMinor.EndAttribute ||
                                  htmlToken.partMinor == HtmlToken.TagPartMinor.EndAttributeWithOtherAttributes ||
                                  htmlToken.partMinor == HtmlToken.TagPartMinor.Attributes);
  
              if (htmlToken.partMinor == HtmlToken.TagPartMinor.Empty)
              {
                  htmlToken.partMinor = HtmlToken.TagPartMinor.BeginAttribute;
              }
  
              htmlToken.attributeList[htmlToken.attributeTail].nameIndex = HtmlNameIndex.Unknown;
              htmlToken.attributeList[htmlToken.attributeTail].partMajor = HtmlToken.AttrPartMajor.Begin;
              htmlToken.attributeList[htmlToken.attributeTail].partMinor = HtmlToken.AttrPartMinor.BeginName;
              htmlToken.attributeList[htmlToken.attributeTail].quoteChar = 0;
              htmlToken.attributeList[htmlToken.attributeTail].name.Initialize(htmlToken.whole.tail, tailOffset);
              htmlToken.attributeList[htmlToken.attributeTail].localName.Initialize(htmlToken.whole.tail, tailOffset);
              htmlToken.attributeList[htmlToken.attributeTail].value.Reset();
  
              state = BuildStateTagAttrName;
          }
  
          public void EndAttributeNamePrefix()
          {
              InternalDebug.Assert(state == BuildStateTagAttrName);
  
              htmlToken.attributeList[htmlToken.attributeTail].localName.Initialize(htmlToken.whole.tail, tailOffset);
          }
  
          
  
          public void EndAttributeName(int nameLength)
          {
              InternalDebug.Assert(state == BuildStateTagAttrName);
  
              htmlToken.attributeList[htmlToken.attributeTail].partMinor |= HtmlToken.AttrPartMinor.EndName;
  
              if (htmlToken.attributeList[htmlToken.attributeTail].localName.head == htmlToken.whole.tail)
              {
                  InternalDebug.Assert(htmlToken.attributeList[htmlToken.attributeTail].localName.headOffset == tailOffset);
  
                  AddNullRun(HtmlRunKind.Name);
                  if (htmlToken.attributeList[htmlToken.attributeTail].localName.head == htmlToken.attributeList[htmlToken.attributeTail].name.head)
                  {
                      htmlToken.attributeList[htmlToken.attributeTail].partMajor |= HtmlToken.AttrPartMajor.EmptyName;
                  }
              }
  
              if (htmlToken.attributeList[htmlToken.attributeTail].IsAttrBegin)
              {
                  AddSentinelRun();
                  htmlToken.attributeList[htmlToken.attributeTail].nameIndex = LookupName(nameLength, htmlToken.attributeList[htmlToken.attributeTail].name);
              }
  
              state = BuildStateTagEndAttrName;
          }
  
          
  
          public void StartValue()
          {
              InternalDebug.Assert(state == BuildStateTagEndAttrName);
  
              htmlToken.attributeList[htmlToken.attributeTail].value.Initialize(htmlToken.whole.tail, tailOffset);
  
              htmlToken.attributeList[htmlToken.attributeTail].partMinor |= HtmlToken.AttrPartMinor.BeginValue;
  
              state = BuildStateTagAttrValue;
          }
  
          
  
          public void SetValueQuote(char ch)
          {
              InternalDebug.Assert(state == BuildStateTagAttrValue);
              InternalDebug.Assert(ParseSupport.QuoteCharacter(ParseSupport.GetCharClass(ch)));
  
              htmlToken.attributeList[htmlToken.attributeTail].IsAttrValueQuoted = true;
              htmlToken.attributeList[htmlToken.attributeTail].quoteChar = (byte) ch;
          }
  
          
  
          public void EndValue()
          {
              InternalDebug.Assert(state == BuildStateTagAttrValue);
  
              if (htmlToken.attributeList[htmlToken.attributeTail].value.head == htmlToken.whole.tail)
              {
                  InternalDebug.Assert(htmlToken.attributeList[htmlToken.attributeTail].value.headOffset == tailOffset);
  
                  AddNullRun(HtmlRunKind.AttrValue);
              }
  
              htmlToken.attributeList[htmlToken.attributeTail].partMinor |= HtmlToken.AttrPartMinor.EndValue;
  
              state = BuildStateTagEndAttrValue;
          }
  
          
  
          public void EndAttribute()
          {
              InternalDebug.Assert(state == BuildStateTagEndAttrName || state == BuildStateTagEndAttrValue);
  
              htmlToken.attributeList[htmlToken.attributeTail].partMajor |= HtmlToken.AttrPartMajor.End;
  
              htmlToken.attributeTail ++;
  
              if (htmlToken.attributeTail < htmlToken.attributeList.Length)
              {
                  
  
                  htmlToken.attributeList[htmlToken.attributeTail].partMajor = HtmlToken.AttrPartMajor.None;
                  htmlToken.attributeList[htmlToken.attributeTail].partMinor = HtmlToken.AttrPartMinor.Empty;
              }
  
              if (htmlToken.partMinor == HtmlToken.TagPartMinor.BeginAttribute)
              {
                  htmlToken.partMinor = HtmlToken.TagPartMinor.Attributes;
              }
              else if (htmlToken.partMinor == HtmlToken.TagPartMinor.ContinueAttribute)
              {
                  htmlToken.partMinor = HtmlToken.TagPartMinor.EndAttribute;
              }
              else
              {
                  InternalDebug.Assert(htmlToken.partMinor == HtmlToken.TagPartMinor.CompleteName ||
                                      htmlToken.partMinor == HtmlToken.TagPartMinor.CompleteNameWithAttributes ||
                                      htmlToken.partMinor == HtmlToken.TagPartMinor.EndName ||
                                      htmlToken.partMinor == HtmlToken.TagPartMinor.EndNameWithAttributes ||
                                      htmlToken.partMinor == HtmlToken.TagPartMinor.EndAttribute ||
                                      htmlToken.partMinor == HtmlToken.TagPartMinor.EndAttributeWithOtherAttributes ||
                                      htmlToken.partMinor == HtmlToken.TagPartMinor.Attributes);
  
                  htmlToken.partMinor |= HtmlToken.TagPartMinor.Attributes;
              }
  
              state = BuildStateTagBeforeAttr;
          }
  
          
  
          public void EndTag(bool complete)
          {
              if (complete)
              {
                  
  
                  if (state != BuildStateTagBeforeAttr)
                  {
                      if (state == BuildStateTagText)
                      {
                          EndTagText();
                      }
                      else if (state == BuildStateTagName)
                      {
                          EndTagName(0);
                      }
                      else
                      {
                          if (state == BuildStateTagAttrName)
                          {
                              EndAttributeName(0);
                          }
                          else if (state == BuildStateTagAttrValue)
                          {
                              EndValue();
                          }
  
                          if (state == BuildStateTagEndAttrName || state == BuildStateTagEndAttrValue)
                          {
                              EndAttribute();
                          }
                      }
                  }
  
                  
                  AddSentinelRun();
  
                  InternalDebug.Assert(state == BuildStateTagBeforeAttr || state == BuildStateTagStarted);
  
                  state = BuildStateEndedHtml;
                  htmlToken.partMajor |= HtmlToken.TagPartMajor.End;
              }
              else
              {
                  if (state >= BuildStateTagAttrName)
                  {
                      
                      
  
                      InternalDebug.Assert(!complete);
  
  
gt:                     if (0 != htmlToken.attributeTail ||