/***********************************************************************
*
*      Projet  : DilibPro
*      Module  : SxmlNode
*      Fichier : SxmlContainer.c
*      Auteur  : J. Ducloy
*
*      Derniere mise a jour Decembre 2001
*      $Id: SxmlContainer.c,v 1.5 2003/07/07 14:59:28 parmentf Exp $
*
************************************************************************
* 
*     Copyright (C) 2001 CNRS INIST
*
************************************************************************/
/**
   @file

   @brief Functions for SxmlContainer handling
   
   An SxmlContainer is an SxmlNode which type is @c XML_NODE_CONTAINER,
   and which subtype is one of:
   - XML_CONTAINER_INTEGER
   - XML_CONTAINER_STRING
   - XML_CONTAINER_REG_EXP
   - XML_CONTAINER_BUFFER
   - XML_CONTAINER_XML_FUNCTION
   - XML_CONTAINER_XML_NODE
   - XML_CONTAINER_OBJECT
   - XML_CONTAINER_STR_SEARCH_TABLE
   - XML_CONTAINER_XML_SEARCH_TABLE
   - XML_CONTAINER_BUFFER_PARSER

   @see SxmlNode

   @author &copy; 2001 INIST-CNRS
   @author Jacques DUCLOY
 */

#include "SxmlNode.h"
#include "BufferParser.h"
#include "StrSearch.h"
#include "StrDict.h"
#include "RegExp.h"
#include "Except.h"

/*  basic container (for system objects) */

/**
   Creates an empty SxmlContainer.
   
   The returned SxmlContainer is freed using SxmlFree.

   @param c1 sub-type
   @returns the created SxmlContainer
 */
SxmlNode *SxmlContainerCreate(char c1)
{
  SxmlNode *n1;

  n1=SxmlNodeCreate(XML_NODE_CONTAINER);
  n1->subType=c1;
  SxmlSetNodeName(n1,"#container");
  return n1;
}

/* ============================ SxmlContainerInteger ==== */

/**
   Creates an SxmlContainerInteger.

   Creates an SxmlContainer which type is @c XML_CONTAINER_INTEGER and
   sets its internal value to @a n1.

   @param n1 integer to be contained
   @return the created SxmlContainer
 */
SxmlNode *SxmlContainerIntegerCreate(int n1)
{
  SxmlNode *xc1;
  xc1=SxmlContainerCreate(XML_CONTAINER_INTEGER);
  SxmlSetNodeIntegerValue(xc1,n1);
  return xc1;
}

SxmlNode *SxmlContainerDoubleCreate(double r1)
{
  SxmlNode *xc1;
  xc1=SxmlContainerCreate(XML_CONTAINER_DOUBLE);
  SxmlSetNodeDoubleValue(xc1,r1);
  return xc1;
}

SxmlNode *SxmlLeafDoubleCreate(char *tag, double r1)
{
  SxmlNode *xLeaf;
  SxmlNode *d1;
  d1=SxmlContainerCreate(XML_CONTAINER_DOUBLE);
  SxmlSetNodeDoubleValue(d1,r1);
  xLeaf=SxmlElementCreate(tag);
  SxmlAppendChild(xLeaf,d1);
  return xLeaf;
}

double SxmlLeafDoubleValue(SxmlNode *dl1)
{
  SxmlNode *contDouble;
  if(!dl1)return 0;
  contDouble=SxmlFirstChild(dl1);
  return SxmlNodeDoubleValue(contDouble);
}

/* ============================ SxmlContainerString ==== */

/**
   Creates an SxmlContainerString.

   Creates an SxmlContainer which type is @c XML_CONTAINER_STRING and
   sets its internal value to @a s1.

   @param s1 string to be contained
   @return the created SxmlContainer   
 */
SxmlNode *SxmlContainerStringCreate(char *s1)
{
  SxmlNode *xc1;
  xc1=SxmlContainerCreate(XML_CONTAINER_STRING);
  SxmlSetNodeValue(xc1,s1);
  return xc1;
}
/* ============================ SxmlContainerRegExp ==== */

