context: /* 
  * Copyright (C) 2002-2011 XimpleWare, info@ximpleware.com
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  */
  using System;
  using com.ximpleware.transcode;
  namespace com.ximpleware
  {
  
      /// <summary> XMLModifier offers an easy-to-use interface for users to
      /// take advantage of the incremental update information
      /// The XML modifier assumes there is a master document on which
      /// the modification is applied: users can remove an element, update
      /// a token, or insert new content anywhere in the document
      /// 
      /// </summary>
      public class XMLModifier
      {
          protected internal VTDNav md; // master document
  
          public const int XML_DELETE = 0;
          private const long MASK_DELETE = 0x00000000000000000L; //0000
          private const long MASK_INSERT_SEGMENT_BYTE = 0x2000000000000000L; //0010
          private const long MASK_INSERT_BYTE = 0x4000000000000000L; //0100
          private const long MASK_INSERT_SEGMENT_BYTE_ENCLOSED = 0x6000000000000000L; //0110
          private const ulong MASK_INSERT_BYTE_ENCLOSED = 0x8000000000000000L; //1000
          //private const long MASK_INSERT_SEGMENT_STRING = 0x6000000000000000L; //0110
  
          //UPGRADE_TODO: Literal detected as an unsigned long can generate compilation errors. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1175'"
          //private const ulong MASK_INSERT_STRING = 0x8000000000000000L; //1000        
          private const ulong MASK_INSERT_FRAGMENT_NS = 0xa000000000000000L; //1010
          private const ulong MASK_INSERT_FRAGMENT_NS_ENCLOSED = 0x8000000000000000L; //1110
          private static byte[] ba1;// = null;// = {0x3e,0};
          private static byte[] ba2;// = null;// = {0x3c,0};
  
          private static byte[] ba3;// = null;// = {0,0x3e};
          private static byte[] ba4;// = null;// = {0,0x3c};
  
          protected internal FastObjectBuffer fob;
          protected internal FastLongBuffer flb;
          internal intHash deleteHash; // one deletion per offset val
          internal intHash insertHash; // one insert per offset val
          protected internal System.String charSet;
          protected internal System.Text.Encoding eg;
          int encoding;
          /// <summary> Constructor for XMLModifier that takes VTDNav object as the master document</summary>
          /// <param name="masterDocument">is the document on which the modification is applied
          /// </param>
          /// 
          static XMLModifier()
          {
              ba1 = new byte[2];
              ba1[0] = 0x3e; ba1[1] = 0;
              ba2 = new byte[2];
              ba2[0] = 0x3c; ba2[1] = 0;
  
              ba3 = new byte[2];
              ba3[0] = 0; ba3[1] = 0x3e;
              ba4 = new byte[2];
              ba4[0] = 0; ba4[1] = 0x3c;
          }
          public XMLModifier(VTDNav masterDocument)
          {
              bind(masterDocument);
          }
  
  
          /// <summary> Argument-less constructor for XMLModifier,
          /// needs to call bind to attach the master document to an instance
          /// of XMLModifier
          /// 
          /// </summary>
          public XMLModifier()
          {
              md = null;
          }
          /// <summary> Attach master document to this instance of XMLModifier</summary>
          /// <param name="masterDocument">*
          /// </param>
          public void bind(VTDNav masterDocument)
          {
              if (masterDocument == null)
                  throw new System.ArgumentException("MasterDocument can't be null");
              md = masterDocument;
              flb = new FastLongBuffer();
              fob = new FastObjectBuffer();
              int i = intHash.determineHashWidth(md.vtdSize);
              insertHash = new intHash(i);
              deleteHash = new intHash(i);
              //determine encoding charset string here
              encoding = md.getEncoding();
              switch (encoding)
              {
  
                  case VTDNav.FORMAT_ASCII:
                      charSet = "ascii";
                      break;
  
                  case VTDNav.FORMAT_ISO_8859_1:
                      charSet = "iso-8859-1";
                      break;
  
                  case VTDNav.FORMAT_UTF8:
                      charSet = "utf-8";
                      break;
  
                  case VTDNav.FORMAT_UTF_16BE:
                      charSet = "utf-16be";
                      break;
  
                  case VTDNav.FORMAT_UTF_16LE:
                      charSet = "utf-16le";
                      break;
                  case VTDNav.FORMAT_ISO_8859_2:
                      charSet = "iso-8859-2";
                      break;
                  case VTDNav.FORMAT_ISO_8859_3:
                      charSet = "iso-8859-3";
                      break;
                  case VTDNav.FORMAT_ISO_8859_4:
                      charSet = "iso-8859-4";
                      break;
                  case VTDNav.FORMAT_ISO_8859_5:
                      charSet = "iso-8859-5";
                      break;
                  case VTDNav.FORMAT_ISO_8859_6:
                      charSet = "iso-8859-6";
                      break;
                  case VTDNav.FORMAT_ISO_8859_7:
                      charSet = "iso-8859-7";
                      break;
                  case VTDNav.FORMAT_ISO_8859_8:
                      charSet = "iso-8859-8";
                      break;
                  case VTDNav.FORMAT_ISO_8859_9:
                      charSet = "iso-8859-9";
                      break;
                  case VTDNav.FORMAT_ISO_8859_10:
                      charSet = "iso-8859-10";
                      break;
                  case VTDNav.FORMAT_ISO_8859_11:
                      charSet = "iso-8859-11";
                      break;
                  case VTDNav.FORMAT_ISO_8859_12:
                      charSet = "iso-8859-12";
                      break;
                  case VTDNav.FORMAT_ISO_8859_13:
                      charSet = "iso-8859-13";
                      break;
                  case VTDNav.FORMAT_ISO_8859_14:
                      charSet = "iso-8859-14";
                      break;
                  case VTDNav.FORMAT_ISO_8859_15:
                      charSet = "iso-8859-15";
                      break;
                  case VTDNav.FORMAT_WIN_1250:
                      charSet = "windows-1250";
                      break;
                  case VTDNav.FORMAT_WIN_1251:
                      charSet = "windows-1251";
                      break;
                  case VTDNav.FORMAT_WIN_1252:
                      charSet = "windows-1252";
                      break;
                  case VTDNav.FORMAT_WIN_1253:
                      charSet = "windows-1253";
                      break;
                  case VTDNav.FORMAT_WIN_1254:
                      charSet = "windows-1254";
                      break;
                  case VTDNav.FORMAT_WIN_1255:
                      charSet = "windows-1255";
                      break;
                  case VTDNav.FORMAT_WIN_1256:
                      charSet = "windows-1256";
                      break;
                  case VTDNav.FORMAT_WIN_1257:
                      charSet = "windows-1257";
                      break;
                  case VTDNav.FORMAT_WIN_1258:
                      charSet = "windows-1258";
                      break;
  
                  default:
                      throw new ModifyException("Master document encoding not yet supported by XML modifier");
  
              }
              eg = System.Text.Encoding.GetEncoding(charSet);
          }
          /// <summary> Removes content from the master XML document 
          /// It first calls getCurrentIndex() if the result is 
          /// a starting tag, then the entire element referred to
          /// by the starting tag is removed
          /// If the result is an attribute name or ns node, then 
          /// the corresponding attribute name/value pair is removed
          /// If the token type is one of text, CDATA or commment,
          /// then the entire node, including the starting and ending 
          /// delimiting text surrounding the content, is removed
          /// 
          /// </summary>
          public void remove()
          {
              int i = md.getCurrentIndex();
              int type = md.getTokenType(i);
              if (type == VTDNav.TOKEN_STARTING_TAG)
              {
                  long l = md.getElementFragment();
                  removeContent((int)l, (int)(l >> 32));
              }
              else if (type == VTDNav.TOKEN_ATTR_NAME || type == VTDNav.TOKEN_ATTR_NS)
              {
                  removeAttribute(i);
              }
              else
              {
                  removeToken(i);
              }
          }
  
          /// <summary> Remove the token content, if the token type is text, CDATA
          /// or comment, then the entire node, including the starting and 
          /// ending delimiting text, will be removed as well
          /// </summary>
          /// <param name="i">*
          /// </param>
          public void removeToken(int i)
          {
              int type = md.getTokenType(i);
              int os = md.getTokenOffset(i);
              int len =
              (type == VTDNav.TOKEN_STARTING_TAG
                  || type == VTDNav.TOKEN_ATTR_NAME
                  || type == VTDNav.TOKEN_ATTR_NS)
                  ? md.getTokenLength(i) & 0xffff
                  : md.getTokenLength(i);
              switch (type)
              {
  
                  case VTDNav.TOKEN_CDATA_VAL:
                      if (md.getEncoding() < VTDNav.FORMAT_UTF_16BE)
                          removeContent(os - 9, len + 12);
                      else
                          removeContent((os - 9) << 1, (len + 12) << 1);
                      return;
  
  
                  case VTDNav.TOKEN_COMMENT:
                      if (md.getEncoding() < VTDNav.FORMAT_UTF_16BE)
                          removeContent(os - 4, len + 7);
                      else
                          removeContent((os - 4) << 1, (len + 7) << 1);
                      return;
  
  
                  default:
                      if (md.getEncoding() < VTDNav.FORMAT_UTF_16BE)
                          removeContent(os, len);
                      else
                          removeContent((os) << 1, (len) << 1);
                      return;
  
              }
          }
          /// <summary> remove an attribute name value pair from the master document</summary>
          /// <param name="attrNameIndex">*
          /// </param>
          public void removeAttribute(int attrNameIndex)
          {
              int type = md.getTokenType(attrNameIndex);
              if (type != VTDNav.TOKEN_ATTR_NAME && type != VTDNav.TOKEN_ATTR_NS)
                  throw new ModifyException("token type should be attribute name");
              int os1 = md.getTokenOffset(attrNameIndex);
              int os2 = md.getTokenOffset(attrNameIndex + 1);
              int len2 = md.getTokenLength(attrNameIndex + 1);
              if (md.getEncoding() < VTDNav.FORMAT_UTF_16BE)
                  removeContent(os1, os2 + len2 - os1 + 1);
              else
                  removeContent(os1 << 1, (os2 + len2 - os1 + 1) << 1);
          }
  
          /// <summary> Remove a segment from the byte content denoted by the offset and length 
          /// </summary>
          /// <param name="offset">
          /// </param>
          /// <param name="len">*
          /// </param>
          public void removeContent(int offset, int len)
          {
              if (offset < md.docOffset || len > md.docLen || offset + len > md.docOffset + md.docLen)
              {
                  throw new ModifyException("Invalid offset or length for removeContent");
              }
              if (deleteHash.isUnique(offset) == false)
                  throw new ModifyException("There can be only one deletion per offset value");
  
              flb.append(((long)len) << 32 | offset | MASK_DELETE);
              fob.append((System.Object)null);
          }
  
          /// <summary> Insert a byte array in the document</summary>
          /// <param name="offset">
          /// </param>
          /// <param name="content">*
          /// </param>
          public void insertBytesAt(int offset, byte[] content)
          {
  
              if (insertHash.isUnique(offset) == false)
              {
                  throw new ModifyException("There can be only one insert per offset");
              }
              flb.append((long)offset | MASK_INSERT_BYTE);
              fob.append(content);
          }
  
          /// <summary>
          ///  Insert a segment in a byte array in the document
          /// </summary>
          /// <param name="offset"></param>
          /// <param name="content"></param>
          /// <param name="contentOffset"></param>
          /// <param name="contentLen"></param>
          public void insertBytesAt(int offset, byte[] content, int contentOffset, int contentLen)
          {
              if (insertHash.isUnique(offset) == false)
              {
                  throw new ModifyException("There can be only one insert per offset");
              }
              if (contentOffset < 0
                      || contentLen < 0
                      || contentOffset + contentLen > content.Length)
              {
                  throw new ModifyException("Invalid contentOffset and/or contentLen");
              }
              flb.append((long)offset | MASK_INSERT_SEGMENT_BYTE);
              ByteSegment bs = new ByteSegment();
              bs.ba = content;
              bs.len = contentLen;
              bs.offset = contentOffset;
  
              fob.append(bs);
          }
  
  
          private void insertBytesAt(int offset, byte[] content, long l)
          {
              if (insertHash.isUnique(offset) == false)
              {
                  throw new ModifyException("There can be only one insert per offset");
              }
              int contentOffset = (int)l;
              int contentLen = (int)(l >> 32);
              if (contentOffset < 0
                      || contentLen < 0
                      || contentOffset + contentLen > content.Length)
              {
                  throw new ModifyException("Invalid contentOffset and/or contentLen");
              }
              flb.append((long)offset | MASK_INSERT_SEGMENT_BYTE);
              ByteSegment bs = new ByteSegment();
              bs.ba = content;
              bs.len = contentLen;
              bs.offset = contentOffset;
              fob.append(bs);
          }
          /// <summary>
          /// 
          /// </summary>
          /// <param name="index"></param>
          /// <param name="newContentBytes"></param>
          /// <param name="contentOffset"></param>
          /// <param name="contentLen"></param>
          /// <param name="src_encoding"></param>
          public void updateToken(int index, byte[] newContentBytes,
          int contentOffset, int contentLen, int src_encoding)
          {
  
              if (src_encoding == encoding)
              {
                  updateToken(index, newContentBytes, contentOffset, contentLen);
                  return;
              }
              if (newContentBytes == null)
                  throw new System.ArgumentException("newContentBytes can't be null");
  
              int offset = md.getTokenOffset(index);
              //int len = md.getTokenLength(index);
              int type = md.getTokenType(index);
              int len = (type == VTDNav.TOKEN_STARTING_TAG
                      || type == VTDNav.TOKEN_ATTR_NAME || type == VTDNav.TOKEN_ATTR_NS) ? md
                      .getTokenLength(index) & 0xffff
                      : md.getTokenLength(index);
  
              // one insert
              byte[] bo = Transcoder.transcode(newContentBytes, contentOffset,
                      contentLen, src_encoding, encoding);
  
              switch (type)
              {
                  case VTDNav.TOKEN_CDATA_VAL:
                      if (encoding < VTDNav.FORMAT_UTF_16BE)
                          insertBytesAt(offset - 9, bo);
                      else
                          insertBytesAt((offset - 9) << 1, bo);
                      break;
                  case VTDNav.TOKEN_COMMENT:
                      if (encoding < VTDNav.FORMAT_UTF_16BE)
                          insertBytesAt(offset - 4, bo);
                      else
                          insertBytesAt((offset - 4) << 1, bo);
                      break;
  
                  default:
                      if (encoding < VTDNav.FORMAT_UTF_16BE)
                          insertBytesAt(offset, bo);
                      else
                          insertBytesAt(offset << 1, bo);
                      break;
              }
              // one delete
              removeToken(index);
          }
          /// <summary>
          /// 
          /// </summary>
          /// <param name="index"></param>
          /// <param name="newContentBytes"></param>
          /// <param name="src_encoding"></param>
          public void updateToken(int index, byte[] newContentBytes, int src_encoding)
          {
              if (src_encoding == encoding)
              {
                  updateToken(index, newContentBytes);
                  return;
              }
              if (newContentBytes == null)
                  throw new System.ArgumentException
                  ("newContentBytes can't be null");
              int offset = md.getTokenOffset(index);
  
              int type = md.getTokenType(index);
              int len =
                 (type == VTDNav.TOKEN_STARTING_TAG
                     || type == VTDNav.TOKEN_ATTR_NAME
                     || type == VTDNav.TOKEN_ATTR_NS)
                     ? md.getTokenLength(index) & 0xffff
                     : md.getTokenLength(index);
              // one insert
              byte[] bo = Transcoder.transcode(newContentBytes, 0,
                      newContentBytes.Length, src_encoding, encoding);
              switch (type)
              {
                  case VTDNav.TOKEN_CDATA_VAL:
                      if (encoding < VTDNav.FORMAT_UTF_16BE)
                          insertBytesAt(offset - 9, bo);
                      else
                          insertBytesAt((offset - 9) >> 1, bo);
                      break;
                  case VTDNav.TOKEN_COMMENT:
                      if (encoding < VTDNav.FORMAT_UTF_16BE)
                          insertBytesAt(offset - 4, bo);
                      else
                          insertBytesAt((offset - 4) >> 1, bo);
                      break;
  
                  default:
                      if (encoding < VTDNav.FORMAT_UTF_16BE)
                          insertBytesAt(offset, bo);
                      else
                          insertBytesAt(offset << 1, bo);
                      break;
              }
              // one delete
              removeToken(index);
          }
          /// <summary> Update the token with the byte array content,
          /// according to the encoding of the master document
          /// </summary>
          /// <param name="offset">
          /// </param>
          /// <param name="newContentBytes">*
          /// </param>
          public void updateToken(int index, byte[] newContentBytes)
          {
              if (newContentBytes == null)
                  throw new System.ArgumentException("String newContent can't be null");
              int offset = md.getTokenOffset(index);
              //int len = md.getTokenLength(index);
              int type = md.getTokenType(index);
              int len =
              (type == VTDNav.TOKEN_STARTING_TAG
                  || type == VTDNav.TOKEN_ATTR_NAME
                  || type == VTDNav.TOKEN_ATTR_NS)
                  ? md.getTokenLength(index) & 0xffff
                  : md.getTokenLength(index);
              // one insert
              switch (type)
              {
                  case VTDNav.TOKEN_CDATA_VAL:
                      if (md.getEncoding() < VTDNav.FORMAT_UTF_16BE)
                          insertBytesAt(offset - 9, newContentBytes);
                      else
                          insertBytesAt((offset - 9) << 1, newContentBytes);
                      break;
  
                  case VTDNav.TOKEN_COMMENT:
                      if (md.getEncoding() < VTDNav.FORMAT_UTF_16BE)
                      {
                          //UPGRADE_TODO: Method 'java.lang.String.getBytes' was converted to 'System.Text.Encoding.GetEncoding(string).GetBytes(string)' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javalangStringgetBytes_javalangString'"
                          insertBytesAt(offset - 4, newContentBytes);
                      }
                      else
                      {
                          //UPGRADE_TODO: Method 'java.lang.String.getBytes' was converted to 'System.Text.Encoding.GetEncoding(string).GetBytes(string)' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javalangStringgetBytes_javalangString'"
                          insertBytesAt((offset - 4) << 1, newContentBytes);
                      }
                      break;
                  default:
                      if (md.getEncoding() < VTDNav.FORMAT_UTF_16BE)
                          insertBytesAt(offset, newContentBytes);
                      else
                          insertBytesAt(offset << 1, newContentBytes);
                      break;
              }
              // one delete
              removeToken(index);
          }
  
  
          public void updateToken(int index, byte[] newContentBytes, int contentOffset, int contentLen)
          {
              if (newContentBytes == null)
                  throw new System.ArgumentException
                  ("newContentBytes can't be null");
  
              int offset = md.getTokenOffset(index);
              //int len = md.getTokenLength(index);
              int type = md.getTokenType(index);
              int len =
              (type == VTDNav.TOKEN_STARTING_TAG
                  || type == VTDNav.TOKEN_ATTR_NAME
                  || type == VTDNav.TOKEN_ATTR_NS)
                  ? md.getTokenLength(index) & 0xffff
                  : md.getTokenLength(index);
              // one insert
              switch (type)
              {
                  case VTDNav.TOKEN_CDATA_VAL:
                      if (encoding < VTDNav.FORMAT_UTF_16BE)
                          insertBytesAt(offset - 9, newContentBytes, contentOffset, contentLen);
                      else
                          insertBytesAt((offset - 9) << 1, newContentBytes, contentOffset, contentLen);
                      break;
                  case VTDNav.TOKEN_COMMENT:
                      if (encoding < VTDNav.FORMAT_UTF_16BE)
                          insertBytesAt(offset - 4, newContentBytes, contentOffset, contentLen);
                      else
                          insertBytesAt((offset - 4) << 1, newContentBytes, contentOffset, contentLen);
                      break;
  
                  default:
                      if (encoding < VTDNav.FORMAT_UTF_16BE)
                          insertBytesAt(offset, newContentBytes, contentOffset, contentLen);
                      else
                          insertBytesAt(offset << 1, newContentBytes, contentOffset, contentLen);
                      break;
              }
              // one delete
              removeToken(index);
          }
          /// <summary> Update the token with the given string value,
          /// notice that string will be converted into byte array
          /// according to the encoding of the master document
          /// </summary>
          /// <param name="offset">
          /// </param>
          /// <param name="newContent">*
          /// </param>
          public void updateToken(int index, System.String newContent)
          {
              if (newContent == null)
                  throw new System.ArgumentException("String newContent can't be null");
              int offset = md.getTokenOffset(index);
              //int len = md.getTokenLength(index);
              int type = md.getTokenType(index);
              int len =
              (type == VTDNav.TOKEN_STARTING_TAG
                  || type == VTDNav.TOKEN_ATTR_NAME
                  || type == VTDNav.TOKEN_ATTR_NS)
                  ? md.getTokenLength(index) & 0xffff
                  : md.getTokenLength(index);
              // one insert
              switch (type)
              {
  
                  case VTDNav.TOKEN_CDATA_VAL:
                      if (md.getEncoding() < VTDNav.FORMAT_UTF_16BE)
                          insertBytesAt(offset - 9, eg.GetBytes(newContent));
                      else
                          insertBytesAt((offset - 9) << 1, eg.GetBytes(newContent));
                      break;
  
                  case VTDNav.TOKEN_COMMENT:
                      if (md.getEncoding() < VTDNav.FORMAT_UTF_16BE)
                      {
                          //UPGRADE_TODO: Method 'java.lang.String.getBytes' was converted to 'System.Text.Encoding.GetEncoding(string).GetBytes(string)' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javalangStringgetBytes_javalangString'"
                          insertBytesAt(offset - 4, eg.GetBytes(newContent));
                      }
                      else
                      {
                          //UPGRADE_TODO: Method 'java.lang.String.getBytes' was converted to 'System.Text.Encoding.GetEncoding(string).GetBytes(string)' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javalangStringgetBytes_javalangString'"
                          insertBytesAt((offset - 4) << 1, eg.GetBytes(newContent));
                      }
                      break;
  
  
                  default:
                      insertBytesAt(offset, eg.GetBytes(newContent));
                      break;
  
              }
              // one delete
              removeToken(index);
          }
  
          /// <summary> 
          /// 
          /// 
          /// </summary>
          protected internal void sort()
          {
              if (flb.size_Renamed_Field > 0)
                  quickSort(0, flb.size_Renamed_Field - 1);
          }
  
          /// <summary> 
          /// This function will do the range checking and make
          /// sure there is no overlaping or invalid deletion 
          /// There can be only one deletion at one offset value
          /// Delete can't overlap with, nor contains, another delete
          /// 
          /// </summary>
          protected internal void check()
          {
              int os1, os2, temp;
              int size = flb.size_Renamed_Field;
              int inc = (md.encoding < VTDNav.FORMAT_UTF_16BE) ? 2 : 4;
              for (int i = 0; i < size; i++)
              {
                  os1 = flb.lower32At(i);
                  os2 = flb.lower32At(i) + (flb.upper32At(i) & 0x1fffffff) - 1;
                  if (i + 1 < size)
                  {
  
gt:                     temp = flb.lower32At(i + 1);