/*
 *  hddm-c :	tool that reads in a HDDM document (Hall D Data Model)
 *		and writes a c header file that embodies the model in
 *		c structures.  It also generates an input/output pair
 *		of functions to translate the model between the memory
 *		representation and a default binary representation that
 *		is suitable for passing over a pipe or storing on disk.
 *
 *  Version 1.2 - Richard Jones, December 2005.
 *  - Updated code to use STL strings and vectors instead of old c-style
 *    pre-allocated arrays and strXXX functions.
 *  - Moved functions into classes grouped by function for better clarity.
 *
 *  Version 1.1 - Richard Jones, September 2003.
 *  - Updated code to work with the new DOM-2 implementation Xerces-c
 *    from apache.org.  Significant changes have taken place in the API
 *    since DOM-1.
 *  - Added support for new types "long" (int64), "string" (char arrays of
 *    arbitrary length), and "anyURI" (special case of string).
 *  - Switched from native encoding to the use of the XDR library to make
 *    hddm files machine-independent.
 *
 *  Original version - Richard Jones, May 25 2001.
 *
 *
 *  Programmer's Notes:
 *  -------------------
 * 1. The HDDM specification describes data files using xml.  For
 *    information about the contents and structure of HDDM documents
 *    see the web page that describes the data model.
 *
 * 2. Access by hddm-c to the xml source is through the industry-
 *    standard Document Object Model (DOM) interface.
 *
 * 3. The code has been tested with the xerces-c DOM implementation from
 *    Apache, and is intended to be used with the xerces-c library.
 *
 * 4. Output is sent to <filename>.h and <filename>.c where <filename> is
 *    by default "hddm" and can be changed with the -o option.
 *
 * 5. As a by-product of using the DOM parser to access the xml source,
 *    hddm-c verifies the source for well-formedness.  Therefore it may
 *    also be used to check the xml data model document.
 *
 *
 *  Implementation Notes:
 *  ---------------------
 * 1. The binary stream consists of the hddm data model in the form
 *    of a well-formed xml header, followed by binary data.
 *
 * 2. The binary data consist of a sequence of event records, which are
 *    repetitions of the basic data model in a serial representation.
 *
 * 3. Each element is output to the stream in the order it appears in
 *    the data model, prefixed by an exclusive byte count.
 *
 * 4. Any c application compiled with the hddm header file that is
 *    generated by hddm-c is able to read any hddm binary file that
 *    was written using the same hddm class, ie. the <HDDM class="x">
 *    document tags must be a non-colliding set (see matching rules).
 *
 * 5. The input/output features of the c library produced by hddm-c
 *    are implemented using the xdr binary i/o library written originally
 *    by Sun Microsystems.  This library provides basic serialization/
 *    deserialization of binary data using network byte-ordering
 *    (RFC-1832) and is a part of many unix installations.
 */

#define MAX_POPLIST_LENGTH 99

#include "XString.hpp"
#include "XParsers.hpp"

#include <assert.h>
#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>

#include <string>
#include <vector>
#include <fstream>

#define X(str) XString(str).unicode_str()
#define S(str) str.c_str()

using namespace xercesc;

XString classPrefix;

void usage()
{
   std::cerr
        << "\nUsage:\n"
        << "    hddm-c [-v | -o <filename>] {HDDM file}\n\n"
        << "Options:\n"
        <<  "    -v			validate only\n"
        <<  "    -o <filename>	write to <filename>.h"
        << std::endl;
}

class XtString : public XString
{
/* XString class with a few extra methods for creating type
 * strings that are useful in creating c structures
 */
 public:
   XtString() {};
   XtString(const char* s): XString(s) {};
   XtString(const XMLCh* p): XString(p) {};
   XtString(const std::string& s): XString(s) {};
   XtString(const XString& x): XString(x) {};
   XtString(const XtString& t): XString((XString&)t) {};
   ~XtString();

   XtString& plural();
   XtString& simpleType();
   XtString& listType();

 private:
   std::list<XtString*> fStringCollection;
};

class CodeBuilder
{
/* The methods in this class are used to write the c code that
 * implements the hddm structures i/o library.
 */
 public:
   std::ofstream hFile;
   std::ofstream cFile;

   CodeBuilder() {};
   ~CodeBuilder() {};

   void checkConsistency(DOMElement* el, DOMElement* elref);
   void writeHeader(DOMElement* el);
   void constructGroup(DOMElement* el);
   void constructConstructors();
   void constructUnpackers();
   void constructReadFunc(DOMElement* topEl);
   void constructSkipFunc();
   void constructPackers();
   void constructFlushFunc(DOMElement* el);
   void writeMatcher();
   void constructOpenFunc(DOMElement* el);
   void constructInitFunc(DOMElement* el);
   void constructCloseFunc(DOMElement* el);
   void constructDocument(DOMElement* el);

 private:
   std::vector<DOMElement*> tagList;
};


