📄 graphbuilderaiml.cpp
字号:
/*
* RebeccaAIML, Artificial Intelligence Markup Language
* C++ api and engine.
*
* Copyright (C) 2005 Frank Hassanabad
*
* 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
*/
//Boost includes
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/filesystem/exception.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/tokenizer.hpp>
#include <boost/algorithm/string_regex.hpp>
#include <sstream>
using namespace boost;
using namespace boost::filesystem;
//Stl includes
#include <iostream>
using namespace std;
//Xerces includes
#include <xercesc/util/XercesDefs.hpp>
#include <xercesc/framework/LocalFileInputSource.hpp>
#include <xercesc/framework/MemBufInputSource.hpp>
XERCES_CPP_NAMESPACE_USE
//Rebecca includes
#include <rebecca/impl/GraphBuilderAIML.h>
#include <rebecca/impl/utils/StackTrace.h>
#include <rebecca/impl/utils/Logging.h>
#include <rebecca/impl/utils/Transcode.h>
#include <rebecca/impl/GraphHandler.h>
#include <rebecca/impl/ConfigurationHandler.h>
#include <rebecca/impl/Exceptions.h>
#include <rebecca/impl/GraphHandlerError.h>
#include <rebecca/impl/ConfigurationHandlerError.h>
#include <rebecca/framework/CustomTags.h>
using namespace rebecca;
using namespace rebecca::framework;
/* Disable Windows VC 7.x warning about
* it ignoring the throw specification
*
* The extra includes under Windows is
* for the callCommand() method to
* operate under Windows
*/
#ifdef _WIN32
# pragma warning( disable : 4290 )
# include <windows.h>
# include <errno.h>
# include <io.h>
# include <fcntl.h>
# include <ctype.h>
#else
# include <dlfcn.h>
#endif
namespace rebecca
{
namespace impl
{
GraphBuilderAIML::GraphBuilderAIML()
throw(InitializationException &, Exception &)
: m_setAIMLValidation(false),
m_NodeMapperRoot(*this),
m_useThatStar(false),
m_useTopicStar(false),
m_doConfigurationValidation(false),
m_size(0),
m_aimlHeader("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?> "
"<aiml version=\"1.0.1\" xmlns=\"http://alicebot.org/2001/AIML-1.0.1\" "
"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
"xsi:schemaLocation=\"http://alicebot.org/2001/AIML-1.0.1 http://aitools.org/aiml/schema/AIML.xsd\">"
),
m_aimlFooter("</aiml>")
{
try
{
LOG_BOT_METHOD("GraphBuilderAIML::GraphBuilderAIML()");
initializeXerces();
init();
}
catch(const XMLException &toCatch)
{
logging("ERROR, Caught XMLXMLException exception");
Transcode message(toCatch.getMessage());
String msg("XMLException during initalization: " + message.getString());
throw InitializationExceptionImpl(msg.c_str());
}
catch(exception &e)
{
throw ExceptionImpl(e.what());
}
}
GraphBuilderAIML::GraphBuilderAIML(const GraphBuilderAIML &builder)
throw(InitializationException &, Exception &)
: m_setAIMLValidation(false),
m_NodeMapperRoot(*this),
m_useThatStar(false),
m_useTopicStar(false),
m_doConfigurationValidation(false),
m_size(0),
m_aimlHeader("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?> "
"<aiml version=\"1.0.1\" xmlns=\"http://alicebot.org/2001/AIML-1.0.1\" "
"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
"xsi:schemaLocation=\"http://alicebot.org/2001/AIML-1.0.1 http://aitools.org/aiml/schema/AIML.xsd\">"
),
m_aimlFooter("</aiml>")
{
try
{
LOG_BOT_METHOD("GraphBuilderAIML::GraphBuilderAIML(const GraphBuilderAIML &builder)");
init();
}
catch(exception &e)
{
throw ExceptionImpl(e.what());
}
}
void GraphBuilderAIML::init()
throw(XMLException &, Exception &)
{
try
{
//defaultCallBacks does nothing.
m_callBacks = &m_defaultCallBacks;
setThat("*");
setTopic("*");
logging("Allocating the parsers for various XML files");
m_AIMLparser.reset(new SAXParser);
m_configurationParser.reset(new SAXParser);
logging("Allocating the document handlers for the respective parsers");
m_AIMLDocumentHandler.reset(new GraphHandler(m_NodeMapperRoot, *this));
m_configurationDocumentHandler.reset(new ConfigurationHandler(*this));
logging("Allocating the Error handling routines for the respective parsers");
m_AIMLErrorHandler.reset(new GraphHandlerError(*this));
m_configurationErrorHandler.reset(new ConfigurationHandlerError(*this));
logging("Setting the document handlers for the respective parsers");
m_AIMLparser->setDocumentHandler(m_AIMLDocumentHandler.get());
m_configurationParser->setDocumentHandler(m_configurationDocumentHandler.get());
logging("Setting up the error handlers for the respective parsers");
m_AIMLparser->setErrorHandler(m_AIMLErrorHandler.get());
m_configurationParser->setErrorHandler(m_configurationErrorHandler.get());
}
catch(exception &e)
{
throw ExceptionImpl(e.what());
}
}
void GraphBuilderAIML::addFile(const char * const file)
throw(FileNotFoundException &, Exception &)
{
try
{
LOG_BOT_METHOD("void GraphBuilderAIML::addFile(const String &file)");
logging("<Input> file: " + String(file));
//Get the file format in the native string for Xerces
path nativeFileFormat(file, native);
//Get the complete path to the file
path completePath = complete(nativeFileFormat);
if(!exists(nativeFileFormat))
{
logging("USER ERROR, is not a file");
String fileNotFoundMsg = "File:" + String(file) + " was not found";
throw FileNotFoundExceptionImpl(fileNotFoundMsg.c_str());
}
else
{
logging("file exists, adding to filesNotGraphed");
//Get the native file system's string to pass to xerces
string nativeFile = completePath.native_file_string();
m_filesGraphed.insert(pair<String, bool>(nativeFile, false));
}
}
catch(exception &e)
{
throw ExceptionImpl(e.what());
}
}
void GraphBuilderAIML::addDirectory(const char * const directory, const char * const regularExpression)
throw(IllegalArgumentException &, DirectoryNotFoundException &, Exception &)
{
try
{
LOG_BOT_METHOD("void GraphBuilderAIML::addDirectory(const String &directory, const char * const regularExpression)");
logging("<Input> directory: " + String(directory));
logging("<Input> regular expression: " + String(regularExpression));
regex rx1(regularExpression, boost::regex::icase);
smatch what;
//Get the file format in the native string for Xerces
path nativeDirFormat(directory, native);
//Get the complete path to the file
path completePath = complete(nativeDirFormat);
if (!exists(nativeDirFormat))
{
logging("ERROR, Exception Directory does not exist");
String directoryNotFoundmsg("Directory:" + String(directory) + " was not found");
throw DirectoryNotFoundExceptionImpl(directoryNotFoundmsg.c_str());
}
// default construction yields past-the-end
directory_iterator end_itr;
//Get the native file system's string to pass to xerces
string nativeDir = completePath.native_file_string();
for ( directory_iterator itr(completePath); itr != end_itr; ++itr )
{
if ( !is_directory( *itr ) && regex_match(string(itr->leaf()), what, rx1))
{
logging("Found file of:" + itr->leaf());
String fileLocation(nativeDir);
fileLocation += "/" + itr->leaf();
addFile(fileLocation.c_str());
}
}
}
catch(filesystem_error &)
{
logging("ERROR, filesystem error");
throw IllegalArgumentExceptionImpl("File system error");
}
catch(bad_expression &)
{
logging("ERROR, bad expression");
throw IllegalArgumentExceptionImpl("Bad expression with the regular expression argument");
}
catch(runtime_error &)
{
logging("ERROR, Runtime error");
throw IllegalArgumentExceptionImpl("Run time error occured, more than likely with the regular expression");
}
catch(FileNotFoundException &)
{
logging("FileNotFoundException, you should not be here.");
throw IllegalArgumentExceptionImpl("File not found exception occured. You must have passed a bad directory in.");
}
catch(exception &e)
{
throw ExceptionImpl(e.what());
}
}
void GraphBuilderAIML::addString(const char * const stringContainingAIML)
throw(Exception &)
{
try
{
String stringToInsert = m_aimlHeader + stringContainingAIML + m_aimlFooter;
m_stringsGraphed.insert(pair<String, bool>(stringToInsert, false));
}
catch(exception &e)
{
throw ExceptionImpl(e.what());
}
}
void GraphBuilderAIML::setAddStringAIMLHeader(const char * const aimlHeader)
throw(Exception &)
{
try
{
m_aimlHeader = aimlHeader;
}
catch(exception &e)
{
throw ExceptionImpl(e.what());
}
}
void GraphBuilderAIML::setAddStringAIMLFooter(const char * const aimlFooter)
throw(Exception &)
{
try
{
m_aimlFooter = aimlFooter;
}
catch(exception &e)
{
throw ExceptionImpl(e.what());
}
}
void GraphBuilderAIML::setAIMLSchema(const char * const schema)
throw(Exception &)
{
try
{
LOG_BOT_METHOD("void GraphBuilderAIML::setAIMLSchema(const char * const schema)");
logging("<Input> schema:" + String(schema));
m_aimlSchema = schema;
}
catch(exception &e)
{
throw ExceptionImpl(e.what());
}
}
void GraphBuilderAIML::setCommonTypesSchema(const char * const schema)
throw(Exception &)
{
try
{
LOG_BOT_METHOD("void GraphBuilderAIML::setCommonTypesSchema(const char * const schema)");
logging("<Input> schema:" + String(schema));
m_commonTypesSchema = schema;
}
catch(exception &e)
{
throw ExceptionImpl(e.what());
}
}
StringPimpl GraphBuilderAIML::getResponseInternal(const char * const input, bool keepPreviousUserInput)
{
/*
* Create a tokenizer to tokenize the words.
*/
typedef tokenizer<char_separator<char> > tokenize;
//const iterator to iterate over the tokens
typedef tokenize::const_iterator CI;
//Create a string from the input
String stringInput = inputSubstitute(input).c_str();
//Create a "seperator" based on the sentence splitters
char_separator<char> sep(m_sentenceSplitters.c_str());
/*
* Create a "seperator" based on the sentence splitters but
* when the sentence is split with this seperator the splitters
* will be kept.
*/
char_separator<char> sepWithKeptDel("", m_sentenceSplitters.c_str());
//Create a tokenizer from the sentences.
tokenize tokens(stringInput, sep);
//The response we are going to return
String returnResponse;
//Iterate over the senteces one sentence at a time.
for(CI it = tokens.begin(); it != tokens.end(); ++it)
{
//Only Srai::getString() disables this
if(keepPreviousUserInput)
{
//Keep the input for the <input index=""> aiml tag
m_previousUserInput.push_front(stringInput);
}
//Get the bot response based upon the string
String response = m_NodeMapperRoot.getTemplateString(*it);
if(!response.empty())
{
/*
* We want to tokenize the returned response but keep the
* seperators such as punctiation and semicolons intact.
*/
tokenize responseTokens(response, sepWithKeptDel);
int i = 1;
DequeString sentencePunct(1);
DequeString sentence;
//Iterate over the response output one sentence at a time.
for(CI jit = responseTokens.begin(); jit != responseTokens.end(); ++jit, ++i)
{
/*
* Create a modifiable string
* version of it and trim the spaces from it.
*/
String that(*jit);
trim(that);
//Fancy way of checking every other sentence
if(i % 2)
{
//boolean for checking if it's just punctuation.
bool isPunct = false;
if(that.size() == 1)
{
/*
* It has a size of 1. Check if it is a sentence splitter.
* This can happen when you have a sentece of the
* form "hi.?" with two punctiations next to each
* other.
*/
for(unsigned int k = 0; k < m_sentenceSplitters.size(); ++k)
{
if(that.at(0) == m_sentenceSplitters.at(k))
{
sentencePunct.at(0) += that;
--i;
isPunct = true;
break;
}
}
}
if(!isPunct)
{
/*
* Not punctiation, so add
* it as that
*/
setThat(that.c_str());
sentence.push_front(that);
sentencePunct.push_front(that);
}
}
else
{
/*
* It is the punctionation by its self.
* So tack on the punctionation to the end of the
* previous sentence.
*/
if(!sentencePunct.empty())
{
sentencePunct.at(0) += that;
}
}
}
//Add this response as a previous bot response
m_previousBotResponse.push_front(sentence);
/*
* Add this response as a previous bot response but with the
* added punctiation.
*/
m_previousBotResponseWithPunct.push_front(sentencePunct);
/*
* If we don't have a return response let's at least return
* a seperating space so if we have more sentences to add to
* the return response they'll be seperated by a space.
*/
if(!returnResponse.empty())
{
returnResponse += " ";
}
//Add the sentence to the returnResponse string
returnResponse += response;
}
else
{
logging("Warning empty response being returned");
}
} //end of iterating over the sentences one sentence at a time
//Trim any remaining spaces at the begining or end of the sentence
trim(returnResponse);
//Return a StringPimpl version of the string
return StringPimpl(returnResponse.c_str());
}
StringPimpl GraphBuilderAIML::getResponse(const char * const input)
throw(Exception &)
{
try
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -