protected int getDataSize() {
    if (!isExternalReferences()) {
      return SMALL_RECORD_SIZE;
    }
    int sum = 2; // u16 number of sheets

    sum += StringUtil.getEncodedSize(field_2_encoded_url);

    for (int i = 0; i < field_3_sheet_names.length; i++) {
      sum += StringUtil.getEncodedSize(field_3_sheet_names[i]);
    }
    return sum;
  }
  public void serialize(LittleEndianOutput out) {
    out.writeShort(field_1_number_of_sheets);

    if (isExternalReferences()) {
      StringUtil.writeUnicodeString(out, field_2_encoded_url);

      for (int i = 0; i < field_3_sheet_names.length; i++) {
        StringUtil.writeUnicodeString(out, field_3_sheet_names[i]);
      }
    } else {
      int field2val = _isAddInFunctions ? TAG_ADD_IN_FUNCTIONS : TAG_INTERNAL_REFERENCES;

      out.writeShort(field2val);
    }
  }
  private static void encodeSingleValue(LittleEndianOutput out, Object value) {
    if (value == EMPTY_REPRESENTATION) {
      out.writeByte(TYPE_EMPTY);
      out.writeLong(0L);
      return;
    }
    if (value instanceof Boolean) {
      Boolean bVal = ((Boolean) value);
      out.writeByte(TYPE_BOOLEAN);
      long longVal = bVal.booleanValue() ? 1L : 0L;
      out.writeLong(longVal);
      return;
    }
    if (value instanceof Double) {
      Double dVal = (Double) value;
      out.writeByte(TYPE_NUMBER);
      out.writeDouble(dVal.doubleValue());
      return;
    }
    if (value instanceof String) {
      String val = (String) value;
      out.writeByte(TYPE_STRING);
      StringUtil.writeUnicodeString(out, val);
      return;
    }
    if (value instanceof ErrorConstant) {
      ErrorConstant ecVal = (ErrorConstant) value;
      out.writeByte(TYPE_ERROR_CODE);
      long longVal = ecVal.getErrorCode();
      out.writeLong(longVal);
      return;
    }

    throw new IllegalStateException("Unexpected value type (" + value.getClass().getName() + "'");
  }
  /** @return encoded size without the 'type' code byte */
  private static int getEncodedSize(Object object) {
    if (object == EMPTY_REPRESENTATION) {
      return 8;
    }
    Class cls = object.getClass();

    if (cls == Boolean.class || cls == Double.class || cls == ErrorConstant.class) {
      return 8;
    }
    String strVal = (String) object;
    return StringUtil.getEncodedSize(strVal);
  }
  @Override
  protected void serialize(LittleEndianOutput out) {

    out.writeInt(_grbit1);
    out.writeByte(_grbit2);
    out.writeByte(_citmShow);
    out.writeShort(_isxdiSort);
    out.writeShort(_isxdiShow);

    if (_subtotalName == null) {
      out.writeShort(STRING_NOT_PRESENT_LEN);
    } else {
      out.writeShort(_subtotalName.length());
    }

    out.writeInt(_reserved1);
    out.writeInt(_reserved2);
    if (_subtotalName != null) {
      StringUtil.putUnicodeLE(_subtotalName, out);
    }
  }
 private static Object readAConstantValue(LittleEndianInput in) {
   byte grbit = in.readByte();
   switch (grbit) {
     case TYPE_EMPTY:
       in.readLong(); // 8 byte 'not used' field
       return EMPTY_REPRESENTATION;
     case TYPE_NUMBER:
       return new Double(in.readDouble());
     case TYPE_STRING:
       return StringUtil.readUnicodeString(in);
     case TYPE_BOOLEAN:
       return readBoolean(in);
     case TYPE_ERROR_CODE:
       int errCode = in.readUShort();
       // next 6 bytes are unused
       in.readUShort();
       in.readInt();
       return ErrorConstant.valueOf(errCode);
   }
   throw new RuntimeException("Unknown grbit value (" + grbit + ")");
 }
    protected void serialize(ContinuableRecordOutput out) {
      int dataSize = getDataSize();

      out.writeContinueIfRequired(8);
      out.writeShort(reserved);
      out.writeShort(dataSize);
      out.writeShort(formattingFontIndex);
      out.writeShort(formattingOptions);

      out.writeContinueIfRequired(6);
      out.writeShort(numberOfRuns);
      out.writeShort(phoneticText.length());
      out.writeShort(phoneticText.length());

      out.writeContinueIfRequired(phoneticText.length() * 2);
      StringUtil.putUnicodeLE(phoneticText, out);

      for (int i = 0; i < phRuns.length; i++) {
        phRuns[i].serialize(out);
      }

      out.write(extraData);
    }
    protected ExtRst(LittleEndianInput in, int expectedLength) {
      reserved = in.readShort();

      // Old style detection (Reserved = 0xFF)
      if (reserved == -1) {
        populateEmpty();
        return;
      }

      // Spot corrupt records
      if (reserved != 1) {
        System.err.println(
            "Warning - ExtRst was 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++) {
          in.readByte();
        }
        // And make us be empty
        populateEmpty();
        return;
      }

      // Carry on reading in as normal
      short stringDataSize = in.readShort();

      formattingFontIndex = in.readShort();
      formattingOptions = in.readShort();

      // RPHSSub
      numberOfRuns = in.readUShort();
      short length1 = in.readShort();
      // No really. Someone clearly forgot to read
      //  the docs on their datastructure...
      short length2 = in.readShort();
      // And sometimes they write out garbage :(
      if (length1 == 0 && length2 > 0) {
        length2 = 0;
      }
      if (length1 != length2) {
        throw new IllegalStateException(
            "The two length fields of the Phonetic Text don't agree! "
                + length1
                + " vs "
                + length2);
      }
      phoneticText = StringUtil.readUnicodeLE(in, 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(in);
      }

      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] = in.readByte();
      }
    }