/*
 * e4xmloutputprocessor.cpp --
 *
 *	This file contains the implementation of the e4_XMLOutputProcessor
 *	class defined in e4xml.h.
 *
 *	Authors: Jacob Levy and Jean-Claude Wippler.
 *		 jyl@best.com	jcw@equi4.com
 *
 *	Copyright: JYL Software, Inc., (c) 2000-2003.
 * 
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 * 
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE, EVEN IF
 * JYL SOFTWARE INC. IS MADE AWARE OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "e4xml.h"

/*
 ***************************************************************************
 *                                                                         *
 * Implementation of e4_XMLOutputProcessor class:                          *
 *                                                                         *
 ***************************************************************************
 */

/*
 * Default constructor
 */

e4_XMLOutputProcessor::e4_XMLOutputProcessor()
    : generator(NULL)
{
}

/*
 * Constructor with parser argument.
 */

e4_XMLOutputProcessor::e4_XMLOutputProcessor(e4_XMLGenerator *g)
    : generator(g)
{
}

/*
 * Destructor.
 */

e4_XMLOutputProcessor::~e4_XMLOutputProcessor()
{
    /*
     * Nothing to do.
     */
}

/*
 * Handle the start of output processing.
 */

bool
e4_XMLOutputProcessor::ProcessOutputBegin(const char *se,
					  e4_Node &sn,
					  bool firstTime)
{
    /*
     * Default implementation doesn't handle multi-invocation generation, so
     * return error if not first time.
     */

    return firstTime;
}

/*
 * Handle the end of output processing.
 */

bool
e4_XMLOutputProcessor::ProcessOutputEnd(bool firstTime)
{
    /*
     * Default implementation doesn't handle multi-invocation generation, so
     * return error if not first time.
     */

    return firstTime;
}

/*
 * Process a node which is a back reference to another node.
 */

bool
e4_XMLOutputProcessor::ProcessBackRefNode(const e4_Node &n, 
					  const char *elementName,
					  int nodeId,
					  int vertexUserData)
{
    *outstream << "<__nodebackref__";
    *outstream << " __nodeid__=\"" << nodeId << "\"";
    *outstream << " __name__=\"" << elementName << "\"";

    if ((vertexUserData != 0) && (generator->IsExportXML() == false)) {
	*outstream << " __vertexuserdata__=\"" << vertexUserData << "\"";
    }

    *outstream << "/>\n";

    return true;
}

/*
 * Process a normal node.
 *
 * On input, if this node has parents and could be part of a cyclic
 * graph, nodeId will be some value other than -1.
 */

bool
e4_XMLOutputProcessor::ProcessNodeBegin(const e4_Node &n,
					const char *elementName,
					int nodeId,
					int vertexUserData,
					e4_DString &dsAttrs,
					bool hasVertices)
{
    int nodeUserData;

    *outstream << "<" << elementName;

    /*
     * If exportPureXML == false, include the user data attached to this
     * node and to the vertex in the output.
     *
     * If exportPureXML == true, then suppress this part of the generated
     * XML so that it corresponds exactly to XML input that could have been
     * used to create this node.
     */

    if (!generator->IsExportXML()) {
	if (nodeId != -1) {
	    *outstream << " __nodeid__=\"" << nodeId << "\"";
	}
	n.GetUserData(nodeUserData);
	if (nodeUserData != 0) {
	    *outstream << " __nodeuserdata__=\"" << nodeUserData << "\"";
	}
	if (vertexUserData != 0) {
	    *outstream << " __vertexuserdata__=\"" << vertexUserData << "\"";
	}
    }

    /*
     * If there are attributes to output for this node then produce them
     * on the output stream.
     */

    if (dsAttrs.Length()) {
	*outstream << dsAttrs.Get();
    }

    /*
     * Finally, if there are no vertices that follow, generate an empty
     * XML element.
     */

    if (!hasVertices) {
	*outstream << "/";
    }

    *outstream << ">\n";

    return (true);
}

/*
 * Produce the node-end bracket in the output.
 */

bool
e4_XMLOutputProcessor::ProcessNodeEnd(const e4_Node &n, 
				      const char *elementName,
				      bool hasVertices)
{
    /*
     * If this node had vertices generated inside it, close off the node's
     * wrapping element.
     */

    if (hasVertices) {
	*outstream << "</" << elementName << ">\n";
    }

    return (true);
}

/*
 * Process a vertex.
 */

