// This file may be redistributed and modified under the terms of the
// GNU Lesser General Public License (See COPYING for details).
// Copyright (C) 2002 Michael Koch <konqueror@gmx.de>
#include <Atlas/Codecs/Bach.h>
#include <Atlas/Debug.h>
#include <iostream>
#include <cstdlib>
static const bool debug_flag = false;
namespace Atlas { namespace Codecs {
Bach::Bach(std::iostream& s, Atlas::Bridge & b)
: m_socket(s)
, m_bridge(b)
, m_comma(false)
, m_linenum(0)
{
m_state.push(PARSE_INIT);
}
void Bach::parseInit(char next)
{
ATLAS_DEBUG(std::cout << "Bach::parseInit" << std::endl;)
if (next=='[')
{
m_bridge.streamBegin();
m_state.push(PARSE_STREAM);
}
}
void Bach::parseStream(char next)
{
ATLAS_DEBUG(std::cout << "Bach::parseStream" << std::endl;)
switch (next)
{
case '{':
m_bridge.streamMessage();
m_state.push(PARSE_MAP);
break;
case ']':
m_bridge.streamEnd();
break;
default:
break;
}
}
void Bach::parseMap(char next)
{
ATLAS_DEBUG(std::cout << "Bach::parseMap" << std::endl;)
switch (next)
{
case '}':
m_bridge.mapEnd();
m_state.pop();
break;
case ',':
case ' ':
case '\t':
break;
case '\"':
m_state.push(PARSE_DATA);
m_state.push(PARSE_NAME);
break;
default:
if (((next>='a')&&(next<='z'))||
((next>='A')&&(next<='Z')))
{
m_socket.putback(next);
m_state.push(PARSE_DATA);
m_state.push(PARSE_NAME);
}
else
{
std::cerr << "Bach::parseMap: unexpected character: " << next << std::endl;
}
break;
}
}
void Bach::parseList(char next)
{
ATLAS_DEBUG(std::cout << "Bach::parseList" << std::endl;)
switch (next)
{
case ']':
m_bridge.listEnd();
m_state.pop();
break;
case '{':
m_bridge.listMapItem();
m_state.push(PARSE_MAP);
break;
case '[':
m_bridge.listListItem();
m_state.push(PARSE_LIST);
break;
case ',':
case ' ':
case '\t':
break;
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '0':
case '-':
m_state.push(PARSE_INT);
m_socket.putback(next);
break;
case '\"':
m_state.push(PARSE_STRING);
break;
default:
std::cerr << "Bach::parseMap: unexpected character: " << next << std::endl;
break;
}
}
void Bach::parseInt(char next)
{
ATLAS_DEBUG(std::cout << "Bach::parseInt" << std::endl;)
switch (next)
{
case '[':
case ']':
case '{':
case '}':
case ',':
m_socket.putback(next);
m_state.pop();
if (m_state.top() == PARSE_MAP)
{
ATLAS_DEBUG(std::cout << "Int: " << m_name << ": " << m_data << std::endl;)
m_bridge.mapIntItem(decodeString(m_name), atol(m_data.c_str()));
}
else if (m_state.top() == PARSE_LIST)
{
ATLAS_DEBUG(std::cout << "Int: " << m_data << std::endl;)
m_bridge.listIntItem(atol(m_data.c_str()));
}
else
{
std::cerr << "Bach::parseIntt: Error" << std::endl;
}
m_name.erase();
m_data.erase();
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '-':
case '+':
case 'e':
case 'E':
m_data += next;
break;
case '.':
m_state.pop();
m_state.push(PARSE_FLOAT);
m_data += next;
break;
default:
std::cerr << "Bach::parseInt: unexpected character: " << next << std::endl;
break;
}
}
void Bach::parseFloat(char next)
{
ATLAS_DEBUG(std::cout << "Bach::parseFloat" << std::endl;)
switch (next)
{
case '[':
case ']':
case '{':
case '}':
case ',':
m_socket.putback(next);
m_state.pop();
if (m_state.top() == PARSE_MAP)
{
ATLAS_DEBUG(std::cout << "Float: " << m_name << ": " << m_data << std::endl;)
m_bridge.mapFloatItem(decodeString(m_name), atof(m_data.c_str()));
}
else if (m_state.top() == PARSE_LIST)
{
ATLAS_DEBUG(std::cout << "Float: " << m_data << std::endl;)
m_bridge.listFloatItem(atof(m_data.c_str()));
}
else
{
std::cerr << "Bach::parseFloat: Error" << std::endl;
}
m_name.erase();
m_data.erase();
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '.':
case '-':
case '+':
case 'e':
case 'E':
m_data += next;
break;
default:
std::cerr << "Bach::parseFloat: unexpected character: " << next << std::endl;
break;
}
}
void Bach::parseString(char next)
{
ATLAS_DEBUG(std::cout << "Bach::parseString" << std::endl;)
switch (next)
{
case '\"':
m_state.pop();
if (m_state.top() == PARSE_MAP)
{
ATLAS_DEBUG(std::cout << "String: " << m_name << ": " << m_data << std::endl;)
m_bridge.mapStringItem(decodeString(m_name), decodeString(m_data));
}
else if (m_state.top() == PARSE_LIST)
{
ATLAS_DEBUG(std::cout << "String: " << m_data << std::endl;)
m_bridge.listStringItem(decodeString(m_data));
}
else
{
std::cerr << "Bach::parseString: Error" << std::endl;
}
m_name.erase();
m_data.erase();
break;
case '\\':
m_state.push(PARSE_LITERAL);
break;
default:
m_data += next;
break;
}
}
void Bach::parseLiteral(char next)
{
m_data += next;
m_state.pop();
}
void Bach::parseData(char next)
{
ATLAS_DEBUG(std::cout << "Bach::parseData" << std::endl;)
switch (next)
{
case '-':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '0':
m_socket.putback(next);
m_state.pop();
m_state.push(PARSE_INT);
break;
case '{':
m_state.pop();
switch (m_state.top())
{
case PARSE_MAP:
m_bridge.mapMapItem(decodeString(m_name));
m_name.erase();
break;
case PARSE_LIST:
m_bridge.listMapItem();
break;
default:
std::cerr << "Bach::parseData: Error: " << (int)m_state.top() << std::endl;
break;
}
m_state.push(PARSE_MAP);
break;
case '[':
m_state.pop();
switch (m_state.top())
{
case PARSE_MAP:
m_bridge.mapListItem(decodeString(m_name));
m_name.erase();
break;
case PARSE_LIST:
m_bridge.listListItem();
break;
default:
std::cerr << "Bach::parseData: Error: " << (int)m_state.top() << std::endl;
break;
}
m_state.push(PARSE_LIST);
break;
case '\"':
m_state.pop();
m_state.push(PARSE_STRING);
break;
case ',':
case ':':
break;
default:
std::cerr << "Bach::parseData: unexpected character: " << next << std::endl;
break;
}
}
void Bach::parseName(char next)
{
ATLAS_DEBUG(std::cout << "Bach::parseName" << std::endl;)
switch (next)
{
case ':':
case '\"':
ATLAS_DEBUG(std::cout << "Name: " << m_name << std::endl;)
m_state.pop();
break;
default:
if (((next>='a')&&(next<='z'))||
((next>='A')&&(next<='Z'))||
((next>='0')&&(next<='9'))||
(next=='_'))
{
m_name += next;
}
else
{
std::cerr << "Bach::parseName: unexpected character: " << next << std::endl;
}
break;
}
}
void Bach::parseComment(char next)
{
if(next == '\n')
m_state.pop();
}
bool Bach::stringmode() const
{
switch(m_state.top())
{
case PARSE_COMMENT:
case PARSE_STRING:
case PARSE_LITERAL:
return true;
default:
return false;
}
}
void Bach::poll(bool can_read)
{
if (!can_read) return;
m_socket.peek();
std::streamsize count;
if ((count = m_socket.rdbuf()->in_avail()) > 0) {
for (int i = 0; i < count; ++i) {
int next = m_socket.rdbuf()->sbumpc();
// check for comment character here, so we don't have
// to do it in every section
switch(next)
{
case '#':
if(!stringmode())
{
m_state.push(PARSE_COMMENT);
continue;
}
break;
case '\n':
m_linenum++;
if(!stringmode())
continue;
break;
case '\r': // dealing with DOS files, I guess
continue;
default:
break;
}
switch (m_state.top())
{
case PARSE_INIT: parseInit(next); break;
case PARSE_STREAM: parseStream(next); break;
case PARSE_MAP: parseMap(next); break;
case PARSE_LIST: parseList(next); break;
case PARSE_DATA: parseData(next); break;
case PARSE_INT: parseInt(next); break;
case PARSE_FLOAT: parseFloat(next); break;
case PARSE_STRING: parseString(next); break;
case PARSE_LITERAL: parseLiteral(next); break;
case PARSE_NAME: parseName(next); break;
case PARSE_COMMENT: parseComment(next); break;
}
}
}
}
const std::string Bach::decodeString(const std::string & toDecode)
{
std::string::size_type pos = 0;
std::string to_decode(toDecode);
while((pos = to_decode.find( "\\\"", pos )) != std::string::npos)
to_decode.replace(pos, 2, 1, '\"');
pos = 0;
while((pos = to_decode.find( "\\\\", pos)) != std::string::npos)
to_decode.replace(pos, 2, 1, '\\');
return to_decode;
}
const std::string Bach::encodeString(const std::string & toEncode)
{
std::string encoded;
std::string::const_iterator it;
for (it = toEncode.begin(); it != toEncode.end(); it++)
{
if (*it=='\\')
encoded += "\\\\";
else if (*it=='\"')
encoded += "\\\"";
else
encoded += *it;
}
return encoded;
}
void Bach::writeIntItem(const std::string & name, long data)
{
if( m_comma )
m_socket << ",";
if( name != "" )
m_socket << name << ":";
m_socket << data;
}
void Bach::writeFloatItem(const std::string & name, double data)
{
if( m_comma )
m_socket << ",";
if( name != "" )
m_socket << name << ":";
m_socket << data;
}
void Bach::writeStringItem(const std::string & name, const std::string & data)
{
if( m_comma )
m_socket << ",";
if( name != "" )
m_socket << name << ":";
m_socket << "\"" << encodeString( data ) << "\"";
}
void Bach::writeLine(const std::string & line, bool endline, bool endtag)
{
if (m_comma && !endtag)
m_socket << ",";
m_socket << line;
}
void Bach::streamBegin()
{
writeLine( "[" );
m_comma = false;
}
void Bach::streamEnd()
{
writeLine( "]", true, true );
}
void Bach::streamMessage()
{
writeLine( "{" );
m_comma = false;
}
void Bach::mapMapItem(const std::string& name)
{
writeLine( name + ":{" );
m_comma = false;
}
void Bach::mapListItem(const std::string& name)
{
writeLine( name + ":[" );
m_comma = false;
}
void Bach::mapIntItem(const std::string& name, long data)
{
writeIntItem( name, data );
m_comma = true;
}
void Bach::mapFloatItem(const std::string& name, double data)
{
writeFloatItem( name, data );
m_comma = true;
}
void Bach::mapStringItem(const std::string& name, const std::string& data)
{
writeStringItem( name, data );
m_comma = true;
}
void Bach::mapEnd()
{
writeLine( "}", true, true );
m_comma = true;
}
void Bach::listMapItem()
{
writeLine( "{" );
m_comma = false;
}
void Bach::listListItem()
{
writeLine( "[" );
m_comma = false;
}
void Bach::listIntItem(long data)
{
writeIntItem( "", data );
m_comma = true;
}
void Bach::listFloatItem(double data)
{
writeFloatItem( "", data );
m_comma = true;
}
void Bach::listStringItem(const std::string& data)
{
writeStringItem( "", data );
m_comma = true;
}
void Bach::listEnd()
{
writeLine( "]", true, true );
m_comma = true;
}
} } //namespace Atlas::Codecs
syntax highlighted by Code2HTML, v. 0.9.1