/**
   Creates an SxmlContainer which type is @c XML_CONTAINER_REG_EXP and
   sets its name to @a s1 and its value to the regular expression
   corresponding to @a s1.

   @param s1 regular expression to store in the SxmlContainer
   @returns the created SxmlContainer containing a RegExp
   @see RegExpCreate
 */
SxmlNode *SxmlContainerRegExpCreate(char *s1)
{
  SxmlNode *xc1;
  RegExp *re;
  xc1=SxmlContainerCreate(XML_CONTAINER_REG_EXP);
  SxmlSetNodeName(xc1,s1);
  re=RegExpCreate(s1);
  SxmlSetNodeValue(xc1,(char *)re);
  return xc1;
}

/* ============================ SxmlContainerString ==== */

/**
   Creates an SxmlContainer which type is @c XML_CONTAINER_BUFFER and sets
   its value to the Buffer created from @a s1, which will be freed by
   @ref SxmlContainerValueFree, or by SxmlFree.
   
   @param s1 the string to be stored in a Buffer
   @returns the SxmlContainer containing the Buffer @a s1.
   @see Buffer
 */
SxmlNode *SxmlContainerBufferCreate(char *s1)
{
  SxmlNode *xc1;
  Buffer *b1;
  xc1=SxmlContainerCreate(XML_CONTAINER_BUFFER);
  if(s1)b1=BufferFromString(s1);
  else b1=BufferCreate(10,10);
  SxmlSetNodeValue(xc1,(char *)b1);
  SxmlSetNodeValueToFree(xc1);
  return xc1;
}

/**
   Returns a pointer to the string stored in @a x1 using 
   @ref SxmlContainerBufferCreate. 

   @warning Once the SxmlContainer is freed, don't use the returned
   pointer again (it is freed too).

   @param x1 SxmlContainerBuffer containing a Buffer
   @return the string of the Buffer contained in @a x1
   @see BufferString
 */
char * SxmlContainerBufferString(SxmlNode *x1)
{
  Buffer *b1;
  b1=(Buffer *)SxmlNodeValue(x1);
  return BufferString(b1);
}

/**
   Creates an SxmlContainer which type is @c XML_CONTAINER_XML_FUNCTION
   and sets its value to the C function pointer @a f1.

   @param f1 C function with no args, and returning an @c SxmlNode*
   @returns the created SxmlContainer
 */
SxmlNode *SxmlContainerSxmlFunctionCreate( SxmlNode *(*f1)() )
{
  SxmlNode *xc1;
  xc1=SxmlContainerCreate(XML_CONTAINER_XML_FUNCTION);
  SxmlSetNodeValue(xc1,(char *)f1);
  return xc1;
}

/* ============================ SxmlNodePointer ==== */

/**
   Creates an SxmlContainer that operates as a pointer on given node @a
   n1.  This node is created with an implicit name @c #pointer, which
   could be modified, using SxmlSetNodeName.  If you want to create a
   "proprietary pointer" (whose value must be freed with pointer
   node), you must use SxmlSetNodeValueToFree to declare that.
   
   @param x1 SxmlNode pointer
   @returns the created SxmlNodePointer
   @see SxmlSetNodeName
 */
SxmlNode *SxmlNodePointerCreate(SxmlNode *x1)
{
  SxmlNode *xc1;
  xc1=SxmlContainerCreate(XML_CONTAINER_XML_NODE);
  SxmlSetNodeValue(xc1,(char *)x1);
  return xc1;
}

/* -------------- object container ---------*/

/**
   Associates functions name to the pointers to the functions.
 */
StrDict *SxmlContainerTypeTable = NULL;
/**
   Buffer used to create temporary strings.
 */
Buffer         *SxmlContainerBufName   = NULL;

/**
   Creates an SxmlContainer wich type is @c XML_CONTAINER_OBJECT and sets
   its custom value to @a s1.

   @param s1 name of the object
   @returns the SxmlContainerObject created.
 */