int main(int argC, char* argV[])
{
   try
   {
      XMLPlatformUtils::Initialize();
   }
   catch (const XMLException* toCatch)
   {
      XtString msg(toCatch->getMessage());
      std::cerr
           << "hddm-c: Error during initialization! :\n"
           << msg << std::endl;
      return 1;
   }

   if (argC < 2)
   {
      usage();
      return 1;
   }
   else if ((argC == 2) && (strcmp(argV[1], "-?") == 0))
   {
      usage();
      return 2;
   }

   XtString xmlFile;
   XtString hFilename;
   bool verifyOnly = false;
   int argInd;
   for (argInd = 1; argInd < argC; argInd++)
   {
      if (argV[argInd][0] != '-')
      {
         break;
      }
      if (strcmp(argV[argInd],"-v") == 0)
      {
         verifyOnly = true;
      }
      else if (strcmp(argV[argInd],"-o") == 0)
      {
         hFilename = XtString(argV[++argInd]);
      }
      else
      {
         std::cerr
              << "Unknown option \'" << argV[argInd]
              << "\', ignoring it\n" << std::endl;
      }
   }

   if (argInd != argC - 1)
   {
      usage();
      return 1;
   }
   xmlFile = XtString(argV[argInd]);

#if defined OLD_STYLE_XERCES_PARSER
   DOMDocument* document = parseInputDocument(xmlFile.c_str(),false);
#else
   DOMDocument* document = buildDOMDocument(xmlFile.c_str(),false);
#endif
   if (document == 0)
   {
      std::cerr
           << "hddm-c : Error parsing HDDM document, "
           << "cannot continue" << std::endl;
      return 1;
   }

   DOMElement* rootEl = document->getDocumentElement();
   XtString rootS(rootEl->getTagName());
   if (rootS != "HDDM")
   {
      std::cerr
           << "hddm-c error: root element of input document is "
           << "\"" << rootS << "\", expected \"HDDM\""
           << std::endl;
      return 1;
   }

   XtString classS(rootEl->getAttribute(X("class")));
   classPrefix = classS;

   XtString hname;
   if (verifyOnly)
   {
      hname = "/dev/null";
   }
   else if (hFilename.size())
   {
      hname = hFilename + ".h";
   }
   else
   {
      hname = "hddm_" + classPrefix + ".h";
   }

   CodeBuilder builder;
   builder.hFile.open(hname.c_str());
   if (! builder.hFile.is_open())
   {
      std::cerr
           << "hddm-c error: unable to open output file "
           << hname << std::endl;
      return 1;
   }

   XtString cname;
   if (verifyOnly)
   {
      cname = "/dev/null";
   }
   else if (hFilename.size())
   {
      cname = hFilename + ".c";
   }
   else
   {
      cname = "hddm_" + classPrefix + ".c";
   }

   builder.cFile.open(cname.c_str());
   if (! builder.cFile.is_open())
   {
      std::cerr
           << "hddm-c error: unable to open output file "
           << cname << std::endl;
      return 1;
   }

   builder.hFile 
         << "/*"						<< std::endl
	 << " * " << hname << " - DO NOT EDIT THIS FILE"	<< std::endl
	 << " *"						<< std::endl
	 << " * This file was generated automatically by hddm-c"
	 << " from the file"					<< std::endl
         << " * " << xmlFile					<< std::endl
								<< std::endl
         << " * This header file defines the c structures that"
	 << " hold the data"					<< std::endl
	 << " * described in the data model"
         << " (from " << xmlFile << "). "			<< std::endl
	 << " *"						<< std::endl
	 << " * The hddm data model tool set was written by"	<< std::endl
	 << " * Richard Jones, University of Connecticut."	<< std::endl
	 << " *"						<< std::endl
	 << " * For more information see the following web site"<< std::endl
	 << " *"						<< std::endl
	 << " * http://zeus.phys.uconn.edu/halld/datamodel/doc"	<< std::endl
	 << " *"						<< std::endl
	 << " */"						<< std::endl
	 							<< std::endl;

   builder.cFile
	 << "/*"						<< std::endl
	 << " * " << cname << " - DO NOT EDIT THIS FILE"	<< std::endl
	 << " *"						<< std::endl
	 << " * This file was generated automatically by hddm-c"
	 << " from the file"					<< std::endl
         << " * " << xmlFile					<< std::endl
								<< std::endl
         << " * This c file contains the i/o interface to"
         << " the c structures"					<< std::endl
	 << " * described in the data model"
         << " (from " << xmlFile << "). "			<< std::endl
	 << " *"						<< std::endl
	 << " * The hddm data model tool set was written by"	<< std::endl
	 << " * Richard Jones, University of Connecticut."	<< std::endl
	 << " *"						<< std::endl
	 << " * For more information see the following web site"<< std::endl
	 << " *"						<< std::endl
	 << " * http://zeus.phys.uconn.edu/halld/datamodel/doc"	<< std::endl
	 << " */"						<< std::endl
								<< std::endl;

   builder.hFile
	 << "#include <stdlib.h>"				<< std::endl
	 << "#include <stdio.h>" 				<< std::endl
	 << "#include <errno.h>" 				<< std::endl
	 << "#include <rpc/rpc.h>" 				<< std::endl
	 << "#include <string.h>"				<< std::endl
	 << "#include <strings.h>"				<< std::endl
	 << "#include <particleType.h>"				<< std::endl
								<< std::endl
         << "typedef char* string_t;	"
         << "// use this alias for string-valued attributes"	<< std::endl
								<< std::endl
	 << "/* Note to users: The option MALLOC_FREE_WITH_MEMCHECK" << std::endl
	 << " * was created for debugging this hddm library, but it" << std::endl
	 << " * is also useful for finding memory leaks in user" << std::endl
	 << " * code.  To use it, replace malloc(n) everywhere in" << std::endl
	 << " * your code with MALLOC(n,\"some descriptive string\")" << std::endl
	 << " * and free(p) with FREE(p) and include this header" << std::endl
	 << " * and compile with -DMALLOC_FREE_WITH_MEMCHECK set." << std::endl
	 << " * Any attempt to malloc memory already malloc'ed or" << std::endl
	 << " * to free memory that has not yet been malloc'ed is" << std::endl
	 << " * immediately flagged with an error message.  A call" << std::endl
	 << " * to checkpoint() anywhere in the user code reports" << std::endl
	 << " * any memory that has been malloc'ed not freed." 	<< std::endl
	 << " */"						<< std::endl
	 << "#if defined MALLOC_FREE_WITH_MEMCHECK"		<< std::endl
         << "#  include <memcheck.h>"				<< std::endl
         << "#  define MALLOC(N,S) (checkin(malloc(N),S))"	<< std::endl
         << "#  define FREE(P) (checkout(P),free(P))"		<< std::endl
         << "#else"						<< std::endl
	 << "#  define MALLOC(N,S) malloc(N)"			<< std::endl
	 << "#  define FREE(P) free(P)"				<< std::endl
         << "#endif"						<< std::endl;

   builder.cFile
	 << "int hddm_nullTarget=0;"				<< std::endl
         << "#define HDDM_NULL (void*)&hddm_nullTarget"         << std::endl
                                                                << std::endl
         << "#include \"" << hname << "\"" 			<< std::endl
								<< std::endl;
   builder.constructGroup(rootEl);

   builder.hFile						<< std::endl
	 << "#ifdef __cplusplus"				<< std::endl
	 << "extern \"C\" {"					<< std::endl
	 << "#endif"						<< std::endl;
   builder.constructConstructors();
   builder.hFile						<< std::endl
	 << "#ifdef __cplusplus"				<< std::endl
	 << "}"							<< std::endl
	 << "#endif"						<< std::endl;

   builder.hFile 						<< std::endl
	 << "#ifndef " << classPrefix << "_DocumentString" 	<< std::endl
	 << "#define " << classPrefix << "_DocumentString" 	<< std::endl
         							<< std::endl
	 << "extern "
	 << "char HDDM_" << classPrefix << "_DocumentString[];"	<< std::endl
         							<< std::endl
         << "#ifdef INLINE_PREPEND_UNDERSCORES"			<< std::endl
         << "#define inline __inline"				<< std::endl
         << "#endif"						<< std::endl
								<< std::endl
	 << "#endif /* " << classPrefix << "_DocumentString */"	<< std::endl;

   builder.cFile						<< std::endl
	 << "char HDDM_" << classPrefix << "_DocumentString[]"
	 << " = "						<< std::endl;
   builder.constructDocument(rootEl);
   builder.cFile << ";"						<< std::endl;

   builder.hFile 						<< std::endl
	 << "#ifndef HDDM_STREAM_INPUT"				<< std::endl
	 << "#define HDDM_STREAM_INPUT -91"			<< std::endl
	 << "#define HDDM_STREAM_OUTPUT -92"			<< std::endl
           							<< std::endl
	 << "struct popNode_s {"				<< std::endl
         << "   void* (*unpacker)(XDR*, struct popNode_s*);"	<< std::endl
         << "   int inParent;"					<< std::endl
         << "   int popListLength;"				<< std::endl
         << "   struct popNode_s* popList["
         << MAX_POPLIST_LENGTH << "];"				<< std::endl
         << "};"						<< std::endl
         << "typedef struct popNode_s popNode;"			<< std::endl
                                                                << std::endl
	 << "typedef struct {"					<< std::endl
	 << "   FILE* fd;"					<< std::endl
	 << "   int iomode;"					<< std::endl
	 << "   int lerrno;"					<< std::endl
	 << "   char* filename;"				<< std::endl
         << "   XDR* xdrs;"					<< std::endl
	 << "   popNode* popTop;"				<< std::endl
	 << "} " << classPrefix << "_iostream_t;"		<< std::endl
								<< std::endl
	 << "#endif /* HDDM_STREAM_INPUT */"			<< std::endl;

   builder.cFile						<< std::endl
         << "#ifndef _FILE_OFFSET_BITS"				<< std::endl
         << "# define _FILE_OFFSET_BITS 64"			<< std::endl
         << "#endif"						<< std::endl
								<< std::endl
         << "static off_t xdr_getpos64(XDR *xdrs)"		<< std::endl
         << "{"							<< std::endl
         << "   if (xdrs->x_base == 0) {"			<< std::endl
         << "      return ftello((FILE *)xdrs->x_private);"	<< std::endl
         << "   }"						<< std::endl
         << "   off_t pos = xdr_getpos(xdrs);"			<< std::endl
         << "   return pos;"					<< std::endl
         << "}"							<< std::endl
         							<< std::endl
         << "static bool_t xdr_setpos64(XDR *xdrs, off_t pos) "	<< std::endl
         << "{ "						<< std::endl
         << "   if (xdrs->x_base == 0) {"			<< std::endl
         << "      return ((fseeko((FILE *)xdrs->x_private, pos, 0) < 0)? FALSE : TRUE);"
								<< std::endl
         << "   }"						<< std::endl
         << "   return xdr_setpos(xdrs,pos);"			<< std::endl
         << "}"							<< std::endl;

   builder.constructUnpackers();

   builder.hFile						<< std::endl
	 << "#ifdef __cplusplus"				<< std::endl
	 << "extern \"C\" {"					<< std::endl
	 << "#endif"						<< std::endl;
   builder.constructReadFunc(rootEl);
   builder.constructSkipFunc();
   builder.constructFlushFunc(rootEl);
   builder.constructOpenFunc(rootEl);
   builder.constructInitFunc(rootEl);
   builder.constructCloseFunc(rootEl);
   builder.hFile						<< std::endl
	 << "#ifdef __cplusplus"				<< std::endl
	 << "}"							<< std::endl
	 << "#endif"						<< std::endl
	            						<< std::endl
	 << "#if !defined HDDM_NULL"				<< std::endl
         << "extern int hddm_nullTarget;"			<< std::endl
         << "# define HDDM_NULL (void*)&hddm_nullTarget"        << std::endl
	 << "#endif"						<< std::endl;

   XMLPlatformUtils::Terminate();
   return 0;
}

XtString::~XtString()
{
   std::list<XtString*>::iterator iter;
   for (iter = fStringCollection.begin();
        iter != fStringCollection.end();
        ++iter)
   {
      delete *iter;
   }
}

