// Ryzom - MMORPG Framework
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// 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. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
#ifndef RY_PD_CPP_OUTPUT_H
#define RY_PD_CPP_OUTPUT_H
#include
#include
#include
#include
#include
#include
inline std::string lcFirst(std::string str)
{
if (!str.empty() && str[0]>='A' && str[0]<='Z')
str[0] += 'a'-'A';
return str;
}
inline std::string padTab(std::string str, uint sz)
{
uint i;
uint csize = 0;
for (i=0; i Methods;
std::string DisplayName;
std::string Description;
std::string Other;
};
class SMethodId
{
public:
SMethodId(sint section=-1, sint method=-1, CClassGenerator *generator=NULL) : Section(section), Method(method), Generator(generator) {}
sint Section;
sint Method;
CClassGenerator *Generator;
void add(const std::string &instruction);
void setDescription(const std::string &description);
};
/// name
std::string Name;
///
std::string Inherit;
/// sections
std::vector Sections;
/// in body
std::string Body;
///
sint CurrentSection;
///
SMethodId CurrentMethod;
///
bool StartInline;
void init(const std::string &name)
{
clear();
Name = name;
}
void clear()
{
Name.clear();
Sections.clear();
Body.clear();
Inherit.clear();
CurrentSection = -1;
}
void setSection(const std::string &name)
{
uint i;
for (i=0; i
CCppOutput & operator << (const T& t)
{
addToStream(NLMISC::toString(t).c_str());
return *this;
}
/// file description
void setFileHeader(const std::string& filename, const std::string& description)
{
if (_Clean)
{
*this << "/** \\file " << filename << "\n";
if (!description.empty())
*this << description << "\n";
*this << "\n";
*this << "$Id$\n";
*this << "*/\n\n";
}
}
private:
bool _CppMode;
bool _XmlMode;
bool _NewLine;
sint _Indent;
std::string _Buffer;
bool _DescriptionMode;
bool _Clean;
std::vector _XmlNodes;
bool _XmlInNode;
bool _XmlLastSlash;
bool _XmlGetNodeName;
bool _XmlCloseNode;
bool _XmlRootNode;
std::string _XmlNodeName;
/// Stream operator
CCppOutput addToStream (const char* str)
{
_Clean = false;
if (_CppMode)
{
while (*str != '\0')
{
if (!_DescriptionMode && *str == '}')
unindent();
bool triggerDescriptionStart = false;
bool triggerDescriptionEnd = false;
if (!strncmp(str, "/**", 3))
{
// start description
triggerDescriptionStart = true;
}
if (_DescriptionMode && !strncmp(str, "*/", 2))
{
// stop description
triggerDescriptionEnd = true;
}
bool skipChar = false;
if (*str != '\r')
{
if (_NewLine)
{
if ((*str == ' ' || *str == '\t') && !_DescriptionMode)
{
skipChar = true;
}
else
{
sint i;
for (i=0; i<_Indent; ++i)
_Buffer += '\t';
_NewLine = false;
if (_DescriptionMode)
{
_Buffer += " ";
if (!triggerDescriptionEnd)
_Buffer += "* ";
if (!triggerDescriptionEnd && *str == '*')
{
skipChar = true;
}
}
}
}
if (!_DescriptionMode && *str == '{')
indent();
}
if (*str == '\n')
{
_NewLine = true;
}
if (!skipChar)
_Buffer += *str;
if (triggerDescriptionStart)
_DescriptionMode = true;
if (triggerDescriptionEnd)
_DescriptionMode = false;
++str;
}
}
else if (_XmlMode)
{
while (*str != '\0')
{
bool skipChar = false;
bool requireIndent = false;
if (*str == '<')
{
//
_XmlCloseNode = (str[1] == '/');
_XmlRootNode = (str[1] == '?');
_XmlInNode = true;
_XmlGetNodeName = true;
_XmlNodeName.clear();
requireIndent = (!_XmlCloseNode && !_XmlRootNode);
if (_XmlCloseNode)
unindent();
}
else if (*str == '>')
{
_XmlInNode = false;
if (!_XmlCloseNode && !_XmlRootNode)
{
_XmlNodes.push_back(_XmlNodeName);
}
if (_XmlCloseNode || _XmlLastSlash)
{
if (_XmlLastSlash)
unindent();
nlassert(!_XmlNodes.empty() && _XmlNodes.back() == _XmlNodeName);
_XmlNodes.pop_back();
}
}
else if (*str == '\n')
{
_NewLine = true;
}
if (_NewLine)
{
if (*str == ' ' || *str == '\t')
{
skipChar = true;
}
else if (*str != '\n' && *str != '\r')
{
sint i;
for (i=0; i<_Indent; ++i)
_Buffer += '\t';
_NewLine = false;
}
}
_XmlLastSlash = (*str == '/');
if (_XmlInNode && _XmlGetNodeName && *str != '/' && *str != '<')
{
if (!isalpha(*str))
_XmlGetNodeName = false;
if (_XmlGetNodeName)
_XmlNodeName += *str;
}
if (!skipChar)
_Buffer += *str;
++str;
if (requireIndent)
indent();
}
}
else
{
_Buffer += str;
}
return *this;
}
};
//
// Inline implementation
//
inline void CCppOutput::clear()
{
_Buffer.clear();
}
inline bool searchForId(char* buffer, char** start, char** end)
{
const char* id = "$Id:";
uint len = (uint)strlen(id);
for (; *buffer != '\0'; ++buffer)
{
if (strncmp(buffer, id, len) == 0)
{
*start = buffer;
*end = buffer+1;
while (**end != '\0' && **end != '$')
++(*end);
if (**end != (char)'$')
return false;
++(*end);
return true;
}
}
return false;
}
inline void CCppOutput::flush(const std::string &fileName)
{
NLMISC::COFile f;
bool overwrite = true;
if (NLMISC::CFile::fileExists(fileName))
{
NLMISC::CIFile fi;
if (fi.open(fileName))
{
std::string buffer;
buffer.resize(fi.getFileSize(), '*');
fi.serialBuffer((uint8*)(&(buffer[0])), fi.getFileSize());
// search for $Id: cpp_output.h,v 1.15 2004/12/13 17:19:01 legros Exp $ string in file...
char *searchidstart, *searchidend;
char *replaceidstart, *replaceidend;
if (searchForId(&(buffer[0]), &replaceidstart, &replaceidend) &&
searchForId(&(_Buffer[0]), &searchidstart, &searchidend))
{
std::string replace = std::string(replaceidstart, replaceidend-replaceidstart);
std::string search = std::string(searchidstart, searchidend-searchidstart);
NLMISC::strFindReplace(_Buffer, search, replace);
}
overwrite = (buffer != _Buffer);
}
}
if (overwrite)
{
if (f.open(fileName))
{
f.serialBuffer((uint8*)(_Buffer.c_str()), (uint)_Buffer.size());
f.close();
}
else
{
nlwarning("Unable to open file '%s' for output", fileName.c_str());
}
}
else
{
nldebug("File '%s' did not changed, skipped", fileName.c_str());
}
clear();
}
inline void CCppOutput::indent()
{
++_Indent;
}
inline void CCppOutput::unindent()
{
--_Indent;
if (_Indent < 0)
_Indent = 0;
}
inline void CClassGenerator::flush(CCppOutput &hFile, CCppOutput &cppFile, CCppOutput &hInlineFile)
{
hFile << "class " << Name;
if (!Inherit.empty())
hFile << " : " << Inherit;
hFile << "\n{\n";
hInlineFile << "/* -----------------------------------------\n";
hInlineFile << " * Inline implementation of " << Name << "\n";
hInlineFile << " * ----------------------------------------- */\n";
cppFile << "/* -----------------------------------------\n";
cppFile << " * Static Implementation of " << Name << "\n";
cppFile << " * ----------------------------------------- */\n";
uint i;
for (i=0; iadd(instruction, *this);
}
inline void CClassGenerator::SMethodId::setDescription(const std::string &description)
{
nlassert(Generator != NULL);
Generator->add(description, *this);
}
inline void CFunctionGenerator::flush(CCppOutput &hFile, CCppOutput &cppFile, CCppOutput &hInlineFile)
{
std::string inlinekw = (IsInline ? std::string("inline ") : std::string(""));
if (!Description.empty())
{
hFile << "\n/**\n" << Description << "\n*/\n";
}
hFile << padTab(Type, 32) << lcFirst(Name) << "(" << Proto << ");\n";
if (IsInline)
{
hInlineFile << padTab(inlinekw+Type, 32) << lcFirst(Name) << "(" << removeDefaultArgs(Proto) << ")\n";
hInlineFile << "{\n";
hInlineFile << Body;
hInlineFile << "}\n\n";
}
else
{
cppFile << padTab(Type, 32) << lcFirst(Name) << "(" << removeDefaultArgs(Proto) << ")\n";
cppFile << "{\n";
cppFile << Body;
cppFile << "}\n\n";
}
}
#endif