domstream.cpp revision 31c0d56f2ec3810c7917d23ebaba028dcee1f118
/**
* Phoebe DOM Implementation.
*
* This is a C++ approximation of the W3C DOM model, which follows
* fairly closely the specifications in the various .idl files, copies of
* which are provided for reference. Most important is this one:
*
* http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/idl-definitions.html
*
* Authors:
* Bob Jamison
*
* Copyright (C) 2006 Bob Jamison
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* Our base input/output stream classes. These are is directly
* inherited from iostreams, and includes any extra
* functionality that we might need.
*
*/
#include <math.h>
#include <stdarg.h>
#include "domstream.h"
#include "dom/charclass.h"
namespace org
{
namespace w3c
{
namespace dom
{
namespace io
{
//#########################################################################
//# U T I L I T Y
//#########################################################################
void pipeStream(InputStream &source, OutputStream &dest)
{
for (;;)
{
int ch = source.get();
if (ch<0)
break;
dest.put(ch);
}
dest.flush();
}
//#########################################################################
//# F O R M A T T E D P R I N T I N G
//#########################################################################
static char *digits = "0123456789abcdefghijklmnopqrstuvwxyz";
static int dprintInt(Writer &outs,
long arg, int base,
int flag, int width, int precision)
{
DOMString buf;
//### Get the digits
while (arg > 0)
{
int ch = arg % base;
buf.insert(buf.begin(), digits[ch]);
arg /= base;
}
if (flag == '#' && base == 16)
{
buf.insert(buf.begin(), 'x');
buf.insert(buf.begin(), '0');
}
if (buf.size() == 0)
buf = "0";
int pad = width - (int)buf.size();
for (int i=0 ; i<pad ; i++)
buf.insert(buf.begin(), '0');
//### Output the result
for (unsigned int i=0 ; i<buf.size() ; i++)
{
if (outs.put(buf[i]) < 0)
return -1;
}
return 1;
}
static int dprintDouble(Writer &outs, double val,
int flag, int width, int precision)
{
DOMString buf;
//printf("int:%f frac:%f\n", intPart, fracPart);
bool negative = false;
if (val < 0)
{
negative = true;
val = -val;
}
int intDigits = 0;
double scale = 1.0;
while (scale < val)
{
intDigits++;
scale *= 10.0;
}
double intPart;
double fracPart = modf(val, &intPart);
if (precision <= 0)
precision = 5;
//### How many pad digits?
int pad = width - intDigits;
if (precision > 0)
pad -= precision + 1;
else if (flag == '#')
pad--;
//### Signs
if (negative)
buf.push_back('-');
else if (flag == '+')
buf.push_back('+');
//### Prefix pad
if (pad > 0 && flag == '0')
{
while (pad--)
buf.push_back('0');
}
//### Integer digits
intPart = (intPart + 0.1 ) / scale; // turn 12345.678 to .12345678
while (intDigits--)
{
intPart *= 10.0;
double dig;
intPart = modf(intPart, &dig);
char ch = '0' + (int)dig;
buf.push_back(ch);
}
if (buf.size() == 0)
buf = "0";
//### Decimal point
if (flag == '#' || precision > 0)
{
buf.push_back('.');
}
//### Fractional digits
while (precision--)
{
fracPart *= 10.0;
double dig;
fracPart = modf(fracPart, &dig);
char ch = '0' + (int)dig;
buf.push_back(ch);
}
//### Left justify if requested
if (pad > 0 && flag == '-')
{
while (pad--)
buf.push_back(' ');
}
//### Output the result
for (unsigned int i=0 ; i<buf.size() ; i++)
{
if (outs.put(buf[i]) < 0)
return -1;
}
return 1;
}
/**
* Output a string. We veer from the standard a tiny bit.
* Normally, a flag of '#' is undefined for strings. We use
* it as an indicator that the user wants to XML-escape any
* XML entities.
*/
static int dprintString(Writer &outs, const DOMString &str,
int flags, int width, int precision)
{
int len = str.size();
if (flags == '#')
{
for (int pos = 0; pos < len; pos++)
{
XMLCh ch = (XMLCh) str[pos];
if (ch == '&')
outs.writeString("&ampr;");
else if (ch == '<')
outs.writeString("&lt;");
else if (ch == '>')
outs.writeString("&gt;");
else if (ch == '"')
outs.writeString("&quot;");
else if (ch == '\'')
outs.writeString("&apos;");
else
outs.put(ch);
}
}
else
{
outs.writeString(str);
}
return 1;
}
static int getint(const DOMString &buf, int pos, int *ret)
{
int len = buf.size();
if (!len)
{
*ret = 0;
return pos;
}
bool has_sign = false;
int val = 0;
if (buf[pos] == '-')
{
has_sign = true;
pos++;
}
while (pos < len)
{
XMLCh ch = buf[pos];
if (ch >= '0' && ch <= '9')
val = val * 10 + (ch - '0');
else
break;
pos++;
}
if (has_sign)
val = -val;
*ret = val;
return pos;
}
static int dprintf(Writer &outs, const DOMString &fmt, va_list ap)
{
int len = fmt.size();
for (int pos=0 ; pos < len ; pos++)
{
XMLCh ch = fmt[pos];
//## normal character
if (ch != '%')
{
if (outs.put(ch)<0)
{
return -1;
}
continue;
}
if (++pos >= len)
{
return -1;
}
ch = fmt[pos];
//## is this %% ?
if (ch == '%') // escaped '%'
{
if (outs.put('%')<0)
{
return -1;
}
continue;
}
//## flag
char flag = '\0';
if (ch == '-' || ch == '+' || ch == ' ' ||
ch == '#' || ch == '0')
{
flag = ch;
if (++pos >= len)
{
return -1;
}
ch = fmt[pos];
}
//## width.precision
int width = 0;
int precision = 0;
pos = getint(fmt, pos, &width);
if (pos >= len)
{
return -1;
}
ch = fmt[pos];
if (ch == '.')
{
if (++pos >= len)
{
return -1;
}
pos = getint(fmt, pos, &precision);
if (pos >= len)
{
return -1;
}
ch = fmt[pos];
}
//## length
char length = '\0';
if (ch == 'l' || ch == 'h')
{
length = ch;
if (++pos >= len)
{
return -1;
}
ch = fmt[pos];
}
//## data type
switch (ch)
{
case 'f':
case 'g':
{
double val = va_arg(ap, double);
dprintDouble(outs, val, flag, width, precision);
break;
}
case 'd':
{
long val = 0;
if (length == 'l')
val = va_arg(ap, long);
else if (length == 'h')
val = (long)va_arg(ap, int);
else
val = (long)va_arg(ap, int);
dprintInt(outs, val, 10, flag, width, precision);
break;
}
case 'x':
{
long val = 0;
if (length == 'l')
val = va_arg(ap, long);
else if (length == 'h')
val = (long)va_arg(ap, int);
else
val = (long)va_arg(ap, int);
dprintInt(outs, val, 16, flag, width, precision);
break;
}
case 's':
{
DOMString val = va_arg(ap, char *);
dprintString(outs, val, flag, width, precision);
break;
}
default:
{
break;
}
}
}
return 1;
}
//#########################################################################
//# B A S I C I N P U T S T R E A M
//#########################################################################
/**
*
*/
BasicInputStream::BasicInputStream(const InputStream &sourceStream)
: source((InputStream &)sourceStream)
{
closed = false;
}
/**
* Returns the number of bytes that can be read (or skipped over) from
* this input stream without blocking by the next caller of a method for
* this input stream.
*/
int BasicInputStream::available()
{
if (closed)
return 0;
return source.available();
}
/**
* Closes this input stream and releases any system resources
* associated with the stream.
*/
void BasicInputStream::close()
{
if (closed)
return;
source.close();
closed = true;
}
/**
* Reads the next byte of data from the input stream. -1 if EOF
*/
int BasicInputStream::get()
{
if (closed)
return -1;
return source.get();
}
//#########################################################################
//# B A S I C O U T P U T S T R E A M
//#########################################################################
/**
*
*/
BasicOutputStream::BasicOutputStream(const OutputStream &destinationStream)
: destination((OutputStream &)destinationStream)
{
closed = false;
}
/**
* Closes this output stream and releases any system resources
* associated with this stream.
*/
void BasicOutputStream::close()
{
if (closed)
return;
destination.close();
closed = true;
}
/**
* Flushes this output stream and forces any buffered output
* bytes to be written out.
*/
void BasicOutputStream::flush()
{
if (closed)
return;
destination.flush();
}
/**
* Writes the specified byte to this output stream.
*/
int BasicOutputStream::put(XMLCh ch)
{
if (closed)
return -1;
if (destination.put(ch) < 0)
return -1;
return 1;
}
//#########################################################################
//# B A S I C R E A D E R
//#########################################################################
/**
*
*/
BasicReader::BasicReader(Reader &sourceReader)
{
source = &sourceReader;
}
/**
* Returns the number of bytes that can be read (or skipped over) from
* this reader without blocking by the next caller of a method for
* this reader.
*/
int BasicReader::available()
{
if (source)
return source->available();
else
return 0;
}
/**
* Closes this reader and releases any system resources
* associated with the reader.
*/
void BasicReader::close()
{
if (source)
source->close();
}
/**
* Reads the next byte of data from the reader.
*/
int BasicReader::get()
{
if (source)
return source->get();
else
return -1;
}
/**
* Reads a line of data from the reader.
*/
DOMString BasicReader::readLine()
{
DOMString str;
while (available() > 0)
{
XMLCh ch = get();
if (ch == '\n')
break;
str.push_back(ch);
}
return str;
}
/**
* Reads a line of data from the reader.
*/
DOMString BasicReader::readWord()
{
DOMString str;
while (available() > 0)
{
XMLCh ch = get();
if (isWhitespace(ch))
break;
str.push_back(ch);
}
return str;
}
static bool getLong(DOMString &str, long *val)
{
const char *begin = str.c_str();
char *end;
long ival = strtol(begin, &end, 10);
if (str == end)
return false;
*val = ival;
return true;
}
static bool getULong(const DOMString &str, unsigned long *val)
{
DOMString tmp = str;
char *begin = (char *)tmp.c_str();
char *end;
unsigned long ival = strtoul(begin, &end, 10);
if (begin == end)
return false;
*val = ival;
return true;
}
static bool getDouble(const DOMString &str, double *val)
{
DOMString tmp = str;
const char *begin = tmp.c_str();
char *end;
double ival = strtod(begin, &end);
if (begin == end)
return false;
*val = ival;
return true;
}
/**
*
*/
Reader &BasicReader::readBool (bool& val )
{
DOMString buf = readWord();
if (buf == "true")
val = true;
else
val = false;
return *this;
}
/**
*
*/
Reader &BasicReader::readShort (short& val )
{
DOMString buf = readWord();
long ival;
if (getLong(buf, &ival))
val = (short) ival;
return *this;
}
/**
*
*/
Reader &BasicReader::readUnsignedShort (unsigned short& val )
{
DOMString buf = readWord();
unsigned long ival;
if (getULong(buf, &ival))
val = (unsigned short) ival;
return *this;
}
/**
*
*/
Reader &BasicReader::readInt (int& val )
{
DOMString buf = readWord();
long ival;
if (getLong(buf, &ival))
val = (int) ival;
return *this;
}
/**
*
*/
Reader &BasicReader::readUnsignedInt (unsigned int& val )
{
DOMString buf = readWord();
unsigned long ival;
if (getULong(buf, &ival))
val = (unsigned int) ival;
return *this;
}
/**
*
*/
Reader &BasicReader::readLong (long& val )
{
DOMString buf = readWord();
long ival;
if (getLong(buf, &ival))
val = ival;
return *this;
}
/**
*
*/
Reader &BasicReader::readUnsignedLong (unsigned long& val )
{
DOMString buf = readWord();
unsigned long ival;
if (getULong(buf, &ival))
val = ival;
return *this;
}
/**
*
*/
Reader &BasicReader::readFloat (float& val )
{
DOMString buf = readWord();
double ival;
if (getDouble(buf, &ival))
val = (float)ival;
return *this;
}
/**
*
*/
Reader &BasicReader::readDouble (double& val )
{
DOMString buf = readWord();
double ival;
if (getDouble(buf, &ival))
val = ival;
return *this;
}
//#########################################################################
//# I N P U T S T R E A M R E A D E R
//#########################################################################
InputStreamReader::InputStreamReader(const InputStream &inputStreamSource)
: inputStream((InputStream &)inputStreamSource)
{
}
/**
* Close the underlying OutputStream
*/
void InputStreamReader::close()
{
inputStream.close();
}
/**
* Flush the underlying OutputStream
*/
int InputStreamReader::available()
{
return inputStream.available();
}
/**
* Overloaded to receive its bytes from an InputStream
* rather than a Reader
*/
int InputStreamReader::get()
{
//Do we need conversions here?
int ch = (XMLCh)inputStream.get();
return ch;
}
//#########################################################################
//# S T D R E A D E R
//#########################################################################
/**
*
*/
StdReader::StdReader()
{
inputStream = new StdInputStream();
}
/**
*
*/
StdReader::~StdReader()
{
delete inputStream;
}
/**
* Close the underlying OutputStream
*/
void StdReader::close()
{
inputStream->close();
}
/**
* Flush the underlying OutputStream
*/
int StdReader::available()
{
return inputStream->available();
}
/**
* Overloaded to receive its bytes from an InputStream
* rather than a Reader
*/
int StdReader::get()
{
//Do we need conversions here?
XMLCh ch = (XMLCh)inputStream->get();
return ch;
}
//#########################################################################
//# B A S I C W R I T E R
//#########################################################################
/**
*
*/
BasicWriter::BasicWriter(const Writer &destinationWriter)
{
destination = (Writer *)&destinationWriter;
}
/**
* Closes this writer and releases any system resources
* associated with this writer.
*/
void BasicWriter::close()
{
if (destination)
destination->close();
}
/**
* Flushes this output stream and forces any buffered output
* bytes to be written out.
*/
void BasicWriter::flush()
{
if (destination)
destination->flush();
}
/**
* Writes the specified byte to this output writer.
*/
int BasicWriter::put(XMLCh ch)
{
if (destination && destination->put(ch)>=0)
return 1;
return -1;
}
/**
* Provide printf()-like formatting
*/
/*
Writer &BasicWriter::printf(char const *fmt, ...)
{
va_list args;
va_start(args, fmt);
//replace this wish vsnprintf()
vsnprintf(formatBuf, 2047, fmt, args);
va_end(args);
writeString(formatBuf);
return *this;
}
*/
Writer &BasicWriter::printf(const DOMString &fmt, ...)
{
va_list args;
va_start(args, fmt);
dprintf(*this, fmt, args);
return *this;
}
/**
* Writes the specified character to this output writer.
*/
Writer &BasicWriter::writeChar(char ch)
{
XMLCh uch = ch;
put(uch);
return *this;
}
/**
* Writes the specified standard string to this output writer.
*/
Writer &BasicWriter::writeString(const DOMString &str)
{
for (int i=0; i< (int)str.size(); i++)
put(str[i]);
return *this;
}
/**
*
*/
Writer &BasicWriter::writeBool (bool val )
{
if (val)
writeString("true");
else
writeString("false");
return *this;
}
/**
*
*/
Writer &BasicWriter::writeShort (short val )
{
char buf[32];
snprintf(buf, 31, "%d", val);
writeString(buf);
return *this;
}
/**
*
*/
Writer &BasicWriter::writeUnsignedShort (unsigned short val )
{
char buf[32];
snprintf(buf, 31, "%u", val);
writeString(buf);
return *this;
}
/**
*
*/
Writer &BasicWriter::writeInt (int val)
{
char buf[32];
snprintf(buf, 31, "%d", val);
writeString(buf);
return *this;
}
/**
*
*/
Writer &BasicWriter::writeUnsignedInt (unsigned int val)
{
char buf[32];
snprintf(buf, 31, "%u", val);
writeString(buf);
return *this;
}
/**
*
*/
Writer &BasicWriter::writeLong (long val)
{
char buf[32];
snprintf(buf, 31, "%ld", val);
writeString(buf);
return *this;
}
/**
*
*/
Writer &BasicWriter::writeUnsignedLong(unsigned long val)
{
char buf[32];
snprintf(buf, 31, "%lu", val);
writeString(buf);
return *this;
}
/**
*
*/
Writer &BasicWriter::writeFloat(float val)
{
char buf[32];
snprintf(buf, 31, "%8.3f", val);
writeString(buf);
return *this;
}
/**
*
*/
Writer &BasicWriter::writeDouble(double val)
{
char buf[32];
snprintf(buf, 31, "%8.3f", val);
writeString(buf);
return *this;
}
//#########################################################################
//# O U T P U T S T R E A M W R I T E R
//#########################################################################
OutputStreamWriter::OutputStreamWriter(OutputStream &outputStreamDest)
: outputStream(outputStreamDest)
{
}
/**
* Close the underlying OutputStream
*/
void OutputStreamWriter::close()
{
flush();
outputStream.close();
}
/**
* Flush the underlying OutputStream
*/
void OutputStreamWriter::flush()
{
outputStream.flush();
}
/**
* Overloaded to redirect the output chars from the next Writer
* in the chain to an OutputStream instead.
*/
int OutputStreamWriter::put(XMLCh ch)
{
//Do we need conversions here?
int intCh = (int) ch;
if (outputStream.put(intCh) < 0)
return -1;
return 1;
}
//#########################################################################
//# S T D W R I T E R
//#########################################################################
/**
*
*/
StdWriter::StdWriter()
{
outputStream = new StdOutputStream();
}
/**
*
*/
StdWriter::~StdWriter()
{
delete outputStream;
}
/**
* Close the underlying OutputStream
*/
void StdWriter::close()
{
flush();
outputStream->close();
}
/**
* Flush the underlying OutputStream
*/
void StdWriter::flush()
{
outputStream->flush();
}
/**
* Overloaded to redirect the output chars from the next Writer
* in the chain to an OutputStream instead.
*/
int StdWriter::put(XMLCh ch)
{
//Do we need conversions here?
int intCh = (int) ch;
if (outputStream->put(intCh) < 0)
return -1;
return 1;
}
//###############################################
//# O P E R A T O R S
//###############################################
//# Normally these would be in the .h, but we
//# just want to be absolutely certain that these
//# are never multiply defined. Easy to maintain,
//# though. Just occasionally copy/paste these
//# into the .h , and replace the {} with a ;
//###############################################
Reader& operator>> (Reader &reader, bool& val )
{ return reader.readBool(val); }
Reader& operator>> (Reader &reader, short &val)
{ return reader.readShort(val); }
Reader& operator>> (Reader &reader, unsigned short &val)
{ return reader.readUnsignedShort(val); }
Reader& operator>> (Reader &reader, int &val)
{ return reader.readInt(val); }
Reader& operator>> (Reader &reader, unsigned int &val)
{ return reader.readUnsignedInt(val); }
Reader& operator>> (Reader &reader, long &val)
{ return reader.readLong(val); }
Reader& operator>> (Reader &reader, unsigned long &val)
{ return reader.readUnsignedLong(val); }
Reader& operator>> (Reader &reader, float &val)
{ return reader.readFloat(val); }
Reader& operator>> (Reader &reader, double &val)
{ return reader.readDouble(val); }
Writer& operator<< (Writer &writer, char val)
{ return writer.writeChar(val); }
Writer& operator<< (Writer &writer, const DOMString &val)
{ return writer.writeString(val); }
Writer& operator<< (Writer &writer, bool val)
{ return writer.writeBool(val); }
Writer& operator<< (Writer &writer, short val)
{ return writer.writeShort(val); }
Writer& operator<< (Writer &writer, unsigned short val)
{ return writer.writeUnsignedShort(val); }
Writer& operator<< (Writer &writer, int val)
{ return writer.writeInt(val); }
Writer& operator<< (Writer &writer, unsigned int val)
{ return writer.writeUnsignedInt(val); }
Writer& operator<< (Writer &writer, long val)
{ return writer.writeLong(val); }
Writer& operator<< (Writer &writer, unsigned long val)
{ return writer.writeUnsignedLong(val); }
Writer& operator<< (Writer &writer, float val)
{ return writer.writeFloat(val); }
Writer& operator<< (Writer &writer, double val)
{ return writer.writeDouble(val); }
} //namespace io
} //namespace dom
} //namespace w3c
} //namespace org
//#########################################################################
//# E N D O F F I L E
//#########################################################################