/**
  * Creates a plain text representation of a collection.
  *
  * @param field The collection to create the plain text representation of.
  * @param indent The indentation to draw.
  * @param type The complete type.
  */
 private void drawCollection(MetaCollection field, String indent, MetaType type) {
   String typedefName = type.getFieldTypedefName(field);
   if (typedefName != null) {
     writer.write(indent + typedefName + " " + field.getName() + ";" + newLine + separator);
   } else {
     writer.write(
         indent + field.getTypeName() + " " + field.getName() + ";" + newLine + separator);
   }
 }
  private void serializeRecursiveType(
      StringWriter writer, String nestedFieldName, MetaCollection seqType, UserData data) {
    // String value = data.getFieldValue(nestedFieldName);
    String typeName = seqType.getTypeName();

    if (!((typeName.startsWith("C_ARRAY")))) {
      writer.write("<NULL>");
    }
    return;
  }
  private void serializeCollection(
      StringWriter writer, String nestedFieldName, MetaCollection field, UserData data) {
    int size, maxSize;
    String typeName = field.getTypeName();

    if ((typeName.equals("c_string"))
        || (typeName.equals("c_wstring"))
        || (typeName.startsWith("C_STRING<"))
        || (typeName.startsWith("C_WSTRING<"))) {
      this.serializeString(writer, nestedFieldName, data);
    } else { // Not a String collection.
      maxSize = field.getMaxSize();

      if (maxSize == 0) {
        this.serializeUnboundedSequence(writer, nestedFieldName, field, data);
      } else if (maxSize == -1) {
          /* recursive type */
        this.serializeRecursiveType(writer, nestedFieldName, field, data);
      } else {
        /* This is an array or bounded sequence. */
        if (typeName.startsWith("C_ARRAY")) {
          /* When dealing with an array, then the actual size
           * is the same as the maximum size. */
          size = maxSize;
        } else {
          /* When dealing with a bounded sequence, then the actual
           * size can differ from the maximum size. */
          size = getSequenceSize(nestedFieldName, field, data);
          /* Also, we need this size within the data. */
          writer.write("<size>" + size + "</size>");
        }
        /* Now, fill up the elements. */
        for (int i = 0; i < size; i++) {
          writer.write("<element>");
          this.serializeType(
              writer, getCollectionFieldName(nestedFieldName, i), field.getSubType(), data, false);
          writer.write("</element>");
        }
      }
    }
  }
  /**
   * Serializes an unbounded sequence.
   *
   * @param nestedFieldName The name of the field (including scope)
   * @param seqType The type of the sequence.
   * @param data The data to serialize.
   */
  private void serializeUnboundedSequence(
      StringWriter writer, String nestedFieldName, MetaCollection seqType, UserData data) {
    String value;
    String typeName;
    StringTokenizer tokenizer;
    MetaField subType = seqType.getSubType();
    String token;
    String seqTypeName;

    seqTypeName = seqType.getTypeName();

    if (subType instanceof MetaPrimitive) {
      value = data.getFieldValue(nestedFieldName);

      if (value != null) {
        if (value.equals("NULL")) {
          writer.write("&lt;NULL&gt;");
        } else if (value.equals("[]")) {
          writer.write("<size>0</size>");
        } else {
          tokenizer = new StringTokenizer(value.substring(1, value.length() - 1), ",");
          int size = tokenizer.countTokens();

          writer.write("<size>" + size + "</size>");

          for (int i = 0; i < size; i++) {
            token = tokenizer.nextToken();
            writer.write("<element>" + token + "</element>");
          }
        }
      } else {
        // also check for the alternative notation
        value = data.getFieldValue(nestedFieldName + "[0]");
        if (value != null) {
          List<String> values = new ArrayList<String>();
          int index = 1;
          while (value != null) {
            values.add(value);
            value = data.getFieldValue(nestedFieldName + "[" + index + "]");
            index++;
          }
          writer.write("<size>" + values.size() + "</size>");
          Iterator<String> it = values.iterator();
          while (it.hasNext()) {
            writer.write("<element>" + it.next() + "</element>");
          }
        } else {
          writer.write("&lt;NULL&gt;");
        }
      }
    } else if (subType instanceof MetaCollection) {
      typeName = subType.getTypeName();

      if ((typeName.equals("c_string"))
          || (typeName.equals("c_wstring"))
          || (typeName.startsWith("C_STRING<"))
          || (typeName.startsWith("C_WSTRING<"))) {
        value = data.getFieldValue(nestedFieldName);

        if (value != null) {
          if (value.equals("NULL")) {
            writer.write("&lt;NULL&gt;");
          } else if (value.equals("[]")) {
            if (!(seqTypeName.startsWith("C_ARRAY"))) {
              writer.write("<size>0</size>");
            }
          } else {
            tokenizer = new StringTokenizer(value.substring(1, value.length() - 1), ",");
            int size = tokenizer.countTokens();

            if (!(seqTypeName.startsWith("C_ARRAY"))) {
              writer.write("<size>" + size + "</size>");
            }
            for (int i = 0; i < size; i++) {
              token = tokenizer.nextToken();
              writer.write("<element>" + token + "</element>");
            }
          }
        } else {
          writer.write("&lt;NULL&gt;");
        }
      }
    } else if (subType instanceof MetaStruct) {
      int size = 0;
      StringWriter localWriter = new StringWriter();
      // Verify if member "size" exists, if so, then serialize and
      // increment
      LinkedHashSet<String> fields = data.getFieldNames();
      boolean found = true;
      while (found) {
        String entryFieldName = nestedFieldName + "[" + size + "]";
        boolean fieldExists = false;
        Iterator<String> it = fields.iterator();
        while (!fieldExists && it.hasNext()) {
          if (it.next().startsWith(entryFieldName)) {
            fieldExists = true;
          }
        }
        if (fieldExists) {
          localWriter.write("<element>");
          serializeStruct(localWriter, entryFieldName, (MetaStruct) subType, data);
          localWriter.write("</element>");
          size++;
        } else {
          found = false;
        }
      }
      if (size == 0) {
        writer.write("&lt;NULL&gt;");
      } else {
        writer.write("<size>" + size + "</size>");
      }
      writer.write(localWriter.toString());
    } else if (subType instanceof MetaUnion) {
      int size = 0;
      StringWriter localWriter = new StringWriter();
      // Verify if member "size" exists, if so, then serialize and
      // increment
      LinkedHashSet<String> fields = data.getFieldNames();
      boolean found = true;
      while (found) {
        String entryFieldName = nestedFieldName + "[" + size + "]";
        boolean fieldExists = false;
        Iterator<String> it = fields.iterator();
        while (!fieldExists && it.hasNext()) {
          if (it.next().startsWith(entryFieldName)) {
            fieldExists = true;
          }
        }
        if (fieldExists) {
          localWriter.write("<element>");
          this.serializeUnion(localWriter, entryFieldName, (MetaUnion) subType, data);
          localWriter.write("</element>");
          size++;
        } else {
          found = false;
        }
      }
      if (size == 0) {
        writer.write("&lt;NULL&gt;");
      } else {
        writer.write("<size>" + size + "</size>");
      }
      writer.write(localWriter.toString());

    } else {
      writer.write("&lt;NULL&gt;");
    }
    return;
  }
  /**
   * Acquires the number of elements of the supplied sequence.
   *
   * @param nestedFieldName The name of the field (including scope)
   * @param seqType The type of the sequence.
   * @param data The data that is being serialized.
   * @return The number of elements in the given sequence.
   */
  private int getSequenceSize(String nestedFieldName, MetaCollection seqType, UserData data) {
    /*
     * This is very much based on how serializeUnboundedSequence get
     * the size of its unbounded sequence.
     * The same actions can be used on bounded sequences as well to
     * get their actual size (which can be less then their maximum
     * size).
     */
    int size = 0;

    StringTokenizer tokenizer;
    String typeName;
    String value;
    String token;
    MetaField subType = seqType.getSubType();

    if (subType instanceof MetaPrimitive) {
      value = data.getFieldValue(nestedFieldName);

      if (value != null) {
        if (!(value.equals("NULL") || value.equals("[]"))) {
          tokenizer = new StringTokenizer(value.substring(1, value.length() - 1), ",");
          size = tokenizer.countTokens();
        }
      } else {
        // also check for the alternative notation
        value = data.getFieldValue(nestedFieldName + "[0]");
        if (value != null) {
          List<String> values = new ArrayList<String>();
          int index = 1;
          while (value != null) {
            values.add(value);
            value = data.getFieldValue(nestedFieldName + "[" + index + "]");
            index++;
          }
          size = values.size();
        }
      }
    } else if (subType instanceof MetaCollection) {
      typeName = subType.getTypeName();

      if ((typeName.equals("c_string"))
          || (typeName.equals("c_wstring"))
          || (typeName.startsWith("C_STRING<"))
          || (typeName.startsWith("C_WSTRING<"))) {
        value = data.getFieldValue(nestedFieldName);

        if (value != null) {
          if (!(value.equals("NULL") || value.equals("[]"))) {
            tokenizer = new StringTokenizer(value.substring(1, value.length() - 1), ",");
            size = tokenizer.countTokens();
          }
        }
      }
    } else if ((subType instanceof MetaStruct) || (subType instanceof MetaUnion)) {
      // Verify if member "size" exists, if so, then serialize and
      // increment
      LinkedHashSet<String> fields = data.getFieldNames();
      boolean found = true;
      while (found) {
        String entryFieldName = nestedFieldName + "[" + size + "]";
        boolean fieldExists = false;
        Iterator<String> it = fields.iterator();
        while (!fieldExists && it.hasNext()) {
          if (it.next().startsWith(entryFieldName)) {
            fieldExists = true;
          }
        }
        if (fieldExists) {
          size++;
        } else {
          found = false;
        }
      }
    }
    return size;
  }
  /**
   * Serializes the contents of the supplied collection.
   *
   * @param nestedFieldName The name of the collection field in the data.
   * @param colType The type of the collection.
   * @param data The data to serialize.
   * @return The serialized representation of the collection data.
   */
  private String getSerializedStringCollection(
      String nestedFieldName, MetaCollection colType, UserData data) {
    String result = null;
    String size = null;
    String typeName = colType.getTypeName();
    int index = nestedFieldName.indexOf('[');
    String temp = null;

    if ((typeName.equals("c_string"))
        || (typeName.equals("c_wstring"))
        || (typeName.startsWith("C_STRING<"))
        || (typeName.startsWith("C_WSTRING<"))) {
      String strData = data.getFieldValue(nestedFieldName);
      result = strData.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
    } else if (colType.getSubType() instanceof MetaCollection) {
      result = "";

      if (colType.getSubType().getTypeName().startsWith("C_SEQUENCE<")) {
        size = "<size>" + ((MetaCollection) colType.getSubType()).getMaxSize() + "</size>";
      }

      if (index != -1) {
        temp = nestedFieldName.substring(0, index);

        for (int i = 0; i < colType.getMaxSize(); i++) {
          result += "<element>";

          if (size != null) {
            result += size;
          }
          result +=
              getSerializedStringCollection(
                  temp + "[" + i + "]" + nestedFieldName.substring(index),
                  (MetaCollection) colType.getSubType(),
                  data);
          result += "</element>";
        }
      } else {
        for (int i = 0; i < colType.getMaxSize(); i++) {
          result += "<element>";

          if (size != null) {
            result += size;
          }
          result +=
              getSerializedStringCollection(
                  nestedFieldName + "[" + i + "]", (MetaCollection) colType.getSubType(), data);
          result += "</element>";
        }
      }
    } else {
      result = "";

      if (index != -1) {
        temp = nestedFieldName.substring(0, index);

        for (int i = 0; i < colType.getMaxSize(); i++) {
          result += "<element>";
          result += data.getFieldValue(temp + "[" + i + "]" + nestedFieldName.substring(index));
          result += "</element>";
        }
      } else {
        for (int i = 0; i < colType.getMaxSize(); i++) {
          result += "<element>";
          result += data.getFieldValue(nestedFieldName + "[" + i + "]");
          result += "</element>";
        }
      }
    }
    return result;
  }
 /**
  * Creates a plain text representation of a typedef collection.
  *
  * @param field The 'typedeffed' collection.
  * @param indent The indentation to draw.
  * @param type The complete type.
  */
 private void drawTypedefCollection(MetaCollection field, String indent, MetaType type) {
   writer.write(indent + field.getTypeName());
 }