/**
   * Serialises out the String. There are special rules about where we can and can't split onto
   * Continue records.
   */
  public void serialize(ContinuableRecordOutput out) {
    int numberOfRichTextRuns = 0;
    int extendedDataSize = 0;
    if (isRichText() && field_4_format_runs != null) {
      numberOfRichTextRuns = field_4_format_runs.size();
    }
    if (isExtendedText() && field_5_ext_rst != null) {
      extendedDataSize = 4 + field_5_ext_rst.getDataSize();
    }

    // Serialise the bulk of the String
    // The writeString handles tricky continue stuff for us
    out.writeString(field_3_string, numberOfRichTextRuns, extendedDataSize);

    if (numberOfRichTextRuns > 0) {

      // This will ensure that a run does not split a continue
      for (int i = 0; i < numberOfRichTextRuns; i++) {
        if (out.getAvailableSpace() < 4) {
          out.writeContinue();
        }
        FormatRun r = field_4_format_runs.get(i);
        r.serialize(out);
      }
    }

    if (extendedDataSize > 0) {
      field_5_ext_rst.serialize(out);
    }
  }
 /**
  * Swaps all use in the string of one font index for use of a different font index. Normally only
  * called when fonts have been removed / re-ordered
  */
 public void swapFontUse(short oldFontIndex, short newFontIndex) {
   for (FormatRun run : field_4_format_runs) {
     if (run._fontIndex == oldFontIndex) {
       run._fontIndex = newFontIndex;
     }
   }
 }
  /**
   * return a character representation of the fields of this record
   *
   * @return String of output for biffviewer etc.
   */
  public String getDebugInfo() {
    StringBuffer buffer = new StringBuffer();

    buffer.append("[UNICODESTRING]\n");
    buffer
        .append("    .charcount       = ")
        .append(Integer.toHexString(getCharCount()))
        .append("\n");
    buffer
        .append("    .optionflags     = ")
        .append(Integer.toHexString(getOptionFlags()))
        .append("\n");
    buffer.append("    .string          = ").append(getString()).append("\n");
    if (field_4_format_runs != null) {
      for (int i = 0; i < field_4_format_runs.size(); i++) {
        FormatRun r = field_4_format_runs.get(i);
        buffer.append("      .format_run" + i + "          = ").append(r.toString()).append("\n");
      }
    }
    if (field_5_ext_rst != null) {
      buffer.append("    .field_5_ext_rst          = ").append("\n");
      buffer.append(field_5_ext_rst.toString()).append("\n");
    }
    buffer.append("[/UNICODESTRING]\n");
    return buffer.toString();
  }
  /**
   * Our handling of equals is inconsistent with compareTo. The trouble is because we don't truely
   * understand rich text fields yet it's difficult to make a sound comparison.
   *
   * @param o The object to compare.
   * @return true if the object is actually equal.
   */
  public boolean equals(Object o) {
    if (!(o instanceof UnicodeString)) {
      return false;
    }
    UnicodeString other = (UnicodeString) o;

    // OK lets do this in stages to return a quickly, first check the actual string
    boolean eq =
        ((field_1_charCount == other.field_1_charCount)
            && (field_2_optionflags == other.field_2_optionflags)
            && field_3_string.equals(other.field_3_string));
    if (!eq) return false;

    // OK string appears to be equal but now lets compare formatting runs
    if ((field_4_format_runs == null) && (other.field_4_format_runs == null))
      // Strings are equal, and there are not formatting runs.
      return true;
    if (((field_4_format_runs == null) && (other.field_4_format_runs != null))
        || (field_4_format_runs != null) && (other.field_4_format_runs == null))
      // Strings are equal, but one or the other has formatting runs
      return false;

    // Strings are equal, so now compare formatting runs.
    int size = field_4_format_runs.size();
    if (size != other.field_4_format_runs.size()) return false;

    for (int i = 0; i < size; i++) {
      FormatRun run1 = field_4_format_runs.get(i);
      FormatRun run2 = other.field_4_format_runs.get(i);

      if (!run1.equals(run2)) return false;
    }

    // Well the format runs are equal as well!, better check the ExtRst data
    if (field_5_ext_rst == null && other.field_5_ext_rst == null) {
      // Good
    } else if (field_5_ext_rst != null && other.field_5_ext_rst != null) {
      int extCmp = field_5_ext_rst.compareTo(other.field_5_ext_rst);
      if (extCmp == 0) {
        // Good
      } else {
        return false;
      }
    } else {
      return false;
    }

    // Phew!! After all of that we have finally worked out that the strings
    // are identical.
    return true;
  }
  public int compareTo(UnicodeString str) {

    int result = getString().compareTo(str.getString());

    // As per the equals method lets do this in stages
    if (result != 0) return result;

    // OK string appears to be equal but now lets compare formatting runs
    if ((field_4_format_runs == null) && (str.field_4_format_runs == null))
      // Strings are equal, and there are no formatting runs.
      return 0;

    if ((field_4_format_runs == null) && (str.field_4_format_runs != null))
      // Strings are equal, but one or the other has formatting runs
      return 1;
    if ((field_4_format_runs != null) && (str.field_4_format_runs == null))
      // Strings are equal, but one or the other has formatting runs
      return -1;

    // Strings are equal, so now compare formatting runs.
    int size = field_4_format_runs.size();
    if (size != str.field_4_format_runs.size()) return size - str.field_4_format_runs.size();

    for (int i = 0; i < size; i++) {
      FormatRun run1 = field_4_format_runs.get(i);
      FormatRun run2 = str.field_4_format_runs.get(i);

      result = run1.compareTo(run2);
      if (result != 0) return result;
    }

    // Well the format runs are equal as well!, better check the ExtRst data
    if ((field_5_ext_rst == null) && (str.field_5_ext_rst == null)) return 0;
    if ((field_5_ext_rst == null) && (str.field_5_ext_rst != null)) return 1;
    if ((field_5_ext_rst != null) && (str.field_5_ext_rst == null)) return -1;

    result = field_5_ext_rst.compareTo(str.field_5_ext_rst);
    if (result != 0) return result;

    // Phew!! After all of that we have finally worked out that the strings
    // are identical.
    return 0;
  }