XtString& XtString::plural()
{
   XtString* p = new XtString(*this);
   fStringCollection.push_back(p);
   XtString::size_type len = p->size();
   if (len > 3 && p->substr(len-3,3) == "tum")
   {
      p->replace(len-3,3,"ta");
   }
   else if (len > 1 && p->substr(len-3,3) == "ies")
   {
      p->replace(len-3,3,"iesList");
   }
   else if (len > 2 && p->substr(len-2,2) == "ex")
   {
      p->replace(len-2,2,"ices");
   }
   else if (len > 2 && p->substr(len-2,2) == "sh")
   {
      p->replace(len-2,2,"shes");
   }
   else if (len > 1 && p->substr(len-1,1) == "s")
   {
      p->replace(len-1,1,"ses");
   }
   else if (len > 1)
   {
      *p += "s";
   }
   return *p;
}

/* Map from tag name to name of the corresponding c-structure
 * for the case of simple tags (those that do not repeat)
 */
XtString& XtString::simpleType()
{
   XtString* p = new XtString(*this);
   fStringCollection.push_back(p);
   (*p)[0] = toupper((*p)[0]);
   *p = classPrefix + "_" + *p + "_t";
   return *p;
}

/* Map from tag name to name of the corresponding c-structure
 * for the case of list tags (those that may repeat)
 */
XtString& XtString::listType()
{
   XtString& r = plural();
   r[0] = toupper(r[0]);
   r = classPrefix + "_" + r + "_t";
   return r;
}

/* Verify that the tag group under this element does not collide
 * with existing tag group elref, otherwise exit with fatal error
 */
void CodeBuilder::checkConsistency(DOMElement* el, DOMElement* elref)
{
   XtString tagS(el->getTagName());
   if (el->getParentNode() == elref->getParentNode())
   {
      std::cerr
           << "hddm-cpp error: tag " << "\"" << tagS 
           << "\" is duplicated within one context in xml document."
	   << std::endl;
      exit(1);
   }

   DOMNamedNodeMap* oldAttr = elref->getAttributes();
   DOMNamedNodeMap* newAttr = el->getAttributes();
   int listLength = oldAttr->getLength();
   for (int n = 0; n < listLength; n++)
   {
      XtString nameS(oldAttr->item(n)->getNodeName());
      XtString oldS(elref->getAttribute(X(nameS)));
      XtString newS(el->getAttribute(X(nameS)));
      if (nameS == "minOccurs")
      {
         continue;
      }
      else if (nameS == "maxOccurs")
      {
         int maxold = (oldS == "unbounded")? 9999 : atoi(S(oldS));
         int maxnew = (newS == "unbounded")? 9999 : atoi(S(newS));
	 if (maxold*maxnew <= maxold)
         {
            std::cerr
                 << "hddm-c error: inconsistent maxOccurs usage by tag "
                 << "\"" << tagS << "\" in xml document." << std::endl;
            exit(1);
         }
      }
      else if (newS != oldS)
      {
         std::cerr
              << "hddm-c error: inconsistent usage of attribute "
              << "\"" << nameS << "\" in tag "
              << "\"" << tagS << "\" in xml document." << std::endl;
         exit(1);
      }
   }
   listLength = newAttr->getLength();
   for (int n = 0; n < listLength; n++)
   {
      XtString nameS(newAttr->item(n)->getNodeName());
      XtString oldS(elref->getAttribute(X(nameS)));
      XtString newS(el->getAttribute(X(nameS)));
      if (nameS == "minOccurs")
      {
         continue;
      }
      else if (nameS == "maxOccurs")
      {
         int maxold = (oldS == "unbounded")? 9999 : atoi(S(oldS));
         int maxnew = (newS == "unbounded")? 9999 : atoi(S(newS));
	 if (maxold*maxnew <= maxnew)
         {
            std::cerr
                 << "hddm-c error: inconsistent maxOccurs usage by tag "
                 << "\"" << tagS << "\" in xml document." << std::endl;
            exit(1);
         }
      }
      else if (newS != oldS)
      {
         std::cerr
              << "hddm-c error: inconsistent usage of attribute "
              << "\"" << nameS << "\" in tag "
              << "\"" << tagS << "\" in xml document." << std::endl;
         exit(1);
      }
   }
   DOMNodeList* oldList = elref->getChildNodes();
   DOMNodeList* newList = el->getChildNodes();
   listLength = oldList->getLength();
   if (newList->getLength() != listLength)
   {
      std::cerr
           << "hddm-c error: inconsistent usage of tag "
           << "\"" << tagS << "\" in xml document." << std::endl;
   exit(1);
   }
   for (int n = 0; n < listLength; n++)
   {
      DOMNode* cont = oldList->item(n);
      XtString nameS(cont->getNodeName());
      short type = cont->getNodeType();
      if (type == DOMNode::ELEMENT_NODE)
      {
         DOMNodeList* contList = el->getElementsByTagName(X(nameS));
         if (contList->getLength() != 1)
         {
             std::cerr
                  << "hddm-c error: inconsistent usage of tag "
                  << "\"" << tagS << "\" in xml document." << std::endl;
             exit(1);
         }
      }
   }
}

/* Write declaration of c-structure for this tag to c-header file */

void CodeBuilder::writeHeader(DOMElement* el)
{
   XtString tagS(el->getTagName());
   XtString ctypeDef = tagS.simpleType();
   hFile << std::endl
	 << "#ifndef SAW_" << ctypeDef 				<< std::endl
	 << "#define SAW_" << ctypeDef				<< std::endl
								<< std::endl
	 << "typedef struct {"					<< std::endl;

   DOMNamedNodeMap* varList = el->getAttributes();
   int varCount = varList->getLength();
   for (int v = 0; v < varCount; v++)
   {
      DOMNode* var = varList->item(v);
      XtString typeS(var->getNodeValue());
      XtString nameS(var->getNodeName());
      if (typeS == "int")
      {
         hFile << "   int32_t              " << nameS << ";" << std::endl;
      }
      else if (typeS == "long")
      {
         hFile << "   int64_t              " << nameS << ";" << std::endl;
      }
      else if (typeS == "float")
      {
         hFile << "   float                " << nameS << ";" << std::endl;
      }
      else if (typeS == "double")
      {
         hFile << "   double               " << nameS << ";" << std::endl;
      }
      else if (typeS == "boolean")
      {
         hFile << "   bool_t               " << nameS << ";" << std::endl;
      }
      else if (typeS == "string")
      {
         hFile << "   string_t             " << nameS << ";" << std::endl;
      }
      else if (typeS == "anyURI")
      {
         hFile << "   string_t             " << nameS << ";" << std::endl;
      }
      else if (typeS == "Particle_t")
      {
         hFile << "   Particle_t           " << nameS << ";" << std::endl;
      }
      else
      {
         /* ignore attributes with unrecognized values */
      }
   }

   DOMNodeList* contList = el->getChildNodes();
   int contLength = contList->getLength();
   for (int c = 0; c < contLength; c++)
   {
      DOMNode* cont = contList->item(c);
      XtString nameS(cont->getNodeName());
      short type = cont->getNodeType();
      if (type == DOMNode::ELEMENT_NODE)
      {
         DOMElement* contEl = (DOMElement*) cont;
         XtString repS(contEl->getAttribute(X("maxOccurs")));
	 int rep = (repS == "unbounded")? 9999 : atoi(S(repS));
         XtString ctypeRef = (rep > 1) ? nameS.listType()
	                               : nameS.simpleType();
         XtString::size_type clen = ctypeRef.size();

         hFile << "   " << ctypeRef << "* ";
         for (int i = 0; i < 19-(int)clen; i++)
         {
            hFile << " ";
         }
         hFile <<  ((rep > 1) ? nameS.plural() : nameS) << ";" << std::endl;
      }
   }

   hFile << "} " << ctypeDef << ";" << std::endl;

   XtString repS(el->getAttribute(X("maxOccurs")));
   int rep = (repS == "unbounded")? 9999 : atoi(S(repS));
   if (rep > 1)
   {
      XtString ctypeRef = tagS.listType();
      hFile << std::endl << "typedef struct {" << std::endl
            << "   unsigned int mult;" << std::endl
            << "   " << ctypeDef << " in[1];" << std::endl
            << "} " << ctypeRef << ";" << std::endl;
   }

   hFile << "#endif /* " << ctypeDef << " */" 			<< std::endl;
}

/* Generate c-structure declarations for this tag and its descendants;
 * this function calls itself recursively
 */
