// ---------------------------------------------------------------------------
// - XsmReader.cpp -
// - afnix:xml module - xsm reader class implementation -
// ---------------------------------------------------------------------------
// - This program is free software; you can redistribute it and/or modify -
// - it provided that this copyright notice is kept intact. -
// - -
// - This program is distributed in the hope that it will be useful, but -
// - without any warranty; without even the implied warranty of -
// - merchantability or fitness for a particular purpose. In no event shall -
// - the copyright holder be liable for any direct, indirect, incidental or -
// - special damages arising in any way out of the use of this software. -
// ---------------------------------------------------------------------------
// - copyright (c) 1999-2007 amaury darsch -
// ---------------------------------------------------------------------------
#include "Vector.hpp"
#include "Unicode.hpp"
#include "Runnable.hpp"
#include "XsmBuffer.hpp"
#include "XsmReader.hpp"
#include "QuarkZone.hpp"
#include "Exception.hpp"
#include "InputString.hpp"
namespace afnix {
// -------------------------------------------------------------------------
// - private section -
// -------------------------------------------------------------------------
// the xml special characters
static const t_quad XSM_CHAR_LT = 0x0000003C; // <
static const t_quad XSM_CHAR_GT = 0x0000003E; // >
static const t_quad XSM_CHAR_AM = 0x00000026; // &
static const t_quad XSM_CHAR_SC = 0x0000003B; // ;
static const t_quad XSM_CHAR_SL = 0x0000002F; // /
static const t_quad XSM_CHAR_EP = 0x00000021; // !
static const t_quad XSM_CHAR_LB = 0x0000005B; // [
static const t_quad XSM_CHAR_RB = 0x0000005D; // ]
static const t_quad XSM_CHAR_MN = 0x0000002D; // -
// this function parse a xml text node
static XsmNode* parse_xsm_txt (XsmBuffer& xbuf) {
// create a text node by string
XsmNode* node = new XsmNode (xbuf.tostring ());
// set the node line number
node->setlnum (xbuf.getlnum ());
// clear the buffer
xbuf.reset ();
// here is the node
return node;
}
// this function parse a xsm tag node
static XsmNode* parse_xsm_tag (XsmBuffer& xbuf) {
// create a tag node by string
XsmNode* node = new XsmNode (XsmNode::TAG_NODE, xbuf.tostring ());
// set the node line number
node->setlnum (xbuf.getlnum ());
// clear the buffer
xbuf.reset ();
// here is the node
return node;
}
// this function parse a xsm reference node
static XsmNode* parse_xsm_ref (XsmBuffer& xbuf) {
// create a reference node by name
XsmNode* node = new XsmNode (XsmNode::REF_NODE, xbuf.tostring ());
// set the node line number
node->setlnum (xbuf.getlnum ());
// clear the buffer
xbuf.reset ();
// here is the eref node
return node;
}
// this function parse a xsm end node
static XsmNode* parse_xsm_end (XsmBuffer& xbuf) {
// create a end node by name
XsmNode* node = new XsmNode (XsmNode::END_NODE, xbuf.tostring ());
// set the node line number
node->setlnum (xbuf.getlnum());
// clear the buffer
xbuf.reset ();
// here is the node
return node;
}
// this function parse a xsm node
static XsmNode* parse_xsm_node (XsoStream& xis) {
// create a xml buffer
XsmBuffer xbuf;
// state: s_helo
// get the first character for dispatch
s_helo:
t_quad c = xis.rduc ();
switch (c) {
case XSM_CHAR_LT:
xbuf.begin (xis.getlnum ());
goto s_stag;
case XSM_CHAR_AM:
xbuf.begin (xis.getlnum ());
goto s_sref;
case nilq:
case eofq:
return nilp;
default:
if (xbuf.isspc (c) == true) {
goto s_helo;
} else {
xbuf.begin (xis.getlnum ());
xbuf.add (c);
goto s_text;
}
}
// state: s_text
// read some text and accumulate
s_text:
c = xis.rduc ();
switch (c) {
case XSM_CHAR_LT:
case XSM_CHAR_AM:
case eofq:
xis.pushback (c);
return parse_xsm_txt (xbuf);
default:
xbuf.add (c);
goto s_text;
}
// state: s_stag
// start a tag with anything inside
s_stag:
c = xis.rduc ();
switch (c) {
case XSM_CHAR_GT:
return parse_xsm_tag (xbuf);
case XSM_CHAR_SL:
goto s_etag;
case XSM_CHAR_EP:
xbuf.add (c);
goto s_ptag;
case eofq:
goto s_error;
default:
xbuf.add (c);
goto s_ntag;
}
// state: s_ntag
// read a tag with anything inside
s_ntag:
c = xis.rduc ();
switch (c) {
case XSM_CHAR_GT:
return parse_xsm_tag (xbuf);
case eofq:
goto s_error;
default:
xbuf.add (c);
goto s_ntag;
}
// state: s_etag
// read an end tag
s_etag:
c = xis.rduc ();
switch (c) {
case XSM_CHAR_GT:
return parse_xsm_end (xbuf);
case eofq:
goto s_error;
default:
xbuf.add (c);
goto s_etag;
}
// state: s_ptag
// read a special tag with !
s_ptag:
c = xis.rduc ();
switch (c) {
case XSM_CHAR_LB:
xbuf.add (c);
goto s_btag;
case XSM_CHAR_MN:
xbuf.add (c);
goto s_bgcm;
case eofq:
goto s_error;
default:
xbuf.add (c);
goto s_ntag;
}
// state: s_btag
// read a bracket tag
s_btag:
c = xis.rduc ();
switch (c) {
case XSM_CHAR_RB:
xbuf.add (c);
goto s_rtag;
case eofq:
goto s_error;
default:
xbuf.add (c);
goto s_btag;
}
// state: s_rtag
// read a bracket tag end
s_rtag:
c = xis.rduc ();
switch (c) {
case XSM_CHAR_GT:
return parse_xsm_tag (xbuf);
case XSM_CHAR_RB:
xbuf.add (c);
goto s_rtag;
case eofq:
goto s_error;
default:
xbuf.add (c);
goto s_btag;
}
// state: s_sref
// start a reference node
s_sref:
c = xis.rduc ();
switch (c) {
case XSM_CHAR_SC:
return parse_xsm_ref (xbuf);
case XSM_CHAR_LT:
case eofq:
xbuf.pushback (XSM_CHAR_AM);
xis.pushback (c);
return parse_xsm_txt (xbuf);
default:
xbuf.add (c);
goto s_sref;
}
// state: s_bgcm
// begin a comment node
s_bgcm:
c = xis.rduc ();
switch (c) {
case XSM_CHAR_MN:
xbuf.add (c);
goto s_ctag;
case eofq:
goto s_error;
default:
xbuf.add (c);
goto s_ntag;
}
// state: s_ctag
// read a comment node
s_ctag:
c = xis.rduc ();
switch (c) {
case XSM_CHAR_MN:
xbuf.add (c);
goto s_encm;
case eofq:
goto s_error;
default:
xbuf.add (c);
goto s_ctag;
}
// state: s_encm
// start the end comment node
s_encm:
c = xis.rduc ();
switch (c) {
case XSM_CHAR_MN:
xbuf.add (c);
goto s_fncm;
case eofq:
goto s_error;
default:
xbuf.add (c);
goto s_ctag;
}
// state: s_fncm
// finish the end comment node
s_fncm:
c = xis.rduc ();
switch (c) {
case XSM_CHAR_GT:
return parse_xsm_tag (xbuf);
case eofq:
throw Exception ("xsm-error", "unterminated comment node");
default:
throw Exception ("xsm-error", "invalid -- sequence in comment node");
}
// state: s_error
// return a syntax error
s_error:
throw Exception ("xsm-error", "syntax error while reading node",
xbuf.tostring ());
}
// -------------------------------------------------------------------------
// - class section -
// -------------------------------------------------------------------------
// create an empty reader
XsmReader::XsmReader (void) {
p_xis = nilp;
}
// create a xsm reader by input stream
XsmReader::XsmReader (Input* is) {
p_xis = nilp;
setis (is);
}
// create a xsm reader by string
XsmReader::XsmReader (const String& xval) {
p_xis = nilp;
setis (xval);
}
// destroy this reader
XsmReader::~XsmReader (void) {
delete p_xis;
}
// return the document class name
String XsmReader::repr (void) const {
return "XsmReader";
}
// set the reader input stream
void XsmReader::setis (Input* is) {
wrlock ();
delete p_xis;
p_xis = new XsoStream (is);
unlock ();
}
// set the reader input stream by string
void XsmReader::setis (const String& xval) {
wrlock ();
delete p_xis;
p_xis = new XsoStream (new InputString (xval));
unlock ();
}
// get the next available node
XsmNode* XsmReader::getnode (void) {
wrlock ();
// check the input stream
if (p_xis == nilp) {
unlock ();
return nilp;
}
try {
// parse the xml stream
XsmNode* result = parse_xsm_node (*p_xis);
unlock ();
return result;
} catch (Exception& e) {
e.updlnum (p_xis->getlnum ());
unlock ();
throw e;
} catch (...) {
unlock ();
throw;
}
}
// -------------------------------------------------------------------------
// - object section -
// -------------------------------------------------------------------------
// the quark zone
static const long QUARK_ZONE_LENGTH = 2;
static QuarkZone zone (QUARK_ZONE_LENGTH);
// the object supported quarks
static const long QUARK_SETIS = zone.intern ("set-input-stream");
static const long QUARK_GETNODE = zone.intern ("get-node");
// create a new object in a generic way
Object* XsmReader::mknew (Vector* argv) {
long argc = (argv == nilp) ? 0 : argv->length ();
// check for 0 argument
if (argc == 0) return new XsmReader;
// check for 1 argument
if (argc == 1) {
Object* obj = argv->get (0);
// check for an input stream
Input* is = dynamic_cast <Input*> (obj);
if (is != nilp) return new XsmReader (is);
// check for a string
String* sobj = dynamic_cast <String*> (obj);
if (sobj != nilp) return new XsmReader (*sobj);
// invalid object
throw Exception ("type-error",
"invalid object with xsm reader constructor",
Object::repr (obj));
}
// argument error
throw Exception ("argument-error",
"too many argument with xsm reader constructor");
}
// return true if the given quark is defined
bool XsmReader::isquark (const long quark, const bool hflg) const {
rdlock ();
if (zone.exists (quark) == true) {
unlock ();
return true;
}
bool result = hflg ? Object::isquark (quark, hflg) : false;
unlock ();
return result;
}
// apply this object with a set of arguments and a quark
Object* XsmReader::apply (Runnable* robj, Nameset* nset, const long quark,
Vector* argv) {
// get the number of arguments
long argc = (argv == nilp) ? 0 : argv->length ();
// check for 0 argument
if (argc == 0) {
if (quark == QUARK_GETNODE) return getnode ();
}
// check for 1 argument
if (argc == 1) {
if (quark == QUARK_SETIS) {
Object* obj = argv->get (0);
if (obj == nilp) return nilp;
// check for an input stream
Input* is = dynamic_cast <Input*> (obj);
if (is != nilp) {
setis (is);
return nilp;
}
// check for a string
String* sobj = dynamic_cast <String*> (obj);
if (sobj != nilp) {
setis (*sobj);
return nilp;
}
throw Exception ("type-error", "invalid object with setis",
Object::repr (obj));
}
}
// call the object method
return Object::apply (robj, nset, quark, argv);
}
}
syntax highlighted by Code2HTML, v. 0.9.1