SxmlNode *SxmlContainerObjectCreate(char *s1)
{
  SxmlNode *x1;

  x1=SxmlContainerCreate(XML_CONTAINER_OBJECT);
  SxmlContainerSetObjectName(x1,s1);
  return x1;
}

/**
   Associates a "method" (that is to say, a C function) @a f1 to an
   object which name is @a n1.

   @param n1 name of the method
   @param f1 pointer to the C function

   @warning this function's name was mispelled
   "SxmlContainerObjetSetMethod", but you're advised to use
   SxmlContainerObjectSetMethod instead.
 */
void SxmlContainerObjetSetMethod(char *n1,
				SxmlNode *(*f1)())
{
  if(!SxmlContainerTypeTable)
    SxmlContainerTypeTable=StrDictCreate(10, 0);
  StrDictAddNewDatum(SxmlContainerTypeTable,n1,(char *)f1);
}

/* ---------------- */
/**
   Dumps the @a c1 SxmlContainer name, type and contents to the
   standard output.  The output is not a XML one (as the name of the
   SxmlContainer is followed by a "-->" to signal it).

   @param c1 SxmlContainer to dump
 */
void SxmlContainerDump(SxmlNode *c1)
{
  SxmlNode *x1;
  switch(SxmlNodeSubType(c1))
    {
    case XML_CONTAINER_XML_NODE:
      printf("<%s> -->",SxmlNodeName(c1));
      x1=(SxmlNode *)SxmlNodeValue(c1);
      if(x1)printf("<%s>", SxmlNodeName(x1));
      else printf("null");
      break;
    case XML_CONTAINER_STR_SEARCH_TABLE:
    case XML_CONTAINER_XML_SEARCH_TABLE:
      printf("<%s> -->",SxmlNodeName(c1));
      if(SxmlNodeValueToFree(c1))
	SxmlSearchTableDump(c1);
      break;
    case XML_CONTAINER_OBJECT:
      {
	SxmlNode *(*f1)();

	if(!SxmlContainerBufName)
	  SxmlContainerBufName=BufferCreate(10,10);
	BufferStrcpy(SxmlContainerBufName, SxmlContainerObjectName(c1));
	BufferStrcat(SxmlContainerBufName, ":dump");
	if ((SxmlContainerTypeTable)
	    &&(f1=(SxmlNode *(*)())StrDictSearch
	       (SxmlContainerTypeTable, BufferString(SxmlContainerBufName))))
	  f1(c1);
	else  printf("<%s #type=%s> -->",SxmlNodeName(c1),SxmlContainerObjectName(c1));
      }
      break;
    default:
      printf("<%s> -->",SxmlNodeName(c1));
    }
}

/**
   Frees the value contained by the SxmlContainer @a c1.  In the case
   of an SxmlContainerObject, a method which name is the name of the
   object followed by ":free" is looked up. If it is found, it is used
   to free the SxmlContainerObject.

   @param c1 SxmlContainer which value is to be freed
 */