void CodeBuilder::constructGroup(DOMElement* el)
{
   XtString tagS(el->getTagName());
   std::vector<DOMElement*>::iterator iter;
   for (iter = tagList.begin(); iter != tagList.end(); iter++)
   {
      XtString targS((*iter)->getTagName());
      if (tagS == targS)
      {
         checkConsistency(el,*iter);
         return;
      }
   }

   tagList.push_back(el);

   DOMNodeList* contList = el->getChildNodes();
   int contLength = contList->getLength();
   for (int c = 0; c < contLength; c++)
   {
      DOMNode* cont = contList->item(c);
      short type = cont->getNodeType();
      if (type == DOMNode::ELEMENT_NODE)
      {
         DOMElement* contEl = (DOMElement*) cont;
         constructGroup(contEl);
      }
   }

   writeHeader(el);
}

/* Generate c code for make_<class letter>_<group name> functions */

void CodeBuilder::constructConstructors()
{
   std::vector<DOMElement*>::iterator iter;
   for (iter = tagList.begin(); iter != tagList.end(); iter++)
   {
      DOMElement* tagEl = *iter;
      XtString tagS(tagEl->getTagName());
      XtString listType = tagS.listType();
      XtString simpleType = tagS.simpleType();

      hFile << std::endl;
      cFile << std::endl;

      XtString repS = tagEl->getAttribute(X("maxOccurs"));
      int rep = (repS == "unbounded")? 9999 : atoi(S(repS));
      if (rep > 1)
      {
         hFile << listType << "* ";
         cFile << listType << "* ";
         XtString listT(listType);
         listT.erase(listT.rfind('_'));
         hFile << "make_" << listT;
         cFile << "make_" << listT;
         hFile << "(int n);" 					<< std::endl;
         cFile << "(int n)" 					<< std::endl
               << "{"						<< std::endl
               << "   int i;"					<< std::endl
               << "   int rep = (n > 1) ? n-1 : 0;"		<< std::endl
               << "   int size = sizeof(" << listType
               << ") + rep * sizeof(" << simpleType << ");"	<< std::endl
               << "   " << listType
               << "* p = (" << listType << "*)MALLOC(size,\""
               << listType << "\");"				<< std::endl
               << "   p->mult = 0;"				<< std::endl
               << "   for (i=0; i<n; i++) {"			<< std::endl
               << "      " << simpleType << "* pp = &p->in[i];" << std::endl;
         DOMNamedNodeMap* varList = tagEl->getAttributes();
         int varCount = varList->getLength();
         for (int v = 0; v < varCount; v++)
         {
            DOMNode* var = varList->item(v);
            XtString typeS(var->getNodeValue());
            XtString nameS(var->getNodeName());
            if (typeS == "string" || 
                typeS == "anyURI")
            {
               cFile << "      pp->" << nameS
                     << " = (string_t)&hddm_nullTarget;"	<< std::endl;
            }
            else if (typeS == "int" ||
	             typeS == "long" ||
                     typeS == "float" ||
                     typeS == "double" ||
                     typeS == "boolean" ||
                     typeS == "Particle_t")
            {
               cFile << "      pp->" << nameS << " = 0;"	<< std::endl;
            }
         }
         DOMNodeList* contList = tagEl->getChildNodes();
         for (int c = 0; c < contList->getLength(); c++)
         {
            DOMNode* cont = contList->item(c);
            short ctype = cont->getNodeType();
            if (ctype == DOMNode::ELEMENT_NODE)
            {
               DOMElement* contEl = (DOMElement*) cont;
               XtString cnameS(contEl->getTagName());
               XtString crepS(contEl->getAttribute(X("maxOccurs")));
	       int crep = (crepS == "unbounded")? 9999 : atoi(S(crepS));
               if (crep > 1)
               {
                  cFile << "      pp->" << cnameS.plural()
                        << " = (" << cnameS.listType()
                        << "*)&hddm_nullTarget;"		<< std::endl;
               }
               else
               {
                  cFile << "      pp->" << cnameS
                        << " = (" << cnameS.simpleType()
                        << "*)&hddm_nullTarget;"		<< std::endl;
               }
            }
         }
         cFile << "   }"					<< std::endl;
      }
      else
      {
         hFile << simpleType << "* ";
         cFile << simpleType << "* ";
         XtString simpleT(simpleType);
         simpleT.erase(simpleT.rfind('_'));
         hFile << "make_" << simpleT;
         cFile << "make_" << simpleT;
         hFile << "();"	 					<< std::endl;
         cFile << "()"	 					<< std::endl
               << "{"						<< std::endl
               << "   int size = sizeof(" << simpleType << ");"	<< std::endl
               << "   " << simpleType << "* p = "
               << "(" << simpleType << "*)MALLOC(size,\""
               << simpleType << "\");"				<< std::endl;
         DOMNamedNodeMap* varList = tagEl->getAttributes();
         int varCount = varList->getLength();
         for (int v = 0; v < varCount; v++)
         {
            DOMNode* var = varList->item(v);
            XtString typeS(var->getNodeValue());
            XtString nameS(var->getNodeName());
            if (typeS == "string" || 
                typeS == "anyURI")
            {
               cFile << "   p->" << nameS
                     << " = (string_t)&hddm_nullTarget;"	<< std::endl;
            }
            else if (typeS == "int" ||
	             typeS == "long" ||
                     typeS == "float" ||
                     typeS == "double" ||
                     typeS == "boolean" ||
                     typeS == "Particle_t")
            {
               cFile << "   p->" << nameS << " = 0;"		<< std::endl;
            }
         }
         DOMNodeList* contList = tagEl->getChildNodes();
         for (int c = 0; c < contList->getLength(); c++)
         {
            DOMNode* cont = contList->item(c);
            short ctype = cont->getNodeType();
            if (ctype == DOMNode::ELEMENT_NODE)
            {
               DOMElement* contEl = (DOMElement*) cont;
               XtString cnameS(contEl->getTagName());
               XtString crepS(contEl->getAttribute(X("maxOccurs")));
	       int crep = (crepS == "unbounded")? 9999 : atoi(S(crepS));
               if (crep > 1)
               {
                  cFile << "   p->" << cnameS.plural()
                        << " = (" << cnameS.listType()
                        << "*)&hddm_nullTarget;"		<< std::endl;
               }
               else
               {
                  cFile << "   p->" << cnameS
                        << " = (" << cnameS.simpleType()
                        << "*)&hddm_nullTarget;"		<< std::endl;
               }
            }
         }
      }
      cFile << "   return p;"					<< std::endl
            << "}"						<< std::endl;
   }
}

/* Generate c functions for unpacking binary stream into c-structures */