bool
e4_XMLOutputProcessor::ProcessVertex(const e4_Vertex& v)
{
    int vi, nbytes, ud;
    double vf;
    const void* bytes;
    const char* s;
    char *base64str = NULL;

    /*
     * Generate vertex element and mandatory vertex name.
     */

    *outstream << "<__vertex__ name=\"" << v.Name() << "\"";

    /*
     * Produce the type and value attributes.
     */

    switch (v.Type()) {
    case E4_VTNODE:
	/*
	 * Should not occur!
	 */

	break;
    case E4_VTINT:
	(void) v.Get(vi);
	*outstream << " type=\"int\" value=\"" << vi << "\"";
	break;
    case E4_VTDOUBLE:
	(void) v.Get(vf);
	*outstream << " type=\"double\" value=\"" << vf << "\"";
	break;
    case E4_VTSTRING:
	(void) v.Get(s);
	*outstream << " type=\"string\" length=\"";
	*outstream << (int) strlen(s) << "\"";
	break;
    case E4_VTBINARY:
	(void) v.Get(bytes, nbytes);
	base64str = base64_encode((byte *) bytes, nbytes);
	*outstream << " type=\"binary\" length=\"";
	*outstream << (int) strlen(base64str) << "\"";
	break;
    default:
	/*
	 * Should not occur!
	 */

	break;
    }

    if (generator->IsExportXML() == false) {
	(void) v.GetUserData(ud);
	if (ud != 0) {
	    *outstream << " __vertexuserdata__=\"" << ud << "\"";
	}
    }

    /*
     * If we are producing a string or binary value output, produce
     * the actual value as PCDATA.
     */

    if (v.Type() == E4_VTSTRING) {
	*outstream << ">" << s << "</__vertex__>\n";
    } else if (v.Type() == E4_VTBINARY) {
	*outstream << ">" << base64str << "</__vertex__>\n";
    } else {
	*outstream << "/>\n";
    }

    /*
     * Free the buffer returned by base64_encode.
     */

    if (base64str != NULL) {
	free(base64str);
    }

    return true;
}

/*
 * Process a comment in the output stream.
 */

bool
e4_XMLOutputProcessor::ProcessComment(const char *comment)
{
    *outstream << "<-- " << comment << " -->\n";
    return true;
}

/*
 * Process character data in the output stream.
 */

bool
e4_XMLOutputProcessor::ProcessCharData(const char *data)
{
    *outstream << data << '\n';
    return true;
}

/*
 * Process CDATA in the output stream.
 */

bool
e4_XMLOutputProcessor::ProcessCDATA(const char *data)
{
    *outstream << "<![CDATA[" << data << "]]>\n";
    return true;
}

/*
 * Process a processing instruction in the output stream.
 */

bool
e4_XMLOutputProcessor::ProcessInstructions(const char *target,
					   const char *data)
{
    *outstream << "<?" << target << " " << data << " ?>\n";
    return true;
}

/*
 * Process an XML declaration in the output stream.
 */

bool
e4_XMLOutputProcessor::ProcessXMLDeclaration(const char *version,
					     const char *encoding,
					     int standalone)
{
    *outstream << "<?xml ";

    if (version && *version) {
	*outstream << " version=\"" << version << "\"";
    }
    if (encoding && *encoding) {
	*outstream << " encoding=\"" << encoding << "\"";
    }
    if (standalone != -1) {
	*outstream << " standalone=\""
		   << ((standalone == 0) ? "no" : "yes")
		   << "\"";
    }

    *outstream << "?>\n";

    return true;
}

/*
 * Process the begin bracket for a DTD declaration.
 */

bool
e4_XMLOutputProcessor::ProcessDTDBegin(const char *doctypename,
				       const char *sysid,
				       const char *pubid,
				       int hasinternalsubset)
{
     *outstream << "<!DOCTYPE " << doctypename;

     if ((sysid != NULL) && (*sysid != '\0')) {
	*outstream << " " << sysid;
     }

     if ((pubid != NULL) && (*pubid != '\0')) {
	*outstream << " \"" << pubid << "\"";
     }

     /*
      * What to do with hasinternalsubset?
      */

     *outstream << " [\n";

     return true;
}

/*
 * Process the end of a DTD declaration.
 */

bool
e4_XMLOutputProcessor::ProcessDTDEnd()
{
    *outstream << "]>\n";
    return true;
}

/*
 * Produce output for an unparsed entity declaration.
 */

bool
e4_XMLOutputProcessor::ProcessUnparsedEntity(const char *entityname,
					     const char *base,
					     const char *systemid,
					     const char *publicid,
					     const char *notationname)
{
    /*
     * Since I don't really know what to produce, just ignore and
     * return false to indicate we failed.
     */

    return false;
}

/*
 * Produce output for a skipped entity.
 */

bool
e4_XMLOutputProcessor::ProcessSkippedEntity(const char *entityname,
					    int isparameterentity)
{
    /*
     * Since I don't really know what to produce, just ignore and
     * return false to indicate we failed.
     */

    return false;
}

/*
 * Produce output for a notation declaration
 */

bool
e4_XMLOutputProcessor::ProcessNotationDecl(const char *notationname,
					   const char *base,
					   const char *systemid,
					   const char *publicid)
{
    /*
     * Since I don't really know what to produce, just ignore and
     * return false to indicate we failed.
     */

    return false;
}
