context: /* ====================================================================
     Licensed to the Apache Software Foundation (ASF) under one or more
     contributor license agreements.  See the NOTICE file distributed with
     this work for Additional information regarding copyright ownership.
     The ASF licenses this file to You under the Apache License, Version 2.0
     (the "License"); you may not use this file except in compliance with
     the License.  You may obtain a copy of the License at
  
         http://www.apache.org/licenses/LICENSE-2.0
  
     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
  ==================================================================== */
  
  namespace AL.Utils.NPOI.HSSF.Record
  {
      using System;
      using System.Collections.Generic;
      using System.Text;
      using AL.Utils.NPOI.HSSF.Record.Cont;
      using AL.Utils.NPOI.Util;
  
      /**
       * Title: Unicode String<p/>
       * Description:  Unicode String - just standard fields that are in several records.
       *               It is considered more desirable then repeating it in all of them.<p/>
       *               This is often called a XLUnicodeRichExtendedString in MS documentation.<p/>
       * REFERENCE:  PG 264 Microsoft Excel 97 Developer's Kit (ISBN: 1-57231-498-2)<p/>
       * REFERENCE:  PG 951 Excel Binary File Format (.xls) Structure Specification v20091214 
       */
      public class UnicodeString : IComparable<UnicodeString>
      { // TODO - make this when the compatibility version is Removed
          private static POILogger _logger = POILogFactory.GetLogger(typeof(UnicodeString));
  
          private short field_1_charCount;
          private byte field_2_optionflags;
          private String field_3_string;
          private List<FormatRun> field_4_format_Runs;
          private ExtRst field_5_ext_rst;
          private static BitField highByte = BitFieldFactory.GetInstance(0x1);
          // 0x2 is reserved
          private static BitField extBit = BitFieldFactory.GetInstance(0x4);
          private static BitField richText = BitFieldFactory.GetInstance(0x8);
  
          public class FormatRun : IComparable<FormatRun>
          {
              internal short _character;
              internal short _fontIndex;
  
              public FormatRun(short character, short fontIndex)
              {
                  this._character = character;
                  this._fontIndex = fontIndex;
              }
  
              public FormatRun(ILittleEndianInput in1) :
                  this(in1.ReadShort(), in1.ReadShort())
              {
              }
  
              public short CharacterPos
              {
                  get
                  {
                      return _character;
                  }
              }
  
              public short FontIndex
              {
                  get
                  {
                      return _fontIndex;
                  }
              }
  
              public override bool Equals(Object o)
              {
                  if (!(o is FormatRun))
                  {
                      return false;
                  }
                  FormatRun other = (FormatRun)o;
  
                  return _character == other._character && _fontIndex == other._fontIndex;
              }
              public override int GetHashCode()
              {
                  return base.GetHashCode();
              }
              public int CompareTo(FormatRun r)
              {
                  if (_character == r._character && _fontIndex == r._fontIndex)
                  {
                      return 0;
                  }
                  if (_character == r._character)
                  {
                      return _fontIndex - r._fontIndex;
                  }
                  return _character - r._character;
              }
  
              public override String ToString()
              {
                  return "character=" + _character + ",fontIndex=" + _fontIndex;
              }
  
              public void Serialize(ILittleEndianOutput out1)
              {
                  out1.WriteShort(_character);
                  out1.WriteShort(_fontIndex);
              }
          }
  
          // See page 681
          public class ExtRst : IComparable<ExtRst>
          {
              private short reserved;
  
              // This is a Phs (see page 881)
              private short formattingFontIndex;
              private short formattingOptions;
  
              // This is a RPHSSub (see page 894)
              private int numberOfRuns;
              private String phoneticText;
  
              // This is an array of PhRuns (see page 881)
              private PhRun[] phRuns;
              // Sometimes there's some cruft at the end
              private byte[] extraData;
  
              private void populateEmpty()
              {
                  reserved = 1;
                  phoneticText = "";
                  phRuns = new PhRun[0];
                  extraData = new byte[0];
              }
              public override int GetHashCode()
              {
                  return base.GetHashCode();
              }
              internal ExtRst()
              {
                  populateEmpty();
              }
              internal ExtRst(ILittleEndianInput in1, int expectedLength)
              {
                  reserved = in1.ReadShort();
  
                  // Old style detection (Reserved = 0xFF)
                  if (reserved == -1)
                  {
                      populateEmpty();
                      return;
                  }
  
                  // Spot corrupt records
                  if (reserved != 1)
                  {
                      _logger.Log(POILogger.WARN, "Warning - ExtRst has wrong magic marker, expecting 1 but found " + reserved + " - ignoring");
                      // Grab all the remaining data, and ignore it
                      for (int i = 0; i < expectedLength - 2; i++)
                      {
                          in1.ReadByte();
                      }
                      // And make us be empty
                      populateEmpty();
                      return;
                  }
  
                  // Carry on Reading in as normal
                  short stringDataSize = in1.ReadShort();
  
                  formattingFontIndex = in1.ReadShort();
                  formattingOptions = in1.ReadShort();
  
                  // RPHSSub
                  numberOfRuns = in1.ReadUShort();
                  short length1 = in1.ReadShort();
                  // No really. Someone Clearly forgot to read
                  //  the docs on their datastructure...
                  short length2 = in1.ReadShort();
                  // And sometimes they write out garbage :(
                  if (length1 == 0 && length2 > 0)
                  {
                      length2 = 0;
                  }
                  if (length1 != length2)
                  {
                      throw new InvalidOperationException(
                            "The two length fields of the Phonetic Text don't agree! " +
                            length1 + " vs " + length2
                      );
                  }
                  phoneticText = StringUtil.ReadUnicodeLE(in1, length1);
  
                  int RunData = stringDataSize - 4 - 6 - (2 * phoneticText.Length);
                  int numRuns = (RunData / 6);
                  phRuns = new PhRun[numRuns];
                  for (int i = 0; i < phRuns.Length; i++)
                  {
                      phRuns[i] = new PhRun(in1);
                  }
  
                  int extraDataLength = RunData - (numRuns * 6);
                  if (extraDataLength < 0)
                  {
                      //System.err.Println("Warning - ExtRst overran by " + (0-extraDataLength) + " bytes");
                      extraDataLength = 0;
                  }
                  extraData = new byte[extraDataLength];
                  for (int i = 0; i < extraData.Length; i++)
                  {
                      extraData[i] = (byte)in1.ReadByte();
                  }
              }
              /**
               * Returns our size, excluding our 
               *  4 byte header
               */
              internal int DataSize
              {
                  get
                  {
                      return 4 + 6 + (2 * phoneticText.Length) +
                         (6 * phRuns.Length) + extraData.Length;
                  }
              }
              internal void Serialize(ContinuableRecordOutput out1)
              {
                  int dataSize = DataSize;
  
                  out1.WriteContinueIfRequired(8);
                  out1.WriteShort(reserved);
                  out1.WriteShort(dataSize);
                  out1.WriteShort(formattingFontIndex);
                  out1.WriteShort(formattingOptions);
  
                  out1.WriteContinueIfRequired(6);
                  out1.WriteShort(numberOfRuns);
                  out1.WriteShort(phoneticText.Length);
                  out1.WriteShort(phoneticText.Length);
  
                  out1.WriteContinueIfRequired(phoneticText.Length * 2);
                  StringUtil.PutUnicodeLE(phoneticText, out1);
  
                  for (int i = 0; i < phRuns.Length; i++)
                  {
                      phRuns[i].Serialize(out1);
                  }
  
                  out1.Write(extraData);
              }
  
              public override bool Equals(Object obj)
              {
                  if (!(obj is ExtRst))
                  {
                      return false;
                  }
                  ExtRst other = (ExtRst)obj;
                  return (CompareTo(other) == 0);
              }
              public int CompareTo(ExtRst o)
              {
                  int result;
  
                  result = reserved - o.reserved;
                  if (result != 0) return result;
                  result = formattingFontIndex - o.formattingFontIndex;
                  if (result != 0) return result;
                  result = formattingOptions - o.formattingOptions;
                  if (result != 0) return result;
                  result = numberOfRuns - o.numberOfRuns;
                  if (result != 0) return result;
  
                  //result = phoneticText.CompareTo(o.phoneticText);
                  result = string.Compare(phoneticText, o.phoneticText, StringComparison.CurrentCulture);
                  if (result != 0) return result;
  
                  result = phRuns.Length - o.phRuns.Length;
                  if (result != 0) return result;
                  for (int i = 0; i < phRuns.Length; i++)
                  {
                      result = phRuns[i].phoneticTextFirstCharacterOffset - o.phRuns[i].phoneticTextFirstCharacterOffset;
                      if (result != 0) return result;
                      result = phRuns[i].realTextFirstCharacterOffset - o.phRuns[i].realTextFirstCharacterOffset;
                      if (result != 0) return result;
                      result = phRuns[i].realTextFirstCharacterOffset - o.phRuns[i].realTextLength;
                      if (result != 0) return result;
                  }
  
                  result = extraData.Length - o.extraData.Length;
                  if (result != 0) return result;
  
                  // If we Get here, it's the same
                  return 0;
              }
  
              internal ExtRst Clone()
              {
  
gt:                 ExtRst ext = new ExtRst();