void CodeBuilder::constructUnpackers()
{
   cFile << std::endl;
   std::vector<DOMElement*>::iterator iter;
   for (iter = tagList.begin(); iter != tagList.end(); iter++)
   {
      DOMElement* tagEl = *iter;
      XtString tagS(tagEl->getTagName());
      XtString listType = tagS.listType();
      XtString simpleType = tagS.simpleType();

      cFile << std::endl << "static ";

      XtString tagType;
      XtString repS = tagEl->getAttribute(X("maxOccurs"));
      int rep = (repS == "unbounded")? 9999 : atoi(S(repS));
      if (rep > 1)
      {
         tagType = tagS.listType();
      }
      else
      {
         tagType = tagS.simpleType();
      }
      XtString tagT(tagType);
      tagT.erase(tagT.rfind('_'));
      cFile << tagType << "* unpack_" << tagT
            << "(XDR* xdrs, popNode* pop)"
								<< std::endl
            << "{"						<< std::endl
            << "   " << tagType << "* this1 = HDDM_NULL;"	<< std::endl
            << "   unsigned int size;"				<< std::endl
	    << "   if (! xdr_u_int(xdrs,&size))"		<< std::endl
            << "   {"						<< std::endl
	    << "       return this1;"				<< std::endl
            << "   }"						<< std::endl
            << "   else if (size > 0)"				<< std::endl
            << "   {"						<< std::endl
            << "      off_t start = xdr_getpos64(xdrs);"	<< std::endl;

      if (rep > 1)
      {
         cFile << "      int m;"				<< std::endl
               << "      unsigned int mult;"			<< std::endl
	       << "      xdr_u_int(xdrs,&mult);"		<< std::endl
               << "      this1 = make_" << tagT << "(mult);"	<< std::endl
               << "      this1->mult = mult;"			<< std::endl
               << "      for (m = 0; m < mult; m++ )"		<< std::endl
               << "      {"					<< std::endl;
      }
      else
      {
         cFile << "      this1 = make_" << tagT << "();"	<< std::endl
               << "      {"					<< std::endl;
      }

      int hasContents = 0;
      DOMNodeList* contList = tagEl->getChildNodes();
      for (int c = 0; c < contList->getLength(); c++)
      {
         DOMNode* cont = contList->item(c);
         short type = cont->getNodeType();
         if (type == DOMNode::ELEMENT_NODE)
         {
            hasContents = 1;
            DOMElement* contEl = (DOMElement*) cont;
            XtString nameS(contEl->getTagName());
            XtString reS(contEl->getAttribute(X("maxOccurs")));
	    int re = (reS == "unbounded")? 9999 : atoi(S(reS));
            cFile << "         int p;"				<< std::endl
                  << "         void* (*ptr) = (void**) &this1->"
                  << ((rep > 1) ? "in[m]." : "" )
                  << ((re > 1) ? nameS.plural() : nameS) << ";"	<< std::endl;
            break;
         }
      }

      DOMNamedNodeMap* attList = tagEl->getAttributes();
      for (int a = 0; a < attList->getLength(); a++)
      {
         DOMNode* att = attList->item(a);
         XtString typeS(att->getNodeValue());
         XtString nameS(att->getNodeName());
         XtString nameStr(nameS);
         if (rep > 1)
         {
            nameStr = "in[m]." + nameS;
         }
         if (typeS == "int")
         {
            cFile << "         xdr_int(xdrs,&this1->"
	          << nameStr << ");"				 << std::endl;
         }
	 else if (typeS == "long")
         {
            cFile << "#ifndef XDR_LONGLONG_MISSING"		 << std::endl
                  << "         xdr_longlong_t(xdrs,&this1->"
	          << nameStr << ");"				 << std::endl
                  << "#else"					 << std::endl
                  << "         {"				 << std::endl
                  << "            int* " << nameStr << "_ = "
                  << "(int*)&this1->" << nameStr << ";"		 << std::endl
                  << "# if __BIG_ENDIAN__"			 << std::endl
                  << "            xdr_int(xdrs,&"
                  << nameStr << "_[0]);"			 << std::endl
                  << "            xdr_int(xdrs,&"
                  << nameStr << "_[1]);"			 << std::endl
                  << "# else"					 << std::endl
                  << "            xdr_int(xdrs,&"
                  << nameStr << "_[1]);"			 << std::endl
                  << "            xdr_int(xdrs,&"
                  << nameStr << "_[0]);"			 << std::endl
                  << "# endif"					 << std::endl
                  << "         }"				 << std::endl
                  << "#endif"					 << std::endl;
         }
         else if (typeS == "float")
         {
            cFile << "         xdr_float(xdrs,&this1->"
	          << nameStr << ");"				 << std::endl;
         }
         else if (typeS == "double")
         {
            cFile << "         xdr_double(xdrs,&this1->"
	          << nameStr << ");"				 << std::endl;
         }
         else if (typeS == "boolean")
         {
            cFile << "         xdr_bool(xdrs,&this1->"
	          << nameStr << ");"				 << std::endl;
         }
         else if (typeS == "Particle_t")
         {
            cFile << "         xdr_int(xdrs,(int*)&this1->"
	          << nameStr << ");"				 << std::endl;
         }
         else if (typeS == "string")
         {
            cFile << "         this1->" << nameStr << " = 0;"	 << std::endl
                  << "         xdr_string(xdrs, &this1->"
	          << nameStr << ", 1000000);"			 << std::endl;
         }
         else if (typeS == "anyURI")
         {
            cFile << "         this1->" << nameStr << " = 0;"	 << std::endl
                  << "         xdr_string(xdrs, &this1->"
	          << nameStr << ", 1000000);"			 << std::endl;
         }
         else
         {
            /* ignore attributes with unrecognized values */
         }
      }

      if (hasContents)
      {
         cFile << "         for (p = 0; p < pop->popListLength; p++)"	<< std::endl
               << "         {"						<< std::endl
               << "            popNode* pnode = pop->popList[p];"	<< std::endl
               << "            if (pnode)"                              << std::endl
               << "            {"                                       << std::endl
               << "               int kid = pnode->inParent;"           << std::endl
               << "               ptr[kid] = pnode->unpacker(xdrs,pnode);"
                                                                        << std::endl
               << "            }"                                       << std::endl
               << "            else"                                    << std::endl
               << "            {"                                       << std::endl
               << "               unsigned int skip;"			<< std::endl
	       << "               xdr_u_int(xdrs,&skip);"		<< std::endl
               << "               xdr_setpos64(xdrs,xdr_getpos64(xdrs)+skip);"
	       								<< std::endl
               << "            }"					<< std::endl
               << "         }"						<< std::endl;
      }
      cFile << "      }"						<< std::endl
            << "      xdr_setpos64(xdrs,start+size);"			<< std::endl
            << "   }"							<< std::endl
            << "   return this1;"					<< std::endl
            << "}"							<< std::endl;
   }
}
 
/* Generate c function to read from binary stream into c-structures */

void CodeBuilder::constructReadFunc(DOMElement* topEl)
{
   XtString topS(topEl->getTagName());
   XtString topType = topS.simpleType();
   XtString topT(topType);
   topT.erase(topT.rfind('_'));
   hFile								<< std::endl
	 << topType << "* read_" << topT
	 << "(" << classPrefix << "_iostream_t* fp" << ");"		<< std::endl;

   cFile								<< std::endl
	 << topType << "* read_" << topT
	 << "(" << classPrefix << "_iostream_t* fp" << ")"		<< std::endl
	 << "{"								<< std::endl
         << "   " << topType << "* nextEvent = " 
	 << "unpack_" << topT << "(fp->xdrs,fp->popTop);"		<< std::endl
	 << "   return (nextEvent == HDDM_NULL)? 0 : nextEvent;"	<< std::endl
	 << "}"								<< std::endl;
}

/* Generate c function to skip over events in the binary stream */

void CodeBuilder::constructSkipFunc()
{
   hFile								<< std::endl
	 << "int skip_" << classPrefix << "_HDDM"
	 << "(" << classPrefix << "_iostream_t* fp, int nskip);"	<< std::endl;

   cFile								<< std::endl
	 << "int skip_" << classPrefix << "_HDDM"
	 << "(" << classPrefix << "_iostream_t* fp, int nskip)"		<< std::endl
	 << "{"								<< std::endl
         << "   int skipped;"						<< std::endl
         << "   for (skipped=0; skipped < nskip; ++skipped)"		<< std::endl
         << "   {"							<< std::endl
         << "      unsigned int size;"					<< std::endl
         << "      if (! xdr_u_int(fp->xdrs,&size))"			<< std::endl
         << "      {"							<< std::endl
         << "         return skipped;"					<< std::endl
         << "      }"							<< std::endl
         << "      else if (size > 0)"					<< std::endl
         << "      {"							<< std::endl
         << "         off_t start = xdr_getpos64(fp->xdrs);"		<< std::endl
         << "         if (xdr_setpos64(fp->xdrs,start+size) != 0) {"	<< std::endl
         << "            fp->lerrno = errno;"				<< std::endl
         << "            return skipped;"				<< std::endl
         << "         }"							<< std::endl
         << "      }"							<< std::endl
         << "   }"							<< std::endl
         << "   return skipped;"					<< std::endl
         << "}"								<< std::endl;
}

/* Generate streamers for packing c-structures onto a binary stream
 * and deleting them from memory when output is complete
 */