void SxmlContainerValueFree(SxmlNode *c1)
{
  switch(SxmlNodeSubType(c1))
    {
    case XML_CONTAINER_BUFFER_PARSER:
      BufferParserFree((BufferParser *)SxmlNodeValue(c1));
      SxmlSetNodeValue(c1,NULL);
      return;
    case XML_CONTAINER_BUFFER:
      BufferFree((Buffer *)SxmlNodeValue(c1));
      SxmlSetNodeValue(c1,NULL);
      return;
    case XML_CONTAINER_REG_EXP:
      RegExpFree((RegExp *)SxmlNodeValue(c1));
      SxmlSetNodeValue(c1,NULL);
      free(SxmlNodeName(c1));
      SxmlSetNodeName(c1,NULL);
      return;
    case  XML_CONTAINER_XML_NODE:
      SxmlFree((SxmlNode *)SxmlNodeValue(c1));
      SxmlSetNodeValue(c1,NULL);
      return;

    case XML_CONTAINER_STR_SEARCH_TABLE:
    case XML_CONTAINER_XML_SEARCH_TABLE:
      {
	char *freeMode;
	StrSearchTable *t1;
	char *key;
	char *value;
	t1=(StrSearchTable *)SxmlNodeValue(c1);
	if(SxmlGetAttribute(c1,"freeMode"))
	  {
	    if (freeMode[1]=='y') /* WARNING: freeMode is not initialized!*/
	      {
		StrSearchIteratorReset(t1);
		while ((key=StrSearchNext(t1)))
		  {
		    if(SxmlNodeSubType(c1)==XML_CONTAINER_STR_SEARCH_TABLE)
		      free(value);
		    else SxmlFree((SxmlNode *)value);
		  }
	      }
	    if (freeMode[0]=='y')StrSearchTableFreeKey(t1);
	    else StrSearchTableFree(t1);
	  }
	else StrSearchTableFree(t1);
	SxmlSetNodeValue(c1,NULL);
      }
      return;
    case XML_CONTAINER_OBJECT:
      {
	SxmlNode *(*f1)();

	if(!SxmlContainerBufName)
	  SxmlContainerBufName=BufferCreate(10,10);
	BufferStrcpy(SxmlContainerBufName, SxmlContainerObjectName(c1));
	BufferStrcat(SxmlContainerBufName, ":free");
	/* If there exists in SxmlContainerTypeTable a fonction free
	   which name is the name of an object followed by ":free", it
	   is used to free this object. */
	if ((SxmlContainerTypeTable)
	    &&(f1=(SxmlNode *(*)())StrDictSearch
	       (SxmlContainerTypeTable, BufferString(SxmlContainerBufName))))
	  f1(c1);
      }
      break;
    default:
      ExceptSetError("SxmlContainer","NF","Bad container subtype or not yet implemented","","",2);
      
    }
}

/**
   Prints the @a c1 SxmlContainer into the @a f1 file, as a well-formed
   XML document (DTD-free).

   @param e1 the SxmlContainer to print
   @param f1 the file to print in
 */

void SxmlContainerFilePrint(SxmlNode *e1,
			   FILE *f1)
{
  SxmlNode *lAtt;
  Buffer *b1;
 switch(SxmlNodeSubType(e1))
    {
    case XML_CONTAINER_DOUBLE:
      fprintf(f1,"%4.5f", SxmlNodeDoubleValue(e1));
      return;
    default:
      break;
    }

  fprintf(f1,"<%s",  SxmlNodeName(e1));
  switch(SxmlNodeSubType(e1))
    {
    case XML_CONTAINER_XML_NODE:
      fprintf(f1," #iType=\"containerSxmlNode\"");
      break;
    }  
  if((lAtt=SxmlGetListAttributes(e1)))
    SxmlListAttributesFilePrint(lAtt,f1);
  fprintf(f1,">");
  switch(SxmlNodeSubType(e1))
    {
    case XML_CONTAINER_BUFFER:
      b1=(Buffer *)SxmlNodeValue(e1);
      fprintf(f1,"%s",BufferString(b1));
      break;
    }  
  fprintf(f1,"</%s>",  SxmlNodeName(e1));
}

void SxmlContainerFileSave(SxmlNode *e1,
			   FILE *f1)
{
  SxmlNode *lAtt;
  switch(SxmlNodeSubType(e1))
    {
    case XML_CONTAINER_DOUBLE:
      fprintf(f1,"<![double[%4.5f]]>", SxmlNodeDoubleValue(e1));
      break;
    }
}

void SxmlContainerIndent(SxmlNode *n1, Buffer *bufInd, int inc)
{
  switch(SxmlNodeSubType(n1))
    {
    case XML_CONTAINER_DOUBLE:
      printf("%s<![double[%4.5f]]>\n", BufferString(bufInd), SxmlNodeDoubleValue(n1));
      break;
    }
}
