/** parse data and print it in a table-nested format to the provided PrintStream */
  public static final void parseData(OMMData data, PrintStream ps, int tabLevel) {
    if (data.isBlank()) dumpBlank(ps);
    else if (OMMTypes.isAggregate(data.getType())) parseAggregate(data, ps, tabLevel + 1);
    else if ((data.getType() == OMMTypes.RMTES_STRING)
        && ((OMMDataBuffer) data).hasPartialUpdates()) {
      Iterator iter = ((OMMDataBuffer) data).partialUpdateIterator();
      while (true) {
        OMMDataBuffer partial = (OMMDataBuffer) iter.next();
        ps.print("hpos: ");
        ps.print(partial.horizontalPosition());
        ps.print(", ");
        ps.print(partial.toString());
        if (iter.hasNext()) ps.print("  |  ");
        else break;
      }
      ps.println();
    } else if (data.getType() == OMMTypes.ANSI_PAGE) {
      // process ANSI with com.reuters.rfa.ansipage
      parseAnsiPageData(data, ps, tabLevel);
    } else if (data.getType() == OMMTypes.BUFFER || data.getType() == OMMTypes.OPAQUE_BUFFER) {
      if (data.getEncodedLength() <= 20) {
        dumpIndent(ps, tabLevel + 1);
        // for small strings, print hex and try to print ASCII
        ps.print(HexDump.toHexString(((OMMDataBuffer) data).getBytes(), false));
        ps.print(" | ");
        ps.println(data);
      } else {
        if (INTERNAL_DEBUG) {
          ps.println("Hex Format and Data Bytes: ");
          ps.println(HexDump.hexDump(((OMMDataBuffer) data).getBytes(), 50));

          ps.println("Hex Format: ");
        }

        int lineSize = 32;
        String s = HexDump.toHexString(((OMMDataBuffer) data).getBytes(), false);

        int j = 0;
        while (j < s.length()) {
          if (j != 0) ps.println();

          dumpIndent(ps, 1);

          int end = j + lineSize;
          if (end >= s.length()) end = s.length();

          for (int i = j; i < end; i++) {
            ps.print(s.charAt(i));
          }
          j = j + lineSize;
        }

        ps.println("\nData Bytes: ");
        dumpIndent(ps, 1);
        ps.println(data);
      }
    } else if (data.getType() == OMMTypes.MSG) {
      parseMsg((OMMMsg) data, ps, tabLevel + 1);
    } else {
      try {
        ps.println(data);
      } catch (Exception e) {
        byte[] rawdata = data.getBytes();
        ps.println(HexDump.hexDump(rawdata));
      }
    }
  }
 private static final void parseEntry(OMMEntry entry, PrintStream ps, int tabLevel) {
   try {
     switch (entry.getType()) {
       case OMMTypes.FIELD_ENTRY:
         {
           OMMFieldEntry fe = (OMMFieldEntry) entry;
           if (CURRENT_DICTIONARY != null) {
             FidDef fiddef = CURRENT_DICTIONARY.getFidDef(fe.getFieldId());
             if (fiddef != null) {
               dumpFieldEntryHeader(fe, fiddef, ps, tabLevel);
               OMMData data = null;
               if (fe.getDataType() == OMMTypes.UNKNOWN) data = fe.getData(fiddef.getOMMType());
               else
                 // defined data already has type
                 data = fe.getData();
               if (data.getType() == OMMTypes.ENUM) {
                 ps.print(
                     CURRENT_DICTIONARY.expandedValueFor(
                         fiddef.getFieldId(), ((OMMEnum) data).getValue()));
                 ps.print(" (");
                 ps.print(data);
                 ps.println(")");
               } else parseData(data, ps, tabLevel);
             } else {
               ps.println(
                   "Received field id: " + fe.getFieldId() + " - Not defined in dictionary");
             }
           } else {
             dumpFieldEntryHeader(fe, null, ps, tabLevel);
             if (fe.getDataType() == OMMTypes.UNKNOWN) {
               OMMDataBuffer data = (OMMDataBuffer) fe.getData();
               ps.println(HexDump.toHexString(data.getBytes(), false));
             } else
             // defined data already has type
             {
               OMMData data = fe.getData();
               parseData(data, ps, tabLevel);
             }
           }
           ps.flush();
         }
         break;
       case OMMTypes.ELEMENT_ENTRY:
         dumpElementEntryHeader((OMMElementEntry) entry, ps, tabLevel);
         parseData(entry.getData(), ps, tabLevel);
         break;
       case OMMTypes.MAP_ENTRY:
         dumpMapEntryHeader((OMMMapEntry) entry, ps, tabLevel);
         if ((((OMMMapEntry) entry).getAction() != OMMMapEntry.Action.DELETE)
             && entry.getDataType() != OMMTypes.NO_DATA) parseData(entry.getData(), ps, tabLevel);
         break;
       case OMMTypes.VECTOR_ENTRY:
         dumpVectorEntryHeader((OMMVectorEntry) entry, ps, tabLevel);
         if ((((OMMVectorEntry) entry).getAction() != OMMVectorEntry.Action.DELETE)
             && (((OMMVectorEntry) entry).getAction() != OMMVectorEntry.Action.CLEAR))
           parseData(entry.getData(), ps, tabLevel);
         break;
       case OMMTypes.FILTER_ENTRY:
         dumpFilterEntryHeader((OMMFilterEntry) entry, ps, tabLevel);
         if (((OMMFilterEntry) entry).getAction() != OMMFilterEntry.Action.CLEAR)
           parseData(entry.getData(), ps, tabLevel);
         break;
       default:
         dumpEntryHeader(entry, ps, tabLevel);
         parseData(entry.getData(), ps, tabLevel);
         break;
     }
   } catch (OMMException e) {
     ps.println("ERROR Invalid data: " + e.getMessage());
   }
 }
  static final void parseMsg(OMMMsg msg, PrintStream ps, int tabLevel) {
    msg.getMsgType();
    dumpIndent(ps, tabLevel);
    ps.println("MESSAGE");
    dumpIndent(ps, tabLevel + 1);
    ps.println("Msg Type: " + OMMMsg.MsgType.toString(msg.getMsgType()));
    dumpIndent(ps, tabLevel + 1);
    ps.println("Msg Model Type: " + RDMMsgTypes.toString(msg.getMsgModelType()));
    dumpIndent(ps, tabLevel + 1);
    ps.println("Indication Flags: " + OMMMsg.Indication.indicationString(msg));

    dumpIndent(ps, tabLevel + 1);
    ps.println("Hint Flags: " + hintString(msg));

    if ((msg.getDataType() == OMMTypes.ANSI_PAGE) && msg.isSet(OMMMsg.Indication.CLEAR_CACHE)) {
      CURRENT_PAGE = null;
    }

    if (msg.has(OMMMsg.HAS_STATE)) {
      dumpIndent(ps, tabLevel + 1);
      ps.println("State: " + msg.getState());
    }
    if (msg.has(OMMMsg.HAS_PRIORITY)) {
      dumpIndent(ps, tabLevel + 1);
      OMMPriority p = msg.getPriority();
      if (p != null) ps.println("Priority: " + p.getPriorityClass() + "," + p.getCount());
      else ps.println("Priority: Error flag recieved but there is not priority present");
    }
    if (msg.has(OMMMsg.HAS_QOS)) {
      dumpIndent(ps, tabLevel + 1);
      ps.println("Qos: " + msg.getQos());
    }
    if (msg.has(OMMMsg.HAS_QOS_REQ)) {
      dumpIndent(ps, tabLevel + 1);
      ps.println("QosReq: " + msg.getQosReq());
    }
    if (msg.has(OMMMsg.HAS_ITEM_GROUP)) {
      dumpIndent(ps, tabLevel + 1);
      ps.println("Group: " + msg.getItemGroup());
    }
    if (msg.has(OMMMsg.HAS_PERMISSION_DATA)) {
      byte[] permdata = msg.getPermissionData();

      dumpIndent(ps, tabLevel + 1);
      ps.print("PermissionData: " + HexDump.toHexString(permdata, false));
      ps.println(" ( " + HexDump.formatHexString(permdata) + " ) ");
    }
    if (msg.has(OMMMsg.HAS_SEQ_NUM)) {
      dumpIndent(ps, tabLevel + 1);
      ps.println("SeqNum: " + msg.getSeqNum());
    }

    if (msg.has(OMMMsg.HAS_CONFLATION_INFO)) {
      dumpIndent(ps, tabLevel + 1);
      ps.println("Conflation Count: " + msg.getConflationCount());
      dumpIndent(ps, tabLevel + 1);
      ps.println("Conflation Time: " + msg.getConflationTime());
    }

    if (msg.has(OMMMsg.HAS_RESP_TYPE_NUM)) {
      dumpIndent(ps, tabLevel + 1);
      ps.print("RespTypeNum: " + msg.getRespTypeNum());
      dumpRespTypeNum(msg, ps);
    }

    if (msg.has(OMMMsg.HAS_ID)) {
      dumpIndent(ps, tabLevel + 1);
      ps.println("Id: " + msg.getId());
    }

    if ((msg.has(OMMMsg.HAS_PUBLISHER_INFO)) || (msg.getMsgType() == OMMMsg.MsgType.POST)) {
      PublisherPrincipalIdentity pi = (PublisherPrincipalIdentity) msg.getPrincipalIdentity();
      if (pi != null) {
        dumpIndent(ps, tabLevel + 1);
        ps.println("Publisher Address: 0x" + Long.toHexString(pi.getPublisherAddress()));
        dumpIndent(ps, tabLevel + 1);
        ps.println("Publisher Id: " + pi.getPublisherId());
      }
    }

    if (msg.has(OMMMsg.HAS_USER_RIGHTS)) {
      dumpIndent(ps, tabLevel + 1);
      ps.println(
          "User Rights Mask: " + OMMMsg.UserRights.userRightsString(msg.getUserRightsMask()));
    }

    if (msg.has(OMMMsg.HAS_ATTRIB_INFO)) {
      dumpIndent(ps, tabLevel + 1);
      ps.println("AttribInfo");
      OMMAttribInfo ai = msg.getAttribInfo();
      if (ai.has(OMMAttribInfo.HAS_SERVICE_NAME)) {
        dumpIndent(ps, tabLevel + 2);
        ps.println("ServiceName: " + ai.getServiceName());
      }
      if (ai.has(OMMAttribInfo.HAS_SERVICE_ID)) {
        dumpIndent(ps, tabLevel + 2);
        ps.println("ServiceId: " + ai.getServiceID());
      }
      if (ai.has(OMMAttribInfo.HAS_NAME)) {
        dumpIndent(ps, tabLevel + 2);
        ps.println("Name: " + ai.getName());
      }
      if (ai.has(OMMAttribInfo.HAS_NAME_TYPE)) {
        dumpIndent(ps, tabLevel + 2);
        ps.print("NameType: " + ai.getNameType());
        if (msg.getMsgModelType() == RDMMsgTypes.LOGIN) {
          ps.println(" (" + RDMUser.NameType.toString(ai.getNameType()) + ")");
        } else if (RDMInstrument.isInstrumentMsgModelType(msg.getMsgModelType())) {

          ps.println(" (" + RDMInstrument.NameType.toString(ai.getNameType()) + ")");
        } else {
          ps.println();
        }
      }
      if (ai.has(OMMAttribInfo.HAS_FILTER)) {
        dumpIndent(ps, tabLevel + 2);
        ps.print("Filter: " + ai.getFilter());
        if (msg.getMsgModelType() == RDMMsgTypes.DIRECTORY) {
          ps.println(" (" + RDMService.Filter.toString(ai.getFilter()) + ")");
        } else if (msg.getMsgModelType() == RDMMsgTypes.DICTIONARY) {
          ps.println(" (" + RDMDictionary.Filter.toString(ai.getFilter()) + ")");
        } else {
          ps.println();
        }
      }
      if (ai.has(OMMAttribInfo.HAS_ID)) {
        dumpIndent(ps, tabLevel + 2);
        ps.println("ID: " + ai.getId());
      }
      if (ai.has(OMMAttribInfo.HAS_ATTRIB)) {
        dumpIndent(ps, tabLevel + 2);
        ps.println("Attrib");
        parseData(ai.getAttrib(), ps, tabLevel + 2);
      }
    }

    dumpIndent(ps, tabLevel + 1);
    ps.print("Payload: ");
    if (msg.getDataType() != OMMTypes.NO_DATA) {
      ps.println(msg.getPayload().getEncodedLength() + " bytes");
      parseData(msg.getPayload(), ps, tabLevel + 1);
    } else {
      ps.println("None");
    }
  }