void CodeBuilder::constructPackers()
{
   cFile << std::endl;
   std::vector<DOMElement*>::iterator iter;
   for (iter = tagList.begin(); iter != tagList.end(); iter++)
   {
      DOMElement* tagEl = *iter;
      XtString tagS(tagEl->getTagName());
      XtString listType = tagS.listType();
      XtString simpleType = tagS.simpleType();

      cFile << "static ";

      XtString tagType;
      XtString repS(tagEl->getAttribute(X("maxOccurs")));
      int rep = (repS == "unbounded")? 9999 : atoi(S(repS));
      if (rep > 1)
      {
         tagType = listType;
      }
      else
      {
         tagType = simpleType;
      }
      XtString tagT(tagType);
      tagT.erase(tagT.rfind('_'));
      cFile << "int pack_" << tagT << "(XDR* xdrs, "
            << tagType << "* this1);"				<< std::endl;
   }

   for (iter = tagList.begin(); iter != tagList.end(); iter++)
   {
      DOMElement* tagEl = *iter;
      XtString tagS(tagEl->getTagName());
      XtString listType = tagS.listType();
      XtString simpleType = tagS.simpleType();

      cFile << std::endl << "static ";

      XtString tagType;
      XtString repS(tagEl->getAttribute(X("maxOccurs")));
      int rep = (repS == "unbounded")? 9999 : atoi(S(repS));
      if (rep > 1)
      {
         tagType = listType;
      }
      else
      {
         tagType = simpleType;
      }
      XtString tagT(tagType);
      tagT.erase(tagT.rfind('_'));
      cFile << "int pack_" << tagT << "(XDR* xdrs, "
            << tagType << "* this1)"				<< std::endl
            << "{"						<< std::endl
            << "   int m=0;"					<< std::endl
            << "   unsigned int size=0;"			<< std::endl
            << "   off_t base,start,end;"			<< std::endl
            << "   base = xdr_getpos64(xdrs);"			<< std::endl
            << "   xdr_u_int(xdrs,&size);"			<< std::endl
            << "   start = xdr_getpos64(xdrs);"			<< std::endl
    								<< std::endl;
      if (rep > 1)
      {
         cFile << "   xdr_u_int(xdrs,&this1->mult);"		<< std::endl
               << "   for (m = 0; m < this1->mult; m++)"	<< std::endl
               << "   {"					<< std::endl;
      }
      else
      {
	 cFile << "   {"					<< std::endl;
      }

      DOMNamedNodeMap* attList = tagEl->getAttributes();
      for (int a = 0; a < attList->getLength(); a++)
      {
         DOMNode* att = attList->item(a);
         XtString typeS(att->getNodeValue());
         XtString nameS(att->getNodeName());
         XtString nameStr(nameS);
         if (rep > 1)
         {
            nameStr = "in[m]." + nameS;
         }
         if (typeS == "int")
         {
            cFile << "      xdr_int(xdrs,&this1->"
                  << nameStr << ");"				 << std::endl;
         }
         if (typeS == "long")
         {
            cFile << "#ifndef XDR_LONGLONG_MISSING"		 << std::endl
                  << "         xdr_longlong_t(xdrs,&this1->"
	          << nameStr << ");"				 << std::endl
                  << "#else"					 << std::endl
                  << "         {"				 << std::endl
                  << "            int* " << nameStr << "_ = "
                  << "(int*)&this1->" << nameStr << ";"		 << std::endl
                  << "# if __BIG_ENDIAN__"			 << std::endl
                  << "            xdr_int(xdrs,&"
                  << nameStr << "_[0]);"			 << std::endl
                  << "            xdr_int(xdrs,&"
                  << nameStr << "_[1]);"			 << std::endl
                  << "# else"					 << std::endl
                  << "            xdr_int(xdrs,&"
                  << nameStr << "_[1]);"			 << std::endl
                  << "            xdr_int(xdrs,&"
                  << nameStr << "_[0]);"			 << std::endl
                  << "# endif"					 << std::endl
                  << "         }"				 << std::endl
                  << "#endif"					 << std::endl;
         }
         else if (typeS == "float")
         {
            cFile << "      xdr_float(xdrs,&this1->"
                  << nameStr << ");"				 << std::endl;
         }
         else if (typeS == "double")
         {
            cFile << "      xdr_double(xdrs,&this1->" 
                  << nameStr << ");" 				 << std::endl;
         }
         else if (typeS == "boolean")
         {
            cFile << "      xdr_bool(xdrs,&this1->"
                  << nameStr << ");"				 << std::endl;
         }
         else if (typeS == "Particle_t")
         {
            cFile << "      xdr_int(xdrs,(int*)&this1->"
                  << nameStr << ");"  				 << std::endl;
         }
         else if (typeS == "string")
         {
            cFile << "      xdr_string(xdrs,&this1->" 
                  << nameStr << ", 1000000);"			 << std::endl;
            cFile << "      FREE(this1->" << nameStr << ");"	 << std::endl;
         }
         else if (typeS == "anyURI")
         {
            cFile << "      xdr_string(xdrs,&this1->" 
                  << nameStr << ", 1000000);" 			 << std::endl;
            cFile << "      FREE(this1->" << nameStr << ");"	 << std::endl;
         }
         else
         {
            /* ignore attributes with unrecognized values */
         }
      }

      DOMNodeList* contList = tagEl->getChildNodes();
      for (int c = 0; c < contList->getLength(); c++)
      {
         DOMNode* cont = contList->item(c);
         short type = cont->getNodeType();
         if (type == DOMNode::ELEMENT_NODE)
         {
            DOMElement* contEl = (DOMElement*) cont;
            XtString nameS(contEl->getTagName());
            XtString reS(contEl->getAttribute(X("maxOccurs")));
	    int re = (reS == "unbounded")? 9999 : atoi(S(reS));
            XtString contType;
            if (re > 1)
            {
               contType = nameS.listType();
            }
            else
            {
               contType = nameS.simpleType();
            }
            XtString contT(contType);
            contT.erase(contT.rfind('_'));
            cFile << "      if (this1->"
                  << ((rep > 1)? "in[m]." : "")
                  << ((re > 1)? nameS.plural(): nameS) << " != ("
                  << contType << "*)&hddm_nullTarget)"			<< std::endl
                  << "      {"						<< std::endl

                  << "         if (pack_" << contT << "(xdrs,this1->"
                  << ((rep > 1)? "in[m]." : "")
                  << ((re > 1)? nameS.plural() : nameS) << ") < 0) {"	<< std::endl
                  << "            return -1;"				<< std::endl
                  << "         }"					<< std::endl
                  << "      }"						<< std::endl
                  << "      else"					<< std::endl
                  << "      {"						<< std::endl
		  << "         int zero=0;"				<< std::endl
                  << "         xdr_int(xdrs,&zero);"			<< std::endl
                  << "      }"						<< std::endl;
         }
      }

      cFile << "   }"							<< std::endl
            << "   FREE(this1);"					<< std::endl
            << "   end = xdr_getpos64(xdrs);"				<< std::endl
            << "   xdr_setpos64(xdrs,base);"				<< std::endl
	    << "   size = end-start;"					<< std::endl
            << "   xdr_u_int(xdrs,&size);"				<< std::endl
            << "   xdr_setpos64(xdrs,end);"				<< std::endl
            << "   return size;"					<< std::endl
            << "}"							<< std::endl;
   }
}

/* Generate c functions for exporting c-structures onto a binary stream */
 
void CodeBuilder::constructFlushFunc(DOMElement* el)
{
   DOMElement* topEl = tagList[0];
   XtString topS(topEl->getTagName());
   XtString topType = topS.simpleType();
   XtString topT(topType);
   topT.erase(topT.rfind('_'));

   constructPackers();

   hFile 							<< std::endl
	 << "int flush_" << topT << "(" << topType << "* this1,"
	 << classPrefix << "_iostream_t* fp" << ");"		<< std::endl;

   cFile 							<< std::endl
	 << "int flush_" << topT << "(" << topType << "* this1,"
	 << classPrefix << "_iostream_t* fp" << ")"		<< std::endl
	 << "{"							<< std::endl
         << "   if (this1 == 0)"				<< std::endl
         << "   {"						<< std::endl
	 << "      return 0;"					<< std::endl
         << "   }"						<< std::endl
         << "   else if (fp == 0)"				<< std::endl
         << "   {"						<< std::endl
	 << "      XDR* xdrs = (XDR*)malloc(sizeof(XDR));"	<< std::endl
	 << "      int max_buffer_size = 1000000;"		<< std::endl
	 << "      char* dump = (char*)malloc(max_buffer_size);"<< std::endl
	 << "      xdrmem_create(xdrs,dump,max_buffer_size,XDR_ENCODE);"
	 							<< std::endl
	 << "      pack_" << topT << "(xdrs,this1);"		<< std::endl
	 << "      xdr_destroy(xdrs);"				<< std::endl
	 << "      free(xdrs);"					<< std::endl
	 << "      free(dump);"					<< std::endl
         << "   }"						<< std::endl
         << "   else if (fp->iomode == HDDM_STREAM_OUTPUT)"	<< std::endl
	 << "   {"						<< std::endl
	 << "      if (pack_" << topT << "(fp->xdrs,this1) < 0) {"
								<< std::endl
	 << "         fp->lerrno = errno;"			<< std::endl
	 << "         return -1;"				<< std::endl
	 << "      }"						<< std::endl
	 << "   }"						<< std::endl
	 << "   return 0;"					<< std::endl
	 << "}"							<< std::endl;
}

