// Copyright (C) 2001 Jean-Marc Valin
#include "path.h"
#include <libxml/tree.h>
#include <libxml/parser.h>
#include "UIDocument.h"
#include "UINetwork.h"
#include "UINode.h"
#include "Node.h"
#include "Network.h"
#include "ParameterSet.h"
#include <sys/stat.h>
#ifndef WIN32
#include <dlfcn.h>
#endif
#include <sys/types.h>
#include <dirent.h>
#include <unistd.h>
#include <sstream>
#include <fstream>
#include <fcntl.h>
#include "stream_wrap.h"
#include "object_param.h"
//@implements UIClasses
using namespace std;
namespace FD {
UIDocument::UIDocument(string _name)
: modified(false)
, docName(_name)
, untitled(true)
, destroyed(false)
{
}
UIDocument::~UIDocument()
{
if (!destroyed)
{
//cerr << "destroying UIDocument " << name << endl;
for (unsigned int i=0;i<networks.size();i++)
{
delete networks[i];
networks[i]=NULL;
}
for (unsigned int i=0;i<textParams.size();i++)
delete textParams[i];
for (unsigned int i=0;i<docInputs.size();i++)
delete docInputs[i];
for (unsigned int i=0;i<docOutputs.size();i++)
delete docOutputs[i];
for (unsigned int i=0;i<docParams.size();i++)
delete docParams[i];
destroyed=true;
}
}
UINetwork *UIDocument::getNetworkNamed(const string &n)
{
for (unsigned int i=0;i<networks.size();i++)
{
if (networks[i]->getName() == n)
return networks[i];
}
return NULL;
}
vector<ItemInfo *> UIDocument::getNetInputs(const string &netName)
{
//updateAllNetworks();
vector <ItemInfo *> inputs;
if (subnetInfo.findNode(netName))
return subnetInfo.findNode(netName)->inputs;
return inputs;
}
vector<ItemInfo *> UIDocument::getNetOutputs(const string &netName)
{
//updateAllNetworks();
vector <ItemInfo *> outputs;
if (subnetInfo.findNode(netName))
return subnetInfo.findNode(netName)->outputs;
return outputs;
}
vector<ItemInfo *> UIDocument::getNetParams(const string &netName)
{
//updateAllNetworks();
vector <ItemInfo *> params;
if (subnetInfo.findNode(netName))
return subnetInfo.findNode(netName)->params;
return params;
}
string UIDocument::getDescription(const string &type)
{
NodeInfo *info = UINodeRepository::Find(type);
if (info)
return info->description;
else
return "Description not available";
}
void UIDocument::addParameterText(string name, string value, string type)
{
DocParameterDataText *textInfo = new DocParameterDataText;
textInfo->name = name;
textInfo->value = value;
textInfo->type = type;
textParams.insert(textParams.end(), textInfo);
}
void UIDocument::printOn(ostream &out) const
{
out << "<UIDocument" << endl;
out << "<name " << docName << " >" << endl;
out << ">" << endl;
}
void UIDocument::load()
{
string fullpath=path+docName;
/* This allows making scripts by ignoring the #! line at the beginning
Unfortunately, it is incompatible with the compression feature of libXML*/
//ostringstream docText;
ifstream docFile(fullpath.c_str());
if (docFile.fail())
{
error("Error: cannot open file");
cerr << "load: error loading " << fullpath << "\n";
addNetwork("MAIN", UINetwork::subnet);
resetModified();
return;
}
char ch;
docFile >> ch;
if (ch=='#')
{
while (ch != '<')
{
docFile >> ch;
if (docFile.fail())
{
error("Error: this doesn't look like an FlowDesigner document");
addNetwork("MAIN", UINetwork::subnet);
resetModified();
return;
}
}
} else if (ch!='<')
{
error("Error: this doesn't look like an FlowDesigner document");
addNetwork("MAIN", UINetwork::subnet);
resetModified();
return;
}
string xmlStr;
docFile >> xmlStr;
if (xmlStr != "?xml")
{
error("Error: this doesn't look like an FlowDesigner document");
addNetwork("MAIN", UINetwork::subnet);
resetModified();
return;
}
//docFile.putback(ch);
string docStr="<?xml";
while(1)
{
//char buff[1025];
//docFile.read(buff, 1024);
//buff[1024]=0;
string buff;
getline( docFile, buff );
if (docFile.fail())
{
//docStr.append(buff, docFile.gcount());
docStr.append(buff.c_str(), docFile.gcount());
break;
}
//docStr.append(buff, 1024);
docStr.append(buff.c_str());
}
cerr<<"loading XML document from memory"<<endl;
loadFromMemory(docStr.c_str(), docStr.size());
cerr<<"done!"<<endl;
}
void UIDocument::loadFromMemory(const char *mem, int size)
{
xmlDocPtr doc = xmlParseMemory (const_cast<char *> (mem), size);
if (!doc || !doc->children || !doc->children->name)
{
error("Error: corrupted XML in file");
addNetwork("MAIN", UINetwork::subnet);
resetModified();
return;
}
xmlNodePtr root=doc->children;
loadXML(root);
xmlFreeDoc(doc);
}
void UIDocument::loadXML(xmlNodePtr root)
{
//loadAllSubnetInfo(root->children);
subnetInfo.clean();
subnetInfo.loadAllSubnetInfo(root->children);
//loading category if it exists
xmlChar *cat = xmlGetProp(root, (xmlChar *)"category");
if (cat)
{
category = string((char *)cat);
free (cat);
}
//loading comments if they exists
xmlChar *comments = xmlGetProp(root, (xmlChar *)"comments");
if (comments)
{
m_comments = string((char *)comments);
free (comments);
}
//loading networks
xmlNodePtr net = root->children;
//cerr << "parsing...\n";
while (net != NULL)
{
//Standard network found
if (string((char*)net->name) == "Network")
{
addNetwork (net);
}
//File included (prototype)
if (string((char*)net->name) == "IncludeNetwork")
{
cerr<<"Warning, included network is still a prototype, use at your own risk"<<endl;
xmlChar *fname = xmlGetProp(net,(xmlChar *)"file");
if (fname)
{
cerr<<"Including : "<<(char*) fname<<endl;
try
{
//let's import the network
importNetwork(string((char*) fname));
}
catch(BaseException *e)
{
e->print(cerr);
delete e;
}
free(fname);
}
}
net = net->next;
}
vector<ItemInfo *> tmp = getNetParams("MAIN");
//cerr << "Got " << tmp.size() << " params in GUIDocument::createParamDialog\n";
//textParams.resize(tmp.size());
for (unsigned int i=0;i<tmp.size();i++)
{
DocParameterDataText *newParam = new DocParameterDataText;
newParam->name = string (tmp[i]->name);
textParams.insert(textParams.end(), newParam);
//textParams[i]->name=tmp[i];
}
//cerr << "--\n";
xmlNodePtr par = root->children;
//cerr << "par = " << par << endl;
while (par)
{
if (string((char*)par->name) == "Parameter")
{
char *str_name = (char *) xmlGetProp(par, (xmlChar *)"name");
char *str_type = (char *) xmlGetProp(par, (xmlChar *)"type");
char *str_value = (char *) xmlGetProp(par, (xmlChar *)"value");
string name = string (str_name);
string type = string (str_type);
string value = string (str_value);
free(str_name); free(str_type); free(str_value);
for (unsigned int i=0;i<textParams.size();i++)
{
if (textParams[i]->name == name)
{
textParams[i]->type = type;
textParams[i]->value = value;
//insertLoadedParam(param, type, value);
}
}
//cerr << "<param: " << name << ", " << type << ":" << value << ">\n";
}
par = par->next;
}
modified = false;
//updating all networks
updateAllNetworks();
}
UINetwork *UIDocument::newNetwork(const string &_name, UINetwork::Type type)
{
//cerr << "UIDocument::newNetwork\n";
return new UINetwork(this, _name, type);
}
UINetwork *UIDocument::newNetwork(xmlNodePtr _net)
{
//cerr << "UIDocument::newNetwork\n";
return new UINetwork(this, _net);
}
UINetwork *UIDocument::addNetwork(string name, UINetwork::Type type)
{
//cerr << "UIDocument::addNetwork (type = " << typeid(this).name() << ")" << endl;
//UINetwork *newNet = new GUINetwork(this, name, iter);
bool found = false;
for (unsigned int i = 0; i < networks.size(); i++) {
if (networks[i]->getName() == name) {
found = true;
break;
}
}
if (found) {
throw new GeneralException(string("Network already exist : ") + string(name), __FILE__, __LINE__);
}
UINetwork *newNet = newNetwork(name, type);
for (unsigned int i=0;i<networks.size();i++)
{
networks[i]->newNetNotify("Subnet",name);
newNet->newNetNotify("Subnet",networks[i]->getName());
}
networks.insert(networks.end(), newNet);
modified = true;
return newNet;
}
UINetwork *UIDocument::addNetwork(xmlNodePtr xmlNet)
{
//cerr << "creating...\n";
UINetwork *newNet = newNetwork(xmlNet);
//look for existing network before adding
UINetwork *tmp_net = getNetworkNamed(newNet->getName());
if (tmp_net != NULL) {
string netName = newNet->getName();
delete newNet;
throw new GeneralException(string("Network (") + netName + string(") already exists"),__FILE__,__LINE__);
}
//cerr << "created\n";
//cerr << "newNet = " << newNet << endl;
//cerr << "network created in UIDocument::addNetwork\n";
for (unsigned int i=0;i<networks.size();i++)
{
networks[i]->newNetNotify("Subnet",newNet->getName());
newNet->newNetNotify("Subnet",networks[i]->getName());
}
//cerr << "newNet = " << newNet << endl;
networks.insert(networks.end(), newNet);
modified = true;
return newNet;
}
//This function looks useless. Is it?
void UIDocument::removeNetwork(UINetwork *toRemove)
{
vector<UINetwork *>::iterator i=networks.begin();
while (i != networks.end())
{
if (*i == toRemove)
{
delete (*i);
networks.erase(i);
break;
}
++i;
}
setModified();
}
void UIDocument::error(const char *err)
{
cerr << err << endl;
}
void UIDocument::save()
{
string fullname = path+docName;
int size;
int save_fd = open(fullname.c_str(), O_CREAT|O_WRONLY|O_TRUNC, 00755);
if (save_fd==-1)
{
error("Error while saving file: cannot open");
return;
}
fd_ostream outFile(save_fd);
if (outFile.fail())
{
error("Error while saving file");
return;
}
char *mem = saveToMemory(size);
outFile << "#!/usr/bin/env batchflow" << endl;
outFile.write(mem, size);
if (outFile.fail())
{
free(mem);
error("Error while saving file");
return;
}
free(mem);
resetModified();
}
char *UIDocument::saveToMemory(int &size)
{
xmlDocPtr doc;
doc = xmlNewDoc((xmlChar *)"1.0");
doc->children = xmlNewDocNode(doc, NULL, (xmlChar *)"Document", NULL);
//saving category if defined
if (category!="") {
xmlSetProp(doc->children, (xmlChar *)"category", (xmlChar *)category.c_str());
}
//saving comments if defined
if (m_comments!="") {
xmlSetProp(doc->children, (xmlChar *)"comments", (xmlChar *)m_comments.c_str());
}
for (unsigned int i=0;i<networks.size();i++)
{
networks[i]->saveXML(doc->children);
}
for (unsigned int i=0;i<textParams.size();i++)
{
xmlNodePtr tree;
tree = xmlNewChild(doc->children, NULL, (xmlChar *)"Parameter", NULL);
xmlSetProp(tree, (xmlChar *)"name", (xmlChar *)textParams[i]->name.c_str());
xmlSetProp(tree, (xmlChar *)"type", (xmlChar *)textParams[i]->type.c_str());
xmlSetProp(tree, (xmlChar *)"value", (xmlChar *)textParams[i]->value.c_str());
}
char *mem;
xmlDocDumpFormatMemory(doc,(xmlChar **)&mem,&size, 1);
xmlFreeDoc(doc);
return mem;
}
string UIDocument::findExternal(const string &filename, const char *searchPath, bool include_home, bool fullPathOutput)
{
vector<string> pathlist = envList(searchPath, include_home);
string fullname;
for (unsigned int i=0;i<pathlist.size();i++)
{
if (findExternalRecursive(pathlist[i],"", filename,fullname, fullPathOutput))
return fullname;
}
return "";
}
bool UIDocument::findExternalRecursive(const string &basePath, const string &path, const string &filename, string &fullname, bool fullPathOutput)
{
struct stat my_stat;
string dirPath = basePath + "/" + path;
DIR *my_directory = opendir (dirPath.c_str());
if (!my_directory) return false;
struct dirent *current_entry;
for (current_entry = readdir(my_directory);
current_entry != NULL; current_entry = readdir(my_directory)) {
string name = current_entry->d_name;
string fullpath = basePath + "/" + path + string("/") + name;
if (stat(fullpath.c_str(), &my_stat) < 0) {
//cerr<<"stat error"<<endl;
perror(fullpath.c_str());
continue;
}
if (S_ISDIR(my_stat.st_mode)) {
//it is a directory, let's doing it recursively
if (name != string("..") && name != string(".")) {
if (findExternalRecursive(basePath, path + "/" + name,filename,fullname, fullPathOutput))
{
closedir(my_directory);
return true;
}
}
}
else {
//it's a file, check if it's the right one
if (name == filename) {
if (fullPathOutput)
fullname = fullpath;
else
fullname = path + string("/") + name;
closedir(my_directory);
return true;
}
}
}
closedir(my_directory);
return false;
}
Network *UIDocument::buildExternal(const string &type, const string &_name, const ParameterSet ¶ms)
{
string fullname = findExternal(type + ".n");
if (fullname == "")
return NULL;
UIDocument doc(fullname);
//cout<<"loading : "<<fullpath<<endl;
doc.load();
UINetwork *net = doc.getNetworkNamed("MAIN");
if (net)
{
return net->build(_name, params);
}
else
{
throw new GeneralException("No MAIN network defined", __FILE__, __LINE__);
}
}
Network *UIDocument::build(const string &_name, const ParameterSet ¶ms)
{
cerr<<"Building network :"<<_name<<endl;
Network *net = NULL;
try {
UINetwork *uinet = getNetworkNamed("MAIN");
if (!uinet)
throw new GeneralException("No MAIN network defined", __FILE__, __LINE__);
//copy params
ParameterSet myParams = params;
//adding document params
for (int i = 0; i < textParams.size(); i++)
{
if (!myParams.exist(textParams[i]->name)) {
//create object
ObjectRef param_value = ObjectParam::stringParam(textParams[i]->type,textParams[i]->value,myParams);
//adding default param value if not specified into arguments
myParams.add(textParams[i]->name,param_value);
}
}
net = uinet->build(_name, myParams);
net->verifyConnect();
return net;
} catch (BaseException *e)
{
e->freeze();
if (net)
{
net->cleanupNotify();
delete net;
}
throw e;
} catch (...)
{
if (net)
{
net->cleanupNotify();
delete net;
}
throw;
}
}
void UIDocument::genCodeExternal(const string &type, ostream &out, int &id, set<string> &nodeList)
{
string fullname = findExternal(type+".n");
if (fullname == "")
throw new GeneralException(string("External node not found: ") + type, __FILE__, __LINE__);
UIDocument doc(fullname);
doc.load();
UINetwork *uinet = doc.getNetworkNamed("MAIN");
if (!uinet)
throw new GeneralException("No MAIN network defined", __FILE__, __LINE__);
uinet->genCode(out, id, nodeList);
}
set<string> UIDocument::genCode(ostream &out, const string &functName, bool localIncludes)
{
set<string> nodeList;
out << "//This code has been generated automatically using codeflow\n";
out << "//Note that automatic code generation is in a very experimental\n";
out << "// stage right now, use at your own risk\n";
if (localIncludes)
{
out << "#include \"Network.h\"\n";
out << "#include \"Iterator.h\"\n";
out << "#include \"object_param.h\"\n\n\n";
} else {
out << "#include <Network.h>\n";
out << "#include <Iterator.h>\n";
out << "#include <object_param.h>\n\n\n";
}
out << "using namespace std;\n";
out << "using namespace FD;\n\n\n";
int id=0;
UINetwork *uinet = getNetworkNamed("MAIN");
if (!uinet)
throw new GeneralException("No MAIN network defined", __FILE__, __LINE__);
uinet->genCode(out, id, nodeList);
out << "Network *" << functName << "(const string &_name, ParameterSet ¶ms)" << endl;
out << "{\n";
out << "\tNetwork *net = genNet0(_name, params);\n";
//Don't verify... in case we need other connections
//out << "\tnet->verifyConnect();\n";
out << "\treturn net;\n";
out << "}\n";
//cerr << "nodes used:\n";
//for (set<string>::iterator it=nodeList.begin();it!=nodeList.end();it++)
// cerr << *it << endl;
return nodeList;
}
//Run without a GUI
void UIDocument::run()
{
Network *net = NULL;
try {
ParameterSet params;
//cerr << "building net...\n";
net = build("MAIN", params);
if (net->getInputNode())
throw new GeneralException ("main network has input node", __FILE__, __LINE__);
//cerr << "initializing...\n";
net->initialize();
//cerr << "running (UIDocument)...\n";
for (int i = 0; ;i++) {
if (!net->hasOutput(i)) break;
cout << *net->getOutput(i,0);
}
} catch (BaseException *e)
{
e->print();
} catch (...)
{
cerr << "unknown exception caught" << endl;
}
if (net)
{
net->cleanupNotify();
delete net;
}
}
void UIDocument::run(ParameterSet &p)
{
Network *net=NULL;
try {
//cerr << "building net...\n";
net = build("MAIN", p);
if (net->getInputNode())
throw new GeneralException ("main network has input node", __FILE__, __LINE__);
//cerr << "initializing...\n";
net->initialize();
//cerr << "running (UIDocument)...\n";
for (int i = 0; ;i++)
{
if (!net->hasOutput(i))
break;
*net->getOutput(i,0);
}
}
catch (BaseException &e) {
e.print();
}
catch (BaseException *e)
{
e->print();
}
if (net)
{
net->cleanupNotify();
delete net;
}
}
void UIDocument::setFullPath(const string &fullpath)
{
//cerr << "fullpath is: \"" << fullpath << "\"" << endl;
int slashpos = fullpath.rfind("/");
//cerr << "slashpos = " << slashpos << endl;
path="";
path.append(fullpath,0,slashpos+1);
docName=fullpath;
docName.erase(0,slashpos+1);
//cerr << "path is: \"" << path << "\"" << endl;
//cerr << "name is: \"" << name << "\"" << endl;
untitled=false;
}
void UIDocument::updateNetInfo(UINetwork *net) {
//change our net information
subnetInfo.updateNetInfo(net);
//update "networks parameters that included this net as a node"
for (int i = 0; i < networks.size(); i++) {
if (networks[i]) {
networks[i]->updateAllSubnetParameters(net->getName(), subnetInfo.findNode(net->getName()));
}
}
}
void UIDocument::updateAllNetworks() {
//update network information
for (unsigned int i=0;i<networks.size();i++) {
//subnetInfo.updateNetInfo(networks[i]);
updateNetInfo(networks[i]);
}
}
void UIDocument::updateAllSubnetTerminals(const string _nettype, const string _terminalname,
UINetTerminal::NetTermType _terminaltype, bool _remove)
{
for (unsigned int i = 0; i < networks.size(); i++)
{
if (networks[i])
networks[i]->updateAllSubnetTerminals(_nettype, _terminalname, _terminaltype, _remove);
}
}
void UIDocument::exportNetwork(const std::string &networkName, const std::string &fileName) {
UINetwork *net = getNetworkNamed(networkName);
if (net) {
int save_fd = open(fileName.c_str(), O_CREAT|O_WRONLY|O_TRUNC, 00755);
if (save_fd == -1) {
error("UIDocument::exportNetwork : Error while saving file: cannot open");
return;
}
fd_ostream outFile(save_fd);
if (outFile.fail()) {
error("UIDocument::exportNetwork : Error while saving file");
return;
}
xmlDocPtr doc;
doc = xmlNewDoc((xmlChar *)"1.0");
doc->children = xmlNewDocNode(doc, NULL, (xmlChar *)"Document", NULL);
//Export network
net->saveXML(doc->children);
char *mem = NULL;
int size = 0;
//Dump to memory
xmlDocDumpFormatMemory(doc,(xmlChar **)&mem,&size, 1);
xmlFreeDoc(doc);
//Write to file
outFile.write(mem, size);
if (outFile.fail()) {
free(mem);
error("UIDocument::exportNetwork : Error while saving file");
return;
}
//Free memory
free(mem);
}
else {
//TODO throw an exception ?
throw new GeneralException(string("Network does not exist :") + networkName,__FILE__,__LINE__);
}
}
void UIDocument::importNetwork(const std::string &fileName) {
//TODO Merge stuff with load to avoid duplication of the code.
//The difference is that we do not reset document data before loading
//and we do not create a "MAIN" network.
string docStr;
ifstream inputFile(fileName.c_str());
if (!inputFile.fail()) {
char ch;
inputFile >> ch;
if (ch=='#')
{
//let's read the leading characters (script like format)
while (ch != '<')
{
inputFile >> ch;
if (inputFile.fail())
{
error("Error: this doesn't look like an FlowDesigner document");
resetModified();
return;
}
}
} else if (ch!='<')
{
error("Error: this doesn't look like an FlowDesigner document");
resetModified();
return;
}
string xmlStr;
inputFile >> xmlStr;
if (xmlStr != "?xml")
{
error("Error: this doesn't look like an FlowDesigner document");
resetModified();
return;
}
//put back xml starting string
docStr = "<?xml";
//read all data from file
while(1) {
char buff[1025];
inputFile.read(buff, 1024);
buff[1024]=0;
if (inputFile.fail()) {
docStr.append(buff, inputFile.gcount());
break;
}
docStr.append(buff, 1024);
}
try {
//loading document
xmlDocPtr doc = xmlParseMemory (const_cast<char *> (docStr.c_str()), docStr.size());
if (!doc || !doc->children || !doc->children->name) {
throw new GeneralException(string("Corrupted XML in file ") + fileName,__FILE__,__LINE__);
}
xmlNodePtr net = doc->children->children;
if (net) {
//loading all networks
while (net != NULL) {
//Standard network
if (string((char*)net->name) == "Network") {
addNetwork (net);
}
//File included (prototype)
if (string((char*)net->name) == "IncludeNetwork") {
cerr<<"Warning, included network is still a prototype, use at your own risk"<<endl;
xmlChar *fname = xmlGetProp(net,(xmlChar *)"file");
if (fname) {
cerr<<"(Recursive) Including : "<<(char*) fname<<endl;
try {
//let's import the network
importNetwork(string((char*) fname));
}
catch(BaseException *e) {
e->print(cerr);
delete e;
}
free(fname);
}//Valid filename
}//IncludeNetwork
net = net->next;
}//while net
}
//free XML data
xmlFreeDoc(doc);
//the network is modified
setModified();
}
catch (BaseException *e) {
throw e->add(new GeneralException(string("Unable to import from file ") + fileName,__FILE__,__LINE__));
}
}
else {
throw new GeneralException(string("File does not exist : ") + fileName,__FILE__,__LINE__);
}
}
}
syntax highlighted by Code2HTML, v. 0.9.1