/* Generate c functions that match up corresponding elements between
 * the c structures and the data model that appears on the input
 * binary stream.  If successful, these routines build a hierarchical
 * structure (the "pop tree") that gives directions to the unpackers.
 *
 * The matching rules are as follows:
 *
 *  1) The attribute list for any given tag must be identical in content
 *     and order wherever it appears, otherwise there is a collision.
 *
 *  2) The content list for any given tag must be internally consistent
 *     within each model, but there are no requirements for agreement
 *     between the c-structures and the binary stream models.  Only the
 *     contents which appear in both models will be unpacked, however.
 */

void CodeBuilder::writeMatcher()
{
   cFile							<< std::endl
	 << "static int getTag(char* d, char* tag)"		<< std::endl
	 << "{"							<< std::endl
	 << "   int level;"					<< std::endl
	 << "   char* token;"					<< std::endl
	 << "   char line[500];"				<< std::endl
	 << "   strncpy(line,d,500);"				<< std::endl
	 << "   line[499] = 0;"					<< std::endl
	 << "   level = index(line,'<')-line;"			<< std::endl
	 << "   if (level < 500 &&"				<< std::endl
	 << "      (token = strtok(line+level+1,\" >\")))"	<< std::endl
	 << "   {"						<< std::endl
	 << "      strncpy(tag,token,500);"			<< std::endl
	 << "      return level/2;"				<< std::endl
	 << "   }"						<< std::endl
	 << "   return -1;"					<< std::endl
	 << "}"							<< std::endl
   								<< std::endl
	 << "static char* getEndTag(char* d, char* tag)"	<< std::endl
	 << "{"							<< std::endl
	 << "   char line[500];"				<< std::endl
	 << "   char endTag[510];"				<< std::endl
	 << "   strncpy(line,d,500);"				<< std::endl
	 << "   line[499] = 0;"					<< std::endl
         << "   if (strstr(strtok(line,\"\\n\"),\"/>\") == 0)"	<< std::endl
	 << "   {"						<< std::endl
	 << "      sprintf(endTag,\"</%s>\",tag);"		<< std::endl
	 << "   }"						<< std::endl
	 << "   else"						<< std::endl
	 << "   {"						<< std::endl
         << "      strcpy(endTag,\"/>\");"			<< std::endl
	 << "   }"						<< std::endl
	 << "   return strstr(d,endTag);"			<< std::endl
	 << "}"							<< std::endl
        							<< std::endl
         << "static void collide(char* b, char* c)"		<< std::endl
	 << "{"							<< std::endl
         << "   char btag[500];"				<< std::endl
         << "   getTag(b,btag);"				<< std::endl
         << "   b = index(b,'<');"				<< std::endl
         << "   c = index(c,'<');"				<< std::endl
         << "   *(index(b,'\\n')) = 0;"				<< std::endl
         << "   *(index(c,'\\n')) = 0;"				<< std::endl
         << "   fprintf(stderr,\"HDDM warning: \");"		<< std::endl
         << "   fprintf(stderr,\"tag %s in input file \", btag);" << std::endl
         << "   fprintf(stderr,\"does not match c header hddm_"
         << classPrefix << ".h\\n\");"				<< std::endl
         << "   fprintf(stderr,\"  input file: %s\\n\", b);"	<< std::endl
         << "   fprintf(stderr,\"  c header: %s\\n\", c);"	<< std::endl
         << "   fprintf(stderr,\"  === Tag %s will be ignored,\", btag);"
								<< std::endl
         << "   fprintf(stderr,\" rebuild to cure the problem ===\\n\");"
								<< std::endl
         << "   *(index(b,0)) = '\\n';"				<< std::endl
         << "   *(index(c,0)) = '\\n';"				<< std::endl
         << "}"							<< std::endl
								<< std::endl
	 << "static popNode* matches(char* b, char* c)"		<< std::endl
	 << "{"							<< std::endl
	 << "   char btag[500];"				<< std::endl
	 << "   char ctag[500];"				<< std::endl
	 << "   int blevel, clevel;"				<< std::endl
         << "   int ptrSeqNo = 0;"				<< std::endl
	 << "   blevel = getTag(b,btag);"			<< std::endl
	 << "   while ((clevel = getTag(c,ctag)) == blevel)"	<< std::endl
	 << "   {"						<< std::endl
	 << "      if "
	 << "((clevel == blevel) && (strcmp(ctag,btag) == 0))"	<< std::endl
	 << "      {"						<< std::endl
         << "         popNode* this1 = "
         << "(popNode*)malloc(sizeof(popNode));"		<< std::endl
	 << "         int len = index(c+1,'\\n') - c;"		<< std::endl
	 << "         if (strncmp(c,b,len) != 0)"		<< std::endl
	 << "         {"					<< std::endl
         << "            collide(b,c);"				<< std::endl
         << "            return 0;"				<< std::endl
	 << "         }"					<< std::endl;

   std::vector<DOMElement*>::iterator iter;
   for (iter = tagList.begin(); iter != tagList.end(); iter++)
   {
      XtString tagS((*iter)->getTagName());
      XtString repS((*iter)->getAttribute(X("maxOccurs")));
      int rep = (repS == "unbounded")? 9999 : atoi(S(repS));
      XtString tagType;
      if (rep > 1)
      {
         tagType = tagS.listType();
      }
      else
      {
         tagType = tagS.simpleType();
      }
      XtString tagT(tagType);
      tagT.erase(tagT.rfind('_'));

      cFile << "         else if "
            << "(strcmp(btag,\"" << tagS << "\") == 0)"		<< std::endl
            << "         {"					<< std::endl
	    << "            this1->unpacker = "
	    << "(void*(*)(XDR*,popNode*))"
            << "unpack_" << tagT << ";"				<< std::endl
            << "         }"					<< std::endl;
   }

   cFile << "         this1->inParent = ptrSeqNo;"		<< std::endl
         << "         this1->popListLength = 0;"		<< std::endl
	 << "         c = index(c+1,'\\n');"			<< std::endl
	 << "         b = index(b+1,'\\n');"			<< std::endl
	 << "         while (getTag(b,btag) > blevel)"		<< std::endl
	 << "         {"					<< std::endl
         << "            this1->popList[this1->popListLength++] = matches(b,c);"
								<< std::endl 
         << "            if (this1->popListLength > "
         << MAX_POPLIST_LENGTH << ")"				<< std::endl
         << "            {"					<< std::endl
         << "               fprintf(stderr,"
         << "\"hddm error - posList overflow.\\n\");"		<< std::endl
         << "               fprintf(stderr,"
         << "\"Increase MAX_POPLIST_LENGTH and recompile.\\n\");" << std::endl
         << "               exit(9);"				<< std::endl
         << "            }"					<< std::endl
	 << "            b = getEndTag(b,btag);"		<< std::endl
	 << "            b = index(b+1,'\\n');"			<< std::endl
	 << "         }"					<< std::endl
	 << "         return this1;"				<< std::endl
	 << "      }"						<< std::endl
	 << "      else"					<< std::endl
	 << "      {"						<< std::endl
	 << "         c = getEndTag(c,ctag);"			<< std::endl
	 << "         c = index(c+1,'\\n');"			<< std::endl
	 << "         ++ptrSeqNo;"				<< std::endl
	 << "      }"						<< std::endl
	 << "   }"						<< std::endl
	 << "   return 0;"					<< std::endl
	 << "}"							<< std::endl;
}

/* Generate c code to open a hddm file for reading */

void CodeBuilder::constructOpenFunc(DOMElement* el)
{
   XtString tagS(el->getTagName());
   XtString tagType = tagS.simpleType();
   XtString tagT(tagType);
   tagT.erase(tagT.rfind('_'));
   hFile							<< std::endl
	 << classPrefix << "_iostream_t* "
	 << "open_" << tagT << "(char* filename);"		<< std::endl;

   writeMatcher();

   cFile							<< std::endl
	 << classPrefix << "_iostream_t* "
	 << "open_" << tagT << "(char* filename)"		<< std::endl
	 << "{"							<< std::endl
	 << "   " << classPrefix << "_iostream_t* fp = "
	 << "(" << classPrefix << "_iostream_t*)"
	 << "malloc(sizeof(" << classPrefix << "_iostream_t));"	<< std::endl
	 << "   char* p;"					<< std::endl
	 << "   char* head;"					<< std::endl
         << "   if (filename)"					<< std::endl
         << "   {"						<< std::endl
	 << "      fp->fd = fopen(filename,\"r\");"		<< std::endl
         << "   }"						<< std::endl
         << "   else"						<< std::endl
         << "   {"						<< std::endl
	 << "      fp->fd = fdopen(0,\"r\");"			<< std::endl
         << "   }"						<< std::endl
	 << "   if (fp->fd == 0)"				<< std::endl
	 << "   {"						<< std::endl
	 << "      free(fp);"					<< std::endl
	 << "      return 0;"					<< std::endl
	 << "   }"						<< std::endl
	 << "   fp->iomode = HDDM_STREAM_INPUT;"		<< std::endl
	 << "   head = (char*)malloc(1000000);"			<< std::endl
	 << "   *head = 0;"					<< std::endl
	 << "   for (p = head;"					<< std::endl
	 << "        strstr(head,\"</HDDM>\") == 0;"		<< std::endl
	 << "        p += strlen(p))"				<< std::endl
	 << "   {"						<< std::endl
	 << "      if (p-head < 999000)"			<< std::endl
	 << "      {"						<< std::endl
	 << "         char *pbuf;"				<< std::endl
	 << "         pbuf = fgets(p,1000,fp->fd);"		<< std::endl
	 << "      }"						<< std::endl
	 << "      else"					<< std::endl
	 << "      {"						<< std::endl
	 << "         break;"					<< std::endl
	 << "      }"						<< std::endl
	 << "   }"						<< std::endl
	 << "   fp->popTop = matches(head,HDDM_" << classPrefix
	 << "_DocumentString);"					<< std::endl
	 << "   free(head);" 					<< std::endl
         << "   if (fp->popTop->popListLength == 0)"		<< std::endl
	 << "   {"						<< std::endl
	 << "      fprintf(stderr,\"HDDM Error: \");"		<< std::endl
	 << "      fprintf(stderr,\"input template model \");"	<< std::endl
	 << "      fprintf(stderr,\"does not match c header.\");"<< std::endl
	 << "      fprintf(stderr,\"  Please recompile.\\n\");"	<< std::endl
	 << "      exit(9);"					<< std::endl
	 << "   }"						<< std::endl
	 << "   fp->filename = "
         << "(char*)malloc(strlen(filename) + 1);"		<< std::endl
	 << "   strcpy(fp->filename,filename);"			<< std::endl
	 << "   fp->xdrs = (XDR*)malloc(sizeof(XDR));"		<< std::endl
         << "   xdrstdio_create(fp->xdrs,fp->fd,XDR_DECODE);"	<< std::endl
	 << "   return fp;"					<< std::endl
	 << "}"							<< std::endl;
}

/* Generate the c code to open a hddm file for writing */

void CodeBuilder::constructInitFunc(DOMElement* el)
{
   XtString tagS(el->getTagName());
   XtString tagType = tagS.simpleType();
   XtString tagT(tagType);
   tagT.erase(tagT.rfind('_'));
   hFile							<< std::endl
	 << classPrefix << "_iostream_t* "
	 << "init_" << tagT << "(char* filename);"		<< std::endl;
   cFile							<< std::endl
	 << classPrefix << "_iostream_t* "
	 << "init_" << tagT << "(char* filename)"		<< std::endl
	 << "{"							<< std::endl
	 << "   int len;"					<< std::endl	
	 << "   char* head;"					<< std::endl
	 << "   " << classPrefix << "_iostream_t* fp = "
	 << "(" << classPrefix << "_iostream_t*)"
	 << "malloc(sizeof(" << classPrefix << "_iostream_t));"	<< std::endl
         << "   if (filename)"					<< std::endl
         << "   {"						<< std::endl
	 << "      fp->fd = fopen(filename,\"w\");"		<< std::endl
         << "   }"						<< std::endl
         << "   else"						<< std::endl
         << "   {"						<< std::endl
	 << "      fp->fd = fdopen(1,\"w\");"			<< std::endl
         << "   }"						<< std::endl
	 << "   if (fp->fd == 0)"				<< std::endl
	 << "   {"						<< std::endl
	 << "      free(fp);"					<< std::endl
	 << "      return 0;"					<< std::endl
	 << "   }"						<< std::endl
	 << "   fp->iomode = HDDM_STREAM_OUTPUT;"		<< std::endl
	 << "   len = strlen(HDDM_" 
	 << classPrefix << "_DocumentString);"			<< std::endl
	 << "   head = (char*)malloc(len+1);"			<< std::endl
	 << "   strcpy(head,HDDM_"
	 << classPrefix << "_DocumentString);"			<< std::endl
	 << "   if (fwrite(head,1,len,fp->fd) != len)"		<< std::endl
	 << "   {"						<< std::endl
	 << "      fprintf(stderr,\"HDDM Error: \");"		<< std::endl
	 << "      fprintf(stderr,\"error writing to \");"	<< std::endl
	 << "      fprintf(stderr,\"output file %s\\n\",filename);" << std::endl
	 << "      exit(9);"					<< std::endl
	 << "   }"						<< std::endl
	 << "   fp->filename = "
         << "(char*)malloc(strlen(filename) + 1);"		<< std::endl
	 << "   strcpy(fp->filename,filename);"			<< std::endl
         << "   fp->popTop = 0;"				<< std::endl
	 << "   fp->xdrs = (XDR*)malloc(sizeof(XDR));"		<< std::endl
         << "   xdrstdio_create(fp->xdrs,fp->fd,XDR_ENCODE);"	<< std::endl
	 << "   free(head);"					<< std::endl
	 << "   return fp;"					<< std::endl
	 << "}"							<< std::endl;
}

/* Generate the c code to close an open hddm file */

void CodeBuilder::constructCloseFunc(DOMElement* el)
{
   XtString tagS(el->getTagName());
   XtString tagType = tagS.simpleType();
   XtString tagT(tagType);
   tagT.erase(tagT.rfind('_'));
   hFile							<< std::endl
	 << "void close_" << tagT << "("
	 << classPrefix << "_iostream_t* fp);"			<< std::endl;

   cFile							<< std::endl
         << "static void popaway(popNode* p)"			<< std::endl
         << "{"							<< std::endl
         << "   if (p)"						<< std::endl
         << "   {"						<< std::endl
         << "      int n;"					<< std::endl
         << "      for (n = 0; n < p->popListLength; n++)"	<< std::endl
         << "      {"						<< std::endl
         << "         popaway(p->popList[n]);"			<< std::endl
         << "      }"						<< std::endl
         << "      free(p);"					<< std::endl
         << "   }"						<< std::endl
         << "}"							<< std::endl
								<< std::endl
	 << "void close_" << tagT
	 << "(" << classPrefix << "_iostream_t* fp)"		<< std::endl
	 << "{"							<< std::endl
	 << "   xdr_destroy(fp->xdrs);"				<< std::endl	
	 << "   free(fp->xdrs);"				<< std::endl
	 << "   fclose(fp->fd);"				<< std::endl	
	 << "   free(fp->filename);"				<< std::endl	
         << "   popaway(fp->popTop);"				<< std::endl
	 << "   free(fp);"					<< std::endl	
	 << "}"							<< std::endl;
}

/* Generate the xml template in normal form and store in a string */

void CodeBuilder::constructDocument(DOMElement* el)
{
   static int indent = 0;
   cFile << "\"";
   for (int n = 0; n < indent; n++)
   {
      cFile << "  ";
   }
   
   XtString tagS(el->getTagName());
   cFile << "<" << tagS;
   DOMNamedNodeMap* attrList = el->getAttributes();
   int attrListLength = attrList->getLength();
   for (int a = 0; a < attrListLength; a++)
   {
      DOMNode* node = attrList->item(a);
      XtString nameS(node->getNodeName());
      XtString valueS(node->getNodeValue());
      cFile << " " << nameS << "=\\\"" << valueS << "\\\"";
   }

   DOMNodeList* contList = el->getChildNodes();
   int contListLength = contList->getLength();
   if (contListLength > 0)
   {
      cFile << ">\\n\"" << std::endl;
      indent++;
      for (int c = 0; c < contListLength; c++)
      {
         DOMNode* node = contList->item(c);
         if (node->getNodeType() == DOMNode::ELEMENT_NODE)
         {
            DOMElement* contEl = (DOMElement*) node;
            constructDocument(contEl);
         }
      }
      indent--;
      cFile << "\"";
      for (int n = 0; n < indent; n++)
      {
         cFile << "  ";
      }
      cFile << "</" << tagS << ">\\n\"" << std::endl;
   }
   else
   {
      cFile << " />\\n\"" << std::endl;
   }
}