move woc
authorkonrad <konrad@6e3c4bff-ac9f-4ac1-96c5-d2ea494d3e33>
Fri, 15 Jan 2010 18:56:08 +0000 (18:56 +0000)
committerkonrad <konrad@6e3c4bff-ac9f-4ac1-96c5-d2ea494d3e33>
Fri, 15 Jan 2010 18:56:08 +0000 (18:56 +0000)
git-svn-id: https://silmor.de/svn/softmagic/pack/trunk@420 6e3c4bff-ac9f-4ac1-96c5-d2ea494d3e33

12 files changed:
woc/htmlout.cpp [new file with mode: 0644]
woc/htmlout.h [new file with mode: 0644]
woc/mfile.cpp [new file with mode: 0644]
woc/mfile.h [new file with mode: 0644]
woc/phpout.cpp [new file with mode: 0644]
woc/phpout.h [new file with mode: 0644]
woc/processor.cpp [new file with mode: 0644]
woc/processor.h [new file with mode: 0644]
woc/qtout.cpp [new file with mode: 0644]
woc/qtout.h [new file with mode: 0644]
woc/woc.cpp [new file with mode: 0644]
woc/woc.pro [new file with mode: 0644]

diff --git a/woc/htmlout.cpp b/woc/htmlout.cpp
new file mode 100644 (file)
index 0000000..3ef5503
--- /dev/null
@@ -0,0 +1,390 @@
+//
+// C++ Implementation: qtout
+//
+// Description: 
+//
+//
+// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2009
+//
+// Copyright: See README/COPYING files that come with this distribution
+//
+//
+
+#include "htmlout.h"
+
+#include <QDir>
+#include <QDomElement>
+
+WocHtmlOut::WocHtmlOut(QDomElement&el)
+{
+       qDebug("Info: creating Html Output Generator.");
+       WocProcessor*woc=WocProcessor::instance();
+       m_basedir=woc->baseDir()+"/"+el.attribute("sourceDir",".");
+       m_subdir=el.attribute("subDir","htmlwob");
+       bool clean=str2bool(el.attribute("clean","0"));
+       //cleanup directory (remove normal files, assume remainder is harmless)
+       QDir d(m_basedir+"/"+m_subdir);
+       if(d.exists() && clean){
+               QStringList ent=d.entryList(QDir::Files);
+               for(int i=0;i<ent.size();i++)
+                       d.remove(ent[i]);
+       }
+       //get/create directory
+       if(!d.exists())QDir(".").mkpath(m_basedir+"/"+m_subdir);
+       
+       //create index file
+       m_index.setFileName(m_basedir+"/"+m_subdir+"/index.html");
+       if(!m_index.open(QIODevice::WriteOnly|QIODevice::Truncate)){
+               qDebug("Error: cannot create HTML index file %s.",m_index.fileName().toAscii().data());
+               emit errorFound();
+               return;
+       }
+       m_index.write(QByteArray("<html>\n<head>\n"));
+       m_index.write(QByteArray("<title>Project Index</title>\n"));
+       
+       m_index.write(QByteArray("</head><body>\n"));
+       //write project info
+       QString inf="<h1>Project "+woc->projectName()+"</h1>\n";
+       inf+="Human Readable Version: "+woc->verHR()+"<br/>";
+       inf+="Communication Layer Version: "+woc->verComm()+"<br/>";
+       inf+="Minimum Compatible Version: "+woc->verNeedComm()+"<p/>";
+       
+       inf+="SVN Repository URL: "+woc->svnRepositoryUrl()+"<br/>";
+       inf+="SVN Repository Root: "+woc->svnRepositoryRoot()+"<br/>";
+       inf+="SVN Revision: "+woc->svnRevision()+"<p/>";
+       
+       inf+="Database Instance Object: "+woc->dbInst()+"<br/>";
+       inf+="Database Schema Object: "+woc->dbSchema()+"<br/>";
+       inf+="Database Schema Version: "+woc->dbVersion()+"<p/>";
+       
+       m_index.write(inf.toAscii());
+       
+       //write global docu
+       QStringList dcs=woc->docStrings();
+       for(int i=0;i<dcs.size();i++){
+               inf="<p>"+dcs[i]+"</p>\n";
+               m_index.write(inf.toAscii());
+       }
+}
+
+WocHtmlOut::~WocHtmlOut(){}
+
+void WocHtmlOut::finalize()
+{
+       //TODO: write index table content
+       m_index.write(QByteArray("<h1>Index</h1>\n"));
+       m_index.write(QByteArray("<table><tr><td><h2>Classes</h2></td><td><h2>Transactions</td><td><h2>Tables</h2></td></tr>\n"));
+       m_index.write(QByteArray("<tr><td valign=\"top\"><ul>\n"));
+       QStringList sl=WocProcessor::instance()->classNames();
+       QString s;
+       qSort(sl);
+       for(int i=0;i<sl.size();i++)
+               s+="<li><a href=\"class-"+sl[i]+".html\">"+sl[i]+"</a></li>\n";
+       m_index.write(s.toAscii());
+       
+       m_index.write(QByteArray("</ul></td><td valign=\"top\"><ul>"));
+       sl=WocProcessor::instance()->transactionNames();
+       s="";
+       qSort(sl);
+       for(int i=0;i<sl.size();i++)
+               s+="<li><a href=\"trn-"+sl[i]+".html\">"+sl[i]+"</a></li>\n";
+       m_index.write(s.toAscii());
+       
+       m_index.write(QByteArray("</ul></td><td valign=\"top\"><ul>"));
+       sl=WocProcessor::instance()->tableNames();
+       s="";
+       qSort(sl);
+       for(int i=0;i<sl.size();i++)
+               s+="<li><a href=\"table-"+sl[i]+".html\">"+sl[i]+"</a></li>\n";
+       m_index.write(s.toAscii());
+       
+       m_index.write(QByteArray("</ul></td></tr></table>\n"));
+       
+       m_index.write(QByteArray("\n</body></html>\n"));
+       m_index.close();
+}
+
+void WocHtmlOut::newTable(const WocTable&tbl)
+{
+       QString cn=tbl.name();
+       QFile htm(m_basedir+"/"+m_subdir+"/table-"+cn+".html");
+       if(!htm.open(QIODevice::WriteOnly|QIODevice::Truncate)){
+               qDebug("Error: cannot create html file for table %s.",cn.toAscii().data());
+               emit errorFound();
+               return;
+       }
+       //lead in
+       htm.write(QString("<html><title>Table "+cn+"</title><body>\n").toAscii());
+       
+       QString hcd;
+       //table declaration
+       hcd+="<h1>Table "+cn+"</h1>\n";
+       
+       if(tbl.isAuditable()){
+               hcd+="<p>This table is audited, see <a href=\"table-"+tbl.name()+"_audit.html\">"
+                       +tbl.name()+"_audit</a> for details.</p>\n";
+       }
+       
+       QStringList td=tbl.docStrings();
+       for(int i=0;i<td.size();i++)
+               hcd+="<p>"+td[i]+"</p>\n";
+       
+       hcd+="<table frame=\"1\" border=\"1\"><tr><td><b>Column Name</b></td><td><b>Type</b></td><td><b>Properties</b></td><td><b>Docu</b></td></tr>\n";
+       QStringList cl=tbl.columns();
+       for(int i=0;i<cl.size();i++){
+               hcd+="<tr><td>"+cl[i]+"</td><td>"+tbl.columnType(cl[i])+"</td><td>";
+               if(tbl.columnIsPrimary(cl[i]))
+                       hcd+="Primary-Key ";
+               if(tbl.columnIsNull(cl[i]))
+                       hcd+="NULL-able ";
+               if(tbl.columnHasDefault(cl[i]))
+                       hcd+="default=\""+tbl.columnDefault(cl[i])+"\" ";
+               if(tbl.columnIsForeign(cl[i])){
+                       QStringList cf=tbl.columnForeign(cl[i]).split(":");
+                       hcd+="Foreign-Key=<a href=\"table-"+cf[0]+".html\">"+cf[0]+"("+cf[1]+")</a> ";
+               }
+               if(tbl.columnIsIndexed(cl[i]))
+                       hcd+="Indexed ";
+               if(tbl.columnIsUnique(cl[i]))
+                       hcd+="Unique";
+               hcd+="</td><td>"+tbl.columnDoc(cl[i])+"</td></tr>\n";
+       }
+       hcd+="</table>\n";
+       
+       //enums
+       for(int i=0;i<cl.size();i++){
+               QList<WocEnum >lst=tbl.columnEnums(cl[i]);
+               if(lst.size()>0){
+                       hcd+="<h2>Enum for column "+cl[i]+"</h2>\n<ul>\n";
+                       for(int j=0;j<lst.size();j++){
+                               hcd+="<li>"+lst[j].name+"="+QString::number(lst[j].val);
+                               if(lst[j].doc!="")hcd+="<br/>"+lst[j].doc;
+                               hcd+="</li>\n";
+                       }
+                       hcd+="</ul>\n";
+               }
+       }
+       
+       //foreign getters
+       cl=tbl.foreigns();
+       if(cl.size()>0){
+               hcd+="<h2>Foreign Getters</h2>\n<ul>";
+               for(int i=0;i<cl.size();i++){
+                       hcd+="<li>"+cl[i];
+                       QString s=tbl.foreignDoc(cl[i]);
+                       if(s!="")hcd+="<br/>"+s;
+                       hcd+="</li>\n";
+               }
+       }
+       
+       //presets
+       QList<QMap<QString,QString> >pre=tbl.presets();
+       if(pre.size()>0){
+               hcd+="<h2>Presets</h2>\n<table frame=\"1\" border=\"1\">\n<tr>";
+               cl=tbl.columns();
+               for(int i=0;i<cl.size();i++)
+                       hcd+="<td><b>"+cl[i]+"</b></td>";
+               hcd+="</tr>\n";
+               for(int i=0;i<pre.size();i++){
+                       hcd+="<tr>";
+                       for(int j=0;j<cl.size();j++){
+                               hcd+="<td>";
+                               if(pre[i].contains(cl[j]))
+                                       hcd+=pre[i][cl[j]];
+                               hcd+="</td>";
+                       }
+                       hcd+="</tr>\n";
+               }
+               hcd+="</table>\n";
+       }
+       
+       hcd+="</body></html>\n";
+       htm.write(hcd.toAscii());
+}
+
+void WocHtmlOut::newClass(const WocClass&cls)
+{
+       QString cn=cls.name();
+       QFile htm(m_basedir+"/"+m_subdir+"/class-"+cn+".html");
+       if(!htm.open(QIODevice::WriteOnly|QIODevice::Truncate)){
+               qDebug("Error: cannot create html file for class %s.",cn.toAscii().data());
+               emit errorFound();
+               return;
+       }
+       //lead in
+       htm.write(QString("<html><title>Class "+cn+"</title><body>\n").toAscii());
+       
+       QString hcd;
+       //class declaration
+       hcd+="<h1>";
+       if(cls.isAbstract(""))hcd+="Abstract ";
+       hcd+="Class "+cn+"</h1>\n";
+       
+       //conditional abstract
+       QStringList ab=cls.abstractLangs();
+       if(ab.size()>0){
+               hcd+="<p>The class is conditionally abstract in: ";
+               for(int i=0;i<ab.size();i++){
+                       if(i)hcd+=", ";
+                       hcd+=ab[i];
+               }
+               hcd+="</p>\n";
+       }
+       
+       //docu
+       QStringList doc=cls.docStrings();
+       for(int i=0;i<doc.size();i++)
+               hcd+="<p>"+doc[i]+"</p>\n";
+       htm.write(hcd.toAscii());
+       
+       //enums
+       classEnums(cls,htm);
+       
+       //properties
+       classProperties(cls,htm);
+       
+       //mappings
+       classMappings(cls,htm);
+       
+       //lead out
+       htm.write(QByteArray("</body></html>\n"));
+}
+
+void WocHtmlOut::classEnums(const WocClass&cls,QFile&hdr)
+{
+       QStringList k=cls.enumTypes();
+       if(k.size()==0)return;
+       QString hcd="<h2>Enums</h2>\n";
+       for(int i=0;i<k.size();i++){
+               //type
+               hcd+="<h3>enum "+k[i]+"</h3>\n";
+               hcd+="<table frame=\"1\" border=\"1\">";
+               hcd+="<tr><td><b>Symbol</b></td><td><b>Value</b></td><td><b>Docu</b></td></tr>\n";
+               QList<WocEnum>ev=cls.enumValues(k[i]);
+               for(int j=0;j<ev.size();j++){
+                       hcd+="<tr><td>"+ev[j].name+"</td><td>"+QString::number(ev[j].val)
+                          +"</td><td>"+ev[j].doc+"</td></tr>\n";
+               }
+               hcd+="</table>\n";
+       }
+       hdr.write(hcd.toAscii());
+}
+
+void WocHtmlOut::classProperties(const WocClass&cls,QFile&hdr)
+{
+       QStringList k=cls.propertyNames();
+       if(k.size()==0)return;
+       QString hcd;
+       //declare members
+       hcd="<h2>Properties</h2>\n<ul>\n";
+       for(int i=0;i<k.size();i++){
+               hcd+="<li>"+k[i]+" (";
+               if(cls.propertyIsObject(k[i]))
+                       hcd+="<a href=\"class-"+cls.propertyPlainType(k[i])+".html\">";
+               hcd+=cls.propertyType(k[i]);
+               if(cls.propertyIsObject(k[i]))
+                       hcd+="</a>";
+               hcd+=")";
+               QString d=cls.propDoc(k[i]);
+               if(d!="")hcd+="<br/>"+d;
+               hcd+="</li>\n";
+       }
+       hcd+="</ul>\n";
+       //write
+       hdr.write(hcd.toAscii());
+}
+
+void WocHtmlOut::classMappings(const WocClass&cls,QFile&hdr)
+{
+       QStringList k=cls.mappingTables();
+       if(k.size()==0)return;
+       QString hcd;
+       for(int i=0;i<k.size();i++){
+               hcd+="<h2>Mapping for Table <a href=\"table-"+k[i]+".html\">"+k[i]+"</a></h2>\n";
+               hcd+="<table frame=\"1\" border=\"1\">\n";
+               hcd+="<tr><td><b>Property</b></td><td><b>Column</b></td></tr>\n";
+               QMap<QString,QString> map=cls.mapping(k[i]);
+               QStringList k2=map.keys();
+               for(int j=0;j<k2.size();j++){
+                       hcd+="<tr><td>"+k2[j]+"</td><td>"+map[k2[j]]+"</td></tr>\n";
+               }
+               hcd+="</table>\n";
+       }
+       
+       hdr.write(hcd.toAscii());
+}
+
+void WocHtmlOut::newTransaction(const WocTransaction&trn)
+{
+       QString cn=trn.name();
+       QFile htm(m_basedir+"/"+m_subdir+"/trn-"+cn+".html");
+       if(!htm.open(QIODevice::WriteOnly|QIODevice::Truncate)){
+               qDebug("Error: cannot create HTML file for transaction %s.",cn.toAscii().data());
+               emit errorFound();
+               return;
+       }
+       //basics
+       QStringList in=trn.inputNames();
+       QStringList out=trn.outputNames();
+       QStringList doc=trn.docStrings();
+       //lead in
+       QString hcd;
+       hcd="<html><title>Transaction "+cn+"</title><body>\n<h1>Transaction "+cn+"</h1>\n";
+       //auth mode
+       hcd+="<p>Authentication mode: ";
+       switch(trn.authMode()){
+               case WocTransaction::Checked:hcd+="Checked (known user, must have the privilege)";break;
+               case WocTransaction::Auth:hcd+="Authenticated (known user, any/no privileges)";break;
+               case WocTransaction::Open:hcd+="Open (unauthenticated, any user)";break;
+               default:hcd+="Ooops. Unknown Mode.";break;
+       }
+       hcd+="</p>\n";
+       //docu
+       for(int i=0;i<doc.size();i++)
+               hcd+="<p>"+doc[i]+"</p>\n";
+       //in/out
+       hcd+="<h2>Inputs:</h2>\n<ul>\n";
+       for(int i=0;i<in.size();i++){
+               hcd+="<li>"+in[i]+": ";
+               QString t=trn.inputType(in[i]);
+               if(trn.isObjectType(t))
+                       hcd+="<a href=\"class-"+trn.plainType(t)+".html\">";
+               hcd+=t;
+               if(trn.isObjectType(t))
+                       hcd+="</a>";
+               //add docu
+               t=trn.inputDoc(in[i]);
+               if(t!="")hcd+="<br/>"+t;
+               hcd+="</li>\n";
+       }
+       hcd+="</ul>\n<h2>Outputs:</h2>\n<ul>\n";
+       for(int i=0;i<out.size();i++){
+               hcd+="<li>"+out[i]+": ";
+               QString t=trn.outputType(out[i]);
+               if(trn.isObjectType(t))
+                       hcd+="<a href=\"class-"+trn.plainType(t)+".html\">";
+               hcd+=t;
+               if(trn.isObjectType(t))
+                       hcd+="</a>";
+               //add docu
+               t=trn.outputDoc(out[i]);
+               if(t!="")hcd+="<br/>"+t;
+               hcd+="</li>\n";
+       }
+       hcd+="</ul>\n";
+       //privileges
+       QStringList pri=trn.privileges();
+       if(pri.size()){
+               hcd+="<h2>Privileges</h2>\n<ul>\n";
+               for(int i=0;i<pri.size();i++){
+                       hcd+="<li>"+pri[i];
+                       QString d=trn.privilegeDoc(pri[i]);
+                       if(d!="")
+                               hcd+="<br/>"+d;
+                       hcd+="</li>\n";
+               }
+               hcd+="</ul>\n";
+       }
+       hcd+="</body></html>\n";
+       htm.write(hcd.toAscii());
+}
diff --git a/woc/htmlout.h b/woc/htmlout.h
new file mode 100644 (file)
index 0000000..c3650d9
--- /dev/null
@@ -0,0 +1,45 @@
+//
+// C++ Interface: qtout
+//
+// Description: 
+//
+//
+// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2009
+//
+// Copyright: See README/COPYING files that come with this distribution
+//
+//
+
+#ifndef WOC_HTMLOUT_H
+#define WOC_HTMLOUT_H
+
+#include "processor.h"
+
+#include <QFile>
+
+class QDomElement;
+
+class WocHtmlOut:public WocOutput
+{
+       public:
+               WocHtmlOut(QDomElement&);
+               ~WocHtmlOut();
+       protected:
+               virtual void finalize();
+               virtual void newClass(const WocClass&);
+               virtual void newTable(const WocTable&);
+               virtual void newTransaction(const WocTransaction&);
+       private:
+               QString m_basedir,m_subdir;
+               QFile m_index;
+               
+               /**helper: generate enums for classes*/
+               void classEnums(const WocClass&,QFile&);
+               /**helper: generate properties*/
+               void classProperties(const WocClass&,QFile&);
+               /**helper: generate mappings*/
+               void classMappings(const WocClass&,QFile&);
+               
+};
+
+#endif
diff --git a/woc/mfile.cpp b/woc/mfile.cpp
new file mode 100644 (file)
index 0000000..e6c0c9a
--- /dev/null
@@ -0,0 +1,85 @@
+//
+// C++ Implementation: mfile
+//
+// Description: 
+//
+//
+// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2010
+//
+// Copyright: See README/COPYING files that come with this distribution
+//
+//
+
+#include "mfile.h"
+
+#include <QFileInfo>
+
+//static 
+QMap<QString,QStringList> MFile::touched;
+
+MFile::MFile(QString n,QObject*parent):QFile(n+",new",parent),name(n){isopen=false;}
+MFile::MFile(QObject *parent):QFile(parent){isopen=false;}
+MFile::~MFile()
+{
+       if(isopen)close();
+}
+
+void MFile::close()
+{
+//     qDebug("Info: closing %s",name.toAscii().data());
+       //compare
+       bool ident=false;
+       if(isopen){
+               QFile::close();
+               QFile::open(QIODevice::ReadOnly);
+               QByteArray here=readAll();
+               QFile there(name);
+               if(there.open(QIODevice::ReadOnly)){
+                       ident=(here == there.readAll());
+                       there.close();
+               }
+       }
+       //actual close
+       QFile::close();
+       //move
+       if(isopen){
+               isopen=false;
+               if(ident){
+                       //if identical: remove new version to preserve file time
+                       remove();
+               }else{
+                       //if not identical: use new version, remove old one
+                       QFile(name).remove();
+                       QFile(name+",new").rename(name);
+               }
+       }
+}
+
+void MFile::setFileName(QString n)
+{
+       name=n;
+       QFile::setFileName(n+",new");
+}
+
+bool MFile::open(QIODevice::OpenMode m)
+{
+       isopen=QFile::open(m);
+       if(isopen){
+               //remember it
+               QFileInfo fi(name);
+               QString p=fi.absolutePath();
+               QString f=fi.fileName();
+               if(!touched.contains(p))touched.insert(p,QStringList());
+               if(!touched[p].contains(f))touched[p]<<f;
+       }
+       return isopen;
+}
+
+bool MFile::touchedFile(QString name)
+{
+       QFileInfo fi(name);
+       QString p=fi.absolutePath();
+       QString f=fi.fileName();
+       if(!touched.contains(p))return false;
+       return touched[p].contains(f);
+}
diff --git a/woc/mfile.h b/woc/mfile.h
new file mode 100644 (file)
index 0000000..44a3d38
--- /dev/null
@@ -0,0 +1,46 @@
+//
+// C++ Interface: mfile
+//
+// Description: 
+//
+//
+// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2010
+//
+// Copyright: See README/COPYING files that come with this distribution
+//
+//
+
+#ifndef WOC_MFILE_H
+#define WOC_MFILE_H
+
+#include <QFile>
+#include <QMap>
+#include <QStringList>
+
+/**overwrites QFile to only generate a file if its new version differs from an existing one, it creates a temporary file with ",new" added to the file name; additionally it records all file names that it touches;
+this is not a complete implementation - just enough for woc*/
+class MFile:public QFile
+{
+       public:
+               MFile(QString name,QObject*parent=0);
+               MFile(QObject *parent=0);
+               ~MFile();
+               
+               virtual void close();
+               virtual bool open(QIODevice::OpenMode m);
+               
+               QString fileName()const{return name;}
+               void setFileName(QString n);
+               
+               static bool touchedFile(QString);
+       private:
+               //flag to show whether file is open
+               bool isopen;
+               //original name of the file
+               QString name;
+               
+               //stores touched files
+               static QMap<QString,QStringList> touched;
+};
+
+#endif
diff --git a/woc/phpout.cpp b/woc/phpout.cpp
new file mode 100644 (file)
index 0000000..5ccb84b
--- /dev/null
@@ -0,0 +1,1103 @@
+//
+// C++ Implementation: phpout
+//
+// Description: 
+//
+//
+// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2009
+//
+// Copyright: See README/COPYING files that come with this distribution
+//
+//
+
+#include "phpout.h"
+
+#include <QDir>
+#include <QDomElement>
+
+static const QByteArray PHPSTART("<?\n//THIS IS AN AUTOGENERATED FILE, DONT CHANGE!\n\n");
+static const QByteArray PHPEND("\n//END OF AUTOGENERATED FILE\n?>");
+
+static const QByteArray SCHEMASTART("class WobSchema extends WobSchemaBase\n{\nfunction __construct(){\n");
+static const QByteArray SCHEMAEND("}};\n");
+
+static const QByteArray TRANSACTCLASS("class WobTransaction extends WobTransactionBase\n{\n");
+static const QByteArray TRANSACTSTART("  static public function handle(){switch(WobTransactionBase::getTransactionName()){\n");
+static const QByteArray TRANSACTEND("\tdefault:WobTransactionBase::noSuchTransaction();break;\n  }}\n");
+
+WocPHPServerOut::WocPHPServerOut(const QDomElement&el)
+{
+       qDebug("Info: creating PHP Server Output Generator.");
+       m_basedir=WocProcessor::instance()->baseDir()+"/"+el.attribute("sourceDir",".");
+       m_subdir=el.attribute("subDir","phpwob");
+       m_fileext=el.attribute("extension",".inc");
+       QList<QDomElement> nl=elementsByTagName(el,"Authenticator");
+       if(nl.size()){
+               QDomElement el2=nl.at(0).toElement();
+               m_isauth=el2.attribute("isAuthenticated","false");
+               m_hasrole=el2.attribute("hasRole","false");
+               m_username=el2.attribute("userName","\"\"");
+               m_authinit=el2.attribute("init","");
+       }else{
+               m_isauth="false";
+               m_hasrole="false";
+               m_username="\"\"";
+       }
+       //cleanup directory (remove normal files, assume remainder is harmless)
+       QDir d(m_basedir+"/"+m_subdir);
+       if(d.exists() && str2bool(el.attribute("clean","0"))){
+               QStringList ent=d.entryList(QDir::Files);
+               for(int i=0;i<ent.size();i++)
+                       d.remove(ent[i]);
+       }
+       //get/create directory
+       if(!d.exists())QDir(".").mkpath(m_basedir+"/"+m_subdir);
+       //create loader
+       m_loader.setFileName(m_basedir+"/"+m_subdir+"/autoload"+m_fileext);
+       if(!m_loader.open(QIODevice::ReadWrite|QIODevice::Truncate)){
+               qDebug("Error: PHP Server Generator - cannot create loader file, deactivating output.");
+               emit errorFound();
+               return;
+       }
+       m_loader.write(PHPSTART);
+       //create schema file
+       m_schema.setFileName(m_basedir+"/"+m_subdir+"/schema"+m_fileext);
+       if(!m_schema.open(QIODevice::ReadWrite|QIODevice::Truncate)){
+               qDebug("Error: PHP Server Generator - cannot create DB schema file.");
+               emit errorFound();
+               return;
+       }
+       m_schema.write(PHPSTART);
+       m_schema.write(SCHEMASTART);
+       m_schema.write(("\t$this->sversion=\""+WocProcessor::instance()->dbVersion()+"\";\n").toAscii());
+       addLoad("WobSchema","schema");
+       //create Transaction file
+       m_transact.setFileName(m_basedir+"/"+m_subdir+"/transaction"+m_fileext);
+       if(!m_transact.open(QIODevice::ReadWrite|QIODevice::Truncate)){
+               qDebug("Error: PHP Server Generator - cannot create transaction processor file.");
+               emit errorFound();
+               return;
+       }
+       m_transact.write(PHPSTART);
+       m_transact.write(TRANSACTCLASS);
+       transInfo();
+       m_transact.write(TRANSACTSTART);
+       addLoad("WobTransaction","transaction");
+}
+
+void WocPHPServerOut::transInfo()
+{
+       WocProcessor*woc=WocProcessor::instance();
+       m_transact.write(QString("  static public function commVersion(){return \""+woc->verComm()+"\";}\n").toAscii());
+       m_transact.write(QString("  static public function needCommVersion(){return \""+woc->verNeedComm()+"\";}\n").toAscii());
+       m_transact.write(QString("  static public function version(){return \""+woc->verHR()+"\";}\n").toAscii());
+       m_transact.write(QString("  static public function svnVersion(){return \""+woc->svnRevision()+"\";}\n\n").toAscii());
+       m_transact.write(QString("  static public function svnRepositoryRoot(){return \""+woc->svnRepositoryRoot()+"\";}\n\n").toAscii());
+       m_transact.write(QString("  static public function svnRepositoryUrl(){return \""+woc->svnRepositoryUrl()+"\";}\n\n").toAscii());
+}
+
+void WocPHPServerOut::transInfo2()
+{
+       WocProcessor*woc=WocProcessor::instance();
+       //transaction names
+       QString code="  static public function transactionNames(){\n\treturn array(";
+       QStringList tns=woc->transactionNames();
+       for(int i=0;i<tns.size();i++){
+               if(i)code+=",";
+               code+="\n\t\ttranslate(\"_TransactionNames\",\""+tns[i]+"\")";
+       }
+       code+=");\n  }\n";
+       //privilege names
+       code+="static public function privilegeNames(){\n\treturn array(";
+       QStringList priv=woc->privilegeNames();
+       for(int i=0;i<priv.size();i++){
+               if(i)code+=",\n\t\t";else code+="\n\t\t";
+               code+="translate(\"_PrivilegeNames\",\""+priv[i]+"\")";
+       }
+       code+="\n\t);\n}\n";
+       
+       m_transact.write(code.toAscii());
+}
+
+void WocPHPServerOut::finalize()
+{
+       if(m_loader.isOpen()){
+               m_loader.write(PHPEND);
+               m_loader.close();
+       }
+       if(m_schema.isOpen()){
+               m_schema.write(SCHEMAEND);
+               m_schema.write(PHPEND);
+               m_schema.close();
+       }
+       if(m_transact.isOpen()){
+               m_transact.write(TRANSACTEND);
+               transInfo2();
+               m_transact.write("};\n");
+               m_transact.write(PHPEND);
+               m_transact.close();
+       }
+}
+
+void WocPHPServerOut::newTable(const WocTable&tbl)
+{
+       if(!m_loader.isOpen())return;
+       WocProcessor *woc=WocProcessor::instance();
+       //create table file
+       QString fn=m_subdir+"/wt_"+tbl.name()+m_fileext;
+       QFile tf(m_basedir+"/"+fn);
+       if(!tf.open(QIODevice::ReadWrite|QIODevice::Truncate)){
+               qDebug("Error: cannot create PHP table file %s.",fn.toAscii().data());
+               emit errorFound();
+               return;
+       }
+       tf.write(PHPSTART);
+       QString code="/* TRANSLATOR WT"+tbl.name()+" */\nclass WT"+tbl.name()+" extends "+tbl.baseClass()+"\n{\n";
+       //initializer
+       code+="protected function __construct(array $data,$isfromdb){parent::__construct($data,$isfromdb,\""+tbl.name()+"\");}\n\n";
+       
+       //static new instance for insert only
+       code+="public static function newRow(array $data=array()){return new WT"+tbl.name()+"($data,false);}\n\n";
+       
+       //static get instance
+       QStringList cols=tbl.columns();
+       QStringList pcols=tbl.primaryColumns();
+        //header
+       code+="public static function getFromDB(";
+       for(int i=0;i<pcols.size();i++){
+               if(i)code+=",";
+               code+="$"+pcols[i];
+       }
+       QString dbi="$"+woc->dbInst();
+        //DB query
+       code+="){\n\tglobal "+dbi+";\n\t$res="+dbi+"->select(\""+tbl.name()+"\",\"*\",\"";
+       for(int i=0;i<pcols.size();i++){
+               if(i)code+=" AND ";
+               code+=pcols[i]+"=\"."+dbi+"->escapeColumn(\""+tbl.name()+"\",\""+pcols[i]+"\",$"+pcols[i]+").\"";
+       }
+       code+="\");\n";
+        //check result
+       code+="\tif($res===false)return false;\n\tif(count($res)<1)return false;\n\telse return new WT"+tbl.name()+"($res[0],true);\n}\n\n";
+       
+       //static get selection
+       code+="public static function selectFromDB($where=\"\",$orderby=\"\"){\n\tglobal "+dbi+";\n\t$res="+dbi+"->select(\""+tbl.name()+"\",\"*\",$where,$orderby);\n\tif($res===false || count($res)<1)return array();\n\t";
+       code+="$r=array();\n\tforeach($res as $row)\n\t\t$r[]=new WT"+tbl.name()+"($row,true);\n\treturn $r;\n}\n\n";
+       
+       //go through columns, generate specific code
+       for(int i=0;i<cols.size();i++){
+               //automatic resolution of internal foreign keys
+               if(tbl.columnIsForeign(cols[i])){
+                       code+="public function getObjectFor"+cols[i]+"(){\n\tglobal "+dbi+";\n\treturn WT";
+                       QStringList foreign=tbl.columnForeign(cols[i]).split(":");
+                       code+=foreign[0]+"::selectFromDB(\""+foreign[1]+"=\"."+dbi+"->escapeColumn(\""+foreign[0]+"\",\""+foreign[1]+"\",$this->"+cols[i]+"));\n}\n\n";
+               }
+               //implement enum check for set method of enum columns
+               if(tbl.columnType(cols[i]).startsWith("enum")){
+                       code+="protected function verifyValue"+cols[i]+"($v){if(false";
+                       QList<WocEnum>ens=tbl.columnEnums(cols[i]);
+                       QList<int>envs;
+                       for(int j=0;j<ens.size();j++){
+                               int v=ens[j].val;
+                               if(envs.contains(v))continue;
+                               envs.append(v);
+                               code+="||$v=="+QString::number(v);
+                       }
+                       code+=")return true;else return false;}\n\n";
+               }
+       }
+       
+       //reverse resolution of configured foreign keys
+       QStringList fs=tbl.foreigns();
+       for(int i=0;i<fs.size();i++){
+               QString via=tbl.foreignQuery(fs[i]);
+               //parse via
+               QStringList v1=via.split("=");
+               if(v1.size()!=2){
+                       qDebug("Warning: Foreign clause %s of table %s has illegal syntax. Should be foreigntable:column=localcolumn.",fs[i].toAscii().data(),tbl.name().toAscii().data());
+                       continue;
+               }
+               QString local=v1[1].trimmed();
+               QStringList foreign=v1[0].split(":");
+               if(foreign.size()!=2){
+                       qDebug("Warning: Foreign clause %s of table %s has illegal syntax. Should be foreigntable:column=localcolumn.",fs[i].toAscii().data(),tbl.name().toAscii().data());
+                       continue;
+               }
+               code+="public function "+fs[i]+"(){\n\tglobal "+dbi+";\n\treturn WT"+foreign[0]+"::selectFromDB(\""+foreign[1]+"=\"."+dbi+"->escapeColumn(\"";
+               code+=foreign[0]+"\",\""+foreign[1]+"\",$this->"+local+"));\n}\n\n";
+       }
+       
+       //create enum constants
+       QList<WocEnum>ens=tbl.getEnums();
+       for(int i=0;i<ens.size();i++){
+               code+="const "+ens[i].name+"="+QString::number(ens[i].val)+";\n";
+       }
+       
+       //hasproperty function
+       code+="public function hasProperty($p){switch($p){\n";
+       for(int i=0;i<cols.size();i++)
+               code+="\tcase \""+cols[i]+"\":\n";
+       QStringList aps=tbl.auditColumns();
+       for(int i=0;i<aps.size();i++)
+               code+="\tcase \""+aps[i]+"\":\n";
+       code+="\t\treturn true;\n\tdefault:return false;}}\n";
+       
+       //create audit stuff
+       if(tbl.isAuditable()){
+               code+="public function isAuditable(){return true;}\n";
+               code+="protected function createAudit(){$ad=WT"+tbl.name()+"_audit::newRow($this->data);\n";
+               code+="\treturn $ad->insert();\n}\n";
+       }
+       
+       //create newKey function
+       code+="public function newKey(){\n\tparent::newKey();\n";
+       for(int i=0;i<cols.size();i++){
+               QString c=tbl.columnCall(cols[i],"php");
+               if(c=="")continue;
+               code+="\t$this->cdata[\""+cols[i]+"\"]="+c+";\n";
+       }
+       code+="}\n";
+       
+       //write table class
+       code+="};\n";
+       tf.write(code.toAscii());
+       tf.write(PHPEND);
+       tf.close();
+       
+       //extend schema file
+       //column definitions
+       code="\t$this->scheme[\""+tbl.name()+"\"]=array(";
+       for(int i=0;i<cols.size();i++){
+               if(i)code+=",";
+               code+="\n\t\t\""+cols[i]+"\"=>array(\"";
+               code+=tbl.columnType(cols[i])+"\"";
+               if(!tbl.columnIsNull(cols[i]))code+=",\"notnull\"";
+               if(tbl.columnIsForeign(cols[i]))code+=",\"foreignkey:"+tbl.columnForeign(cols[i])+"\"";
+               if(pcols.size()<2 && tbl.columnIsPrimary(cols[i]))code+=",\"primarykey\"";
+               if(tbl.columnHasDefault(cols[i]))code+=",\"default:"+tbl.columnDefault(cols[i])+"\"";
+               if(tbl.columnIsIndexed(cols[i]))code+=",\"index\"";
+               if(tbl.columnIsUnique(cols[i]))code+=",\"unique\"";
+               code+=")";
+       }
+       if(pcols.size()>=2){
+               code+=",\n\t\t\":primarykey\"=>array(";
+               for(int i=0;i<pcols.size();i++){
+                       if(i)code+=",";
+                       code+="\""+pcols[i]+"\"";
+               }
+               code+=")";
+       }
+       code+="\n\t);\n";
+       if(tbl.inBackup())code+="\t$this->backup[]=\""+tbl.name()+"\";\n";
+       //write presets
+       QList<QMap<QString,QString> >presets=tbl.presets();
+       if(presets.size()>0){
+               code+="\t$this->preset[\""+tbl.name()+"\"]=array(";
+               for(int i=0;i<presets.size();i++){
+                       if(i)code+=",";
+                       code+="\n\t\tarray(";
+                       QStringList k=presets[i].keys();
+                       for(int j=0;j<k.size();j++){
+                               if(j)code+=",";
+                               code+="\""+k[j]+"\"=>"+presets[i][k[j]];
+                       }
+                       code+=")";
+               }
+               code+="\n\t);\n";
+       }
+                       
+       //write
+       m_schema.write(code.toAscii());
+       
+       //create autoloading
+       addLoad("WT"+tbl.name(),"wt_"+tbl.name());
+}
+
+void WocPHPServerOut::addLoad(QString cn,QString fn)
+{
+       QString ld="$AUTOCLASS[\""+cn+"\"]=\""+m_subdir+"/"+fn+m_fileext+"\";\n";
+       m_loader.write(ld.toAscii());
+}
+
+void WocPHPServerOut::newClass(const WocClass&cls)
+{
+       //cover basics
+       QString cn=className(cls);
+       QString cna=abstractClassName(cls);
+       QString fn="wo_"+cls.name();
+       addLoad(cna,fn);
+       fn=m_subdir+"/"+fn+m_fileext;
+       QFile tf(m_basedir+"/"+fn);
+       if(!tf.open(QIODevice::ReadWrite|QIODevice::Truncate)){
+               qDebug("Error: cannot create PHP object file %s.",fn.toAscii().data());
+               emit errorFound();
+               return;
+       }
+       tf.write(PHPSTART);
+       
+       ////
+       //generate code
+       QString code="/* TRANSLATOR "+cna+" */\nclass "+cna+" extends "+cls.serverBaseClass()+"{\n\n";
+       tf.write(code.toAscii());
+       
+       //property declaration and constructor
+       tf.write(classConstruct(cls).toAscii());
+       
+       //enums
+       tf.write(classEnums(cls).toAscii());
+       
+       //properties
+       tf.write(classProperties(cls).toAscii());
+       
+       //mappings
+       tf.write(classMappings(cls).toAscii());
+       
+       //serializers
+       tf.write(classSerializers(cls).toAscii());
+       
+       //de-serializer
+       tf.write(classDeserializers(cls).toAscii());
+       
+       //end of class
+       code="\n//end of class\n};\n";
+       tf.write(code.toAscii());
+       
+       tf.write(PHPEND);
+       tf.close();
+}
+
+QString WocPHPServerOut::classConstruct(const WocClass&cls)
+{
+       QString code;
+       QStringList k=cls.propertyNames();
+       for(int i=0;i<k.size();i++){
+               code+="protected $prop_"+k[i];
+               if(!cls.propertyIsList(k[i]))code+="=null";
+               code+=";\n";
+       }
+       code+="public function __construct()\n{\n";
+       for(int i=0;i<k.size();i++)
+               if(cls.propertyIsList(k[i]))
+                       code+="\t$this->prop_"+k[i]+"=array();";
+       code+="}\n";
+       return code;
+}
+
+QString WocPHPServerOut::classEnums(const WocClass&cls)
+{
+       QString code;
+       QStringList k=cls.enumTypes();
+       for(int i=0;i<k.size();i++){
+               code+="//enum "+k[i]+"\n";
+               QList<WocEnum>ev=cls.enumValues(k[i]);
+               for(int j=0;j<ev.size();j++)
+                       code+="const "+ev[j].name+"="+QString::number(ev[j].val)+";\n";
+       }
+       return code;
+}
+
+QString WocPHPServerOut::classProperties(const WocClass&cls)
+{
+       QString code;
+       QStringList k=cls.propertyNames();
+       for(int i=0;i<k.size();i++){
+               code+="\n";
+               //generate validator
+               code+=classPropertyValidator(cls,k[i]);
+               //generic getter
+               code+="public function get"+k[i]+"(){return $this->prop_"+k[i]+";}\n";
+               //is it a list?
+               if(cls.propertyIsList(k[i])){
+                       //lists...
+                       //getters
+                       code+=classPropertyListGetters(cls,k[i]);
+                       //setters
+                       code+=classPropertyListSetters(cls,k[i]);
+               }else{
+                       //non-lists...
+                       //getters
+                       code+=classPropertyScalarGetters(cls,k[i]);
+                       //setter
+                       code+=classPropertyScalarSetters(cls,k[i]);
+               }
+       }
+       
+       return code;
+}
+
+QString WocPHPServerOut::classPropertyValidator(const WocClass&cls,QString prop)
+{
+       QString code;
+       code+="static public function validate"+prop+"($value){\n";
+       if(cls.propertyIsEnum(prop)){
+               QList<WocEnum>ev=cls.enumValues(cls.propertyPlainType(prop));
+               code+="\tif(is_numeric($value)){\n\t\t$value=$value+0;\n";
+               for(int j=0;j<ev.size();j++){
+                       code+="\t\tif($value=="+QString::number(ev[j].val)+")return true;\n";
+               }
+               code+="\t\treturn false;\n\t}else{\n";
+               for(int j=0;j<ev.size();j++){
+                       code+="\t\tif($value==\""+ev[j].name+"\")return true;\n";
+               }
+               code+="\t\treturn false;\n\t}\n";
+       }else
+       if(cls.propertyIsInt(prop))
+               code+="\treturn is_numeric($value);\n";
+       else
+       if(cls.propertyIsString(prop)||cls.propertyIsBlob(prop))
+               code+="\treturn true;\n";//TODO: special handling for astring
+       else
+       if(cls.propertyIsBool(prop))
+               code+="\treturn is_bool($value);\n";
+       else
+       if(cls.propertyIsObject(prop))
+               code+="\treturn is_a($value,\"WO"+cls.propertyPlainType(prop)+"\");\n";
+       else{
+               qDebug("Warning: unable to generate validator for class %s property %s: unknown type.",cls.name().toAscii().data(),prop.toAscii().data());
+               code+="\treturn false;\n";
+       }
+       code+="}\n";
+       
+       return code;
+}
+
+QString WocPHPServerOut::classPropertyListGetters(const WocClass&cls,QString prop)
+{
+       QString code;
+       if(cls.propertyIsString(prop))
+               code+="public function getstrlist_"+prop+"(){return $this->prop_"+prop+";}\n";
+       else
+       if(cls.propertyIsBlob(prop)){
+               code+="public function getstrlist_"+prop+"(){\n\t$ret=array();\n";
+               code+="\tforeach($this->prop_"+prop+" as $p)$ret[]=base64_encode($this->prop_"+prop+");";
+               code+="\treturn $ret;\n}\n";
+       }else
+       if(cls.propertyIsInt(prop)){
+               code+="public function getstrlist_"+prop+"(){\n";
+               code+="\t$ret=array();\n\tforeach($this->prop_"+prop+" as $p)$ret[]=\"\".$p;\n";
+               code+="\treturn $ret;\n}\n";
+       }else
+       if(cls.propertyIsBool(prop)){
+               code+="public function getstrlist_"+prop+"(){\n";
+               code+="\t$ret=array();\n\tforeach($this->prop_"+prop+" as $p)$ret[]=$p?\"yes\":\"no\";\n";
+               code+="\treturn $ret;\n}\n";
+       }else
+       if(cls.propertyIsEnum(prop)){
+               code+="public function getstrlist_"+prop+"(){\n";
+               code+="\t$ret=array();\n";
+               code+="\tforeach($this->prop_"+prop+" as $p)switch($p){\n";
+               QList<WocEnum> ev=cls.enumValues(cls.propertyPlainType(prop));
+               for(int j=0;j<ev.size();j++){
+                       code+="\t\tcase "+QString::number(ev[j].val)+":$ret[]=\""+ev[j].name+"\";break;\n";
+               }
+               code+="\t\tdefault:$ret[]=null;break;\n\t}\n\treturn $ret;\n}\n";
+       }
+       
+       return code;
+}
+
+QString WocPHPServerOut::classPropertyListSetters(const WocClass&cls,QString prop)
+{
+       QString code;
+       code+="public function clear_"+prop+"(){$this->prop_"+prop+"=array();}\n";
+       QString acode;//body of add_ function, see below
+       code+="public function set"+prop+"(array $values){\n";
+       if(cls.propertyIsEnum(prop)){
+               QList<WocEnum>ev=cls.enumValues(cls.propertyPlainType(prop));
+               code+="\t$prop=array();\n";
+               code+="\tforeach($values as $value){\n";
+               code+="\t\tif(is_numeric($value)){\n\t\t\t$value=$value+0;\n";
+               acode+="\tif(is_numeric($value)){\n\t\t$value=$value+0;\n";
+               for(int j=0;j<ev.size();j++){
+                       code+="\t\t\tif($value=="+QString::number(ev[j].val)+"){\n";
+                       code+="\t\t\t\t$prop[]="+QString::number(ev[j].val)+";\n";
+                       code+="\t\t\t}else\n";
+                       acode+="\t\tif($value=="+QString::number(ev[j].val)+"){\n";
+                       acode+="\t\t\t$this->prop_"+prop+"[]="+QString::number(ev[j].val)+";\n";
+                       acode+="\t\t\treturn true;\n\t\t}\n";
+               }
+               code+="\t\t\treturn false;\n\t\t}else{\n";
+               acode+="\t\treturn false;\n\t}else{\n";
+               for(int j=0;j<ev.size();j++){
+                       code+="\t\t\tif($value==\""+ev[j].name+"\"){\n";
+                       code+="\t\t\t\t$prop[]="+QString::number(ev[j].val)+";\n";
+                       code+="\n\t\t\t}else\n";
+                       acode+="\t\tif($value==\""+ev[j].name+"\"){\n";
+                       acode+="\t\t\t$this->prop_"+prop+"[]="+QString::number(ev[j].val)+";\n";
+                       acode+="\t\t\treturn true;\n\t\t}\n";
+               }
+               code+="\t\t\treturn false;\n\t\t}\n\t}\n";
+               code+="\t$this->prop_"+prop+"=$prop;\n";
+               code+="\treturn true;\n";
+               acode+="\t\treturn false;\n\t}\n";
+       }else
+       if(cls.propertyIsInt(prop)){
+               code+="\t$prop=array();\n\tforeach($values as $value)\n";
+               code+="\t\tif(is_numeric($value)){\n\t\t\t$prop[]=0+$value;\n\t\t}else return false;\n";
+               code+="\t$this->prop_"+prop+"=$prop;\n\treturn true;\n";
+               acode+="\tif(is_numeric($value)){\n";
+               acode+="\t\t$this->prop_"+prop+"=0+$value;\n\t\treturn true;\n\t}else return false;\n";
+       }else
+       if(cls.propertyIsBool(prop)){
+               code+="\t$prop=array();\n\tforeach($values as $value)\n";
+               code+="\t\tif(is_bool($value))$prop[]=$value!=false;else\n";
+               code+="\t\tif($value==\"yes\")$prop[]=true;else\n";
+               code+="\t\tif($value==\"no\")$prop[]=false;else return false;\n";
+               code+="\t$this->prop_"+prop+"=$prop;\n\treturn true;\n";
+               acode+="\tif(is_bool($value)){\n";
+               acode+="\t\t$this->prop_"+prop+"=$value!=false;\n\t\treturn true;\n\t}else return false;\n";
+       }else
+       if(cls.propertyIsString(prop)||cls.propertyIsBlob(prop)){
+               code+="\t$prop=array();\n\tforeach($values as $value)$prop[]=\"\".$value;\n";
+               code+="\t$this->prop_"+prop+"=$prop;\n\treturn true;\n";
+               //TODO: special handling for astring
+               acode+="\t$this->prop_"+prop+"[]=\"\".$value;\n\treturn true;\n";
+       }else
+       if(cls.propertyIsObject(prop)){
+               code+="\t$prop=array();\n\tforeach($values as $value)\n";
+               code+="\t\tif(is_a($value,\"WO"+cls.propertyPlainType(prop)+"\"))\n";
+               code+="\t\t\t$prop[]=$value;\n";
+               code+="\t\telse return false;\n";
+               code+="\t$this->prop_"+prop+"=$prop;\n";
+               code+="\treturn true;\n";
+               acode+="\tif(is_a($value,\"WO"+cls.propertyPlainType(prop)+"\")){\n";
+               acode+="\t\t$this->prop_"+prop+"[]=$value;\n";
+               acode+="\t\treturn true;\n";
+               acode+="\t}else return false;\n";
+       }else{
+               qDebug("Warning: unable to generate setter for class %s property %s: unknown type.",cls.name().toAscii().data(),prop.toAscii().data());
+               code+="\treturn false;\n";
+       }
+       code+="}\n";
+       code+="public function add_"+prop+"($value){\n"+acode+"}\n";
+       
+       return code;
+}
+
+QString WocPHPServerOut::classPropertyScalarGetters(const WocClass&cls,QString prop)
+{
+       QString code;
+       if(cls.propertyIsString(prop))
+               code+="public function getstr_"+prop+"(){return $this->prop_"+prop+";}\n";
+       else
+       if(cls.propertyIsBlob(prop))
+               code+="public function getstr_"+prop+"(){return base64_encode($this->prop_"+prop+");}\n";
+       else
+       if(cls.propertyIsInt(prop))
+               code+="public function getstr_"+prop+"(){return \"\".$this->prop_"+prop+";}\n";
+       else
+       if(cls.propertyIsBool(prop))
+               code+="public function getstr_"+prop+"(){return $this->prop_"+prop+"?\"yes\":\"no\";}\n";
+       else
+       if(cls.propertyIsEnum(prop)){
+               code+="public function getstr_"+prop+"(){\n\tswitch($this->prop_"+prop+"){\n";
+               QList<WocEnum> ev=cls.enumValues(cls.propertyPlainType(prop));
+               for(int j=0;j<ev.size();j++){
+                       code+="\t\tcase "+QString::number(ev[j].val)+":return translate(\""+abstractClassName(cls)+"\",\""+ev[j].name+"\");\n";
+               }
+               code+="\t\tdefault:return null;\n\t}\n}\n";
+       }
+       
+       return code;
+}
+
+QString WocPHPServerOut::classPropertyScalarSetters(const WocClass&cls,QString prop)
+{
+       QString code;
+       code+="public function set"+prop+"($value){\n";
+       if(cls.propertyIsEnum(prop)){
+               QList<WocEnum>ev=cls.enumValues(cls.propertyPlainType(prop));
+               code+="\tif(is_numeric($value)){\n\t\t$value=$value+0;\n";
+               for(int j=0;j<ev.size();j++){
+                       code+="\t\tif($value=="+QString::number(ev[j].val)+"){\n";
+                       code+="\t\t\t$this->prop_"+prop+"="+QString::number(ev[j].val)+";\n";
+                       code+="\t\t\treturn true;\n\t\t}\n";
+               }
+               code+="\t\treturn false;\n\t}else{\n";
+               for(int j=0;j<ev.size();j++){
+                       code+="\t\tif($value==\""+ev[j].name+"\"){\n";
+                       code+="\t\t\t$this->prop_"+prop+"="+QString::number(ev[j].val)+";\n";
+                       code+="\t\t\treturn true;\n\t\t}\n";
+               }
+               code+="\t\treturn false;\n\t}\n";
+       }else
+       if(cls.propertyIsInt(prop))
+               code+="\tif(is_numeric($value)){\n\t\t$this->prop_"+prop+"=0+$value;\n\t\treturn true;\n\t}else return false;\n";
+       else
+       if(cls.propertyIsBool(prop)){
+               code+="\tif(is_bool($value))$this->prop_"+prop+"=$value!=false;else\n";
+               code+="\tif($value==\"yes\")$this->prop_"+prop+"=true;else\n";
+               code+="\tif($value==\"no\")$this->prop_"+prop+"=false;\n\telse return false;\n";
+               code+="\treturn true;\n";
+       }else
+       if(cls.propertyIsString(prop)||cls.propertyIsBlob(prop))
+               code+="\t$this->prop_"+prop+"=\"\".$value;\n\treturn true;\n";
+               //TODO: special handling for astring
+               //TODO: do something about Blob
+       else
+       if(cls.propertyIsObject(prop)){
+               code+="\tif(is_a($value,\"WO"+cls.propertyPlainType(prop)+"\")){\n";
+               code+="\t\t$this->prop_"+prop+"=$value;\n";
+               code+="\t\treturn true;\n\t}\n";
+       }else{
+               qDebug("Warning: unable to generate setter for class %s property %s: unknown type.",cls.name().toAscii().data(),prop.toAscii().data());
+               code+="\treturn false;\n";
+       }
+       code+="}\n";
+       
+       return code;
+}
+
+QString WocPHPServerOut::classSerializers(const WocClass&cls)
+{
+       QString code;
+       //toString function (wraps toXml)
+       code+="\npublic function toString(){\n\t$xml=new DomDocument;\n";
+       code+="\t$xml->appendChild($this->toXml($xml));\n\treturn $xml->saveXml();\n}\n";
+       //toXml function:
+       code+="public function toXml($xml,$elementname=\""+cls.name()+"\"){\n";
+       code+="\t$root=$xml->createElement($elementname);\n";
+       //add properties
+       QStringList p=cls.propertyNames();
+       for(int j=0;j<p.size();j++)
+               code+=propertyToXml(cls,p[j]);
+       //return result
+       code+="\treturn $root;\n}\n";
+       return code;
+}
+
+QString WocPHPServerOut::classDeserializers(const WocClass&cls)
+{
+       QString code;
+       QStringList k;
+       code+="\nstatic public function fromString($txt){\n\t$xml=new DomDocument;\n";
+       code+="\tif(!$xml->loadXml(trim($txt)))";
+       code+="\n\t\tthrow WobXmlException(translate(\""+abstractClassName(cls)+"\",\"Unable to deserialize object of type "+className(cls)+": invalid XML.\"));";
+       code+="\n\treturn self::fromXml($xml,$xml->documentElement);\n}\n";
+       code+="static public function fromXml($xml,$elem){\n\t$data=new "+className(cls)+"();\n";
+       k=cls.propertyNames();
+       for(int i=0;i<k.size();i++){
+               //scan properties
+               if(cls.propertyIsList(k[i])){
+                       code+="\t$data->clear_"+k[i]+"();\n";
+                       code+="\tforeach(WObject::elementsByTagName($elem,\""+k[i]+"\") as $el){\n";
+                       if(cls.propertyIsObject(k[i])){
+                               code+="\t\t$data->add_"+k[i]+"(WO"+cls.propertyPlainType(k[i])+"::fromXml($xml,$el));\n";
+                       }else if(cls.propertyIsBlob(k[i])){
+                               code+="\t\t$data->add_"+k[i]+"(base64_decode($el->textContent));\n";
+                       }else{
+                               code+="\t\t$data->add_"+k[i]+"($el->textContent);\n";
+                       }
+                       code+="\t}\n";
+               }else{
+                       if(cls.propertyIsObject(k[i])){
+                               code+="\tforeach(WObject::elementsByTagName($elem,\""+k[i]+"\") as $el){\n";
+                               code+="\t\t$data->set"+k[i]+"(WO"+cls.propertyPlainType(k[i])+"::fromXml($xml,$el));\n";
+                               code+="\t}\n";
+                       }else
+                       if(cls.propertyIsAttribute(k[i])){
+                               code+="\tif($elem->hasAttribute(\""+k[i]+"\"))\n";
+                               code+="\t\t$data->set"+k[i]+"($elem->getAttribute(\""+k[i]+"\"));\n";
+                       }else{
+                               code+="\tforeach(WObject::elementsByTagName($elem,\""+k[i]+"\") as $el){\n";
+                               if(cls.propertyIsBlob(k[i]))
+                                       code+="\t\t$data->set"+k[i]+"(base64_decode($el->textContent));\n";
+                               else
+                                       code+="\t\t$data->set"+k[i]+"($el->textContent);\n";
+                               code+="\t}\n";
+                       }
+               }
+       }
+       code+="\treturn $data;\n}\n";
+       return code;
+}
+
+QString WocPHPServerOut::classMappings(const WocClass&cls)
+{
+       //implement mappings
+       QString code;
+       QStringList k=cls.mappingTables();
+       for(int i=0;i<k.size();i++){
+               WocTable tab=WocProcessor::instance()->table(k[i]);
+               //single object mapping
+               code+="\nstatic public function fromTable"+k[i]+"($table){\n";
+               code+="\tif($table === false)return false;\n";
+               code+="\t$data=new WO"+cls.name()+"();\n";
+               QMap<QString,QString>map=cls.mapping(k[i]);
+               QStringList mapk=cls.mappingProperties(k[i]);
+               for(int j=0;j<mapk.size();j++){
+                       QString meth=cls.mapMethod(k[i],mapk[j],"php");
+                       if(meth!="")code+="\t$data->prop_"+mapk[j]+"="+meth+";\n";
+                       else code+="\t$data->prop_"+mapk[j]+"=$table->"+map[mapk[j]]+";\n";
+               }
+               code+="\treturn $data;\n}\n";
+               //wrapper for multi-object mapping
+               code+="static public function fromTableArray"+k[i]+"(array $table){\n\t$ret=array();\n";
+               code+="\tfor($i=0;$i<count($table);$i++)$ret[]=self::fromTable"+k[i]+"($table[$i]);\n";
+               code+="\treturn $ret;\n}\n";
+               //reverse mapping
+               code+="public function toTable"+k[i]+"(&$table){\n";
+               for(int j=0;j<mapk.size();j++){
+                       //do not reverse translate method conversions
+                       QString meth=cls.mapMethod(k[i],mapk[j],"php").trimmed();
+                       if(meth!="")continue;
+                       //check that column exists
+                       if(tab.hasColumn(map[mapk[j]])){
+                               code+="\tif($this->prop_"+mapk[j]+"!==null && $table->"+map[mapk[j]]+"!=$this->prop_"+mapk[j]+")\n";
+                               code+="\t\t$table->"+map[mapk[j]]+"=$this->prop_"+mapk[j]+";\n";
+                       }
+               }
+               code+="\treturn $table;\n}\n";
+       }
+       
+       return code;
+}
+
+QString WocPHPServerOut::propertyToXml(const WocClass&cls,QString sl)
+{
+       QString prop=sl.trimmed();
+       //is it a list?
+       if(cls.propertyIsList(prop)){
+               //is it a class?
+               if(cls.propertyIsObject(prop)){
+                       QString code="\tforeach($this->get"+prop+"() as $o)\n\t\t";
+                       code+="$root->appendChild($o->toXml($xml,\""+prop+"\"));\n";
+                       return code;
+               }else{
+                       //there is no way to create lists of attributes, hence we always create elements
+                       QString code="\tforeach($this->getstrlist_"+prop+"() as $e)\n\t\t";
+                       code+="$root->appendChild($xml->createElement(\""+prop+"\",xq($e)));\n";
+                       return code;
+               }
+       }
+       //non lists:
+       QString code="\t$p=$this->get"+prop+"();\n\tif($p!==null)";
+       //is it an attribute?
+       if(cls.propertyIsAttribute(prop))
+               return code+"\t$root->setAttribute(\""+prop+"\",$this->getstr_"+prop+"());\n";
+       //is it an element?
+       if(cls.propertyIsElement(prop))
+               return code+"\t$root->appendChild($xml->createElement(\""+prop+"\",xq($this->getstr_"+prop+"())));\n";
+       //is it a class?
+       if(cls.propertyIsObject(prop))
+               return code+"$root->appendChild($p->toXml($xml,\""+prop+"\"));\n";
+       //anything else?
+       qDebug("Warning: end of WocPHPServerOut::propertyToXml - this code should not be reachable.");
+       return "//internal generator error!\n";
+}
+
+void WocPHPServerOut::newTransaction(const WocTransaction&trn)
+{
+       //create file
+       QString cn=trnClassName(trn);
+       QString fn="wtr_"+trn.name();
+       addLoad(cn,fn);
+       fn=m_subdir+"/"+fn+m_fileext;
+       QFile tf(m_basedir+"/"+fn);
+       if(!tf.open(QIODevice::ReadWrite|QIODevice::Truncate)){
+               qDebug("Error: cannot create PHP object file %s.",fn.toAscii().data());
+               emit errorFound();
+               return;
+       }
+       tf.write(PHPSTART);
+       
+       ////
+       //generate code
+       QString code="/* TRANSLATOR "+cn+" */\nclass "+cn+" extends WobTransaction{\n";
+       //constructor
+       code+=trnConstruct(trn);
+       tf.write(code.toAscii());
+       
+       //request handler:
+       code="public function handleRequest(){\n";
+       
+       //security handling
+       switch(trn.authMode()){
+               case WocTransaction::Checked:
+                       code+="\t/*security check: authenticated and authorized*/\n";
+                       code+="\t"+m_authinit+";\n";
+                       code+="\tif(!"+m_isauth+"||!"+QString(m_hasrole).replace("%","\""+trn.name()+"\"")+")$this->notAuthenticated();\n";
+                       break;
+               case WocTransaction::Auth:
+                       code+="\t/*security check: authenticated*/\n";
+                       code+="\t"+m_authinit+";\n";
+                       code+="\tif(!"+m_isauth+")$this->notAuthenticated();\n";
+                       break;
+               default:
+                       code+="\t/*no security check, open function*/\n";
+                       break;//none
+       }
+       
+       //parse low level XML
+       code+="\t/*low level XML parsing*/\n";
+       code+="\tglobal $HTTP_RAW_POST_DATA;\n\tif(isset($HTTP_RAW_POST_DATA))$txt=$HTTP_RAW_POST_DATA;else $txt=\"\";\n";
+       code+="\t$xml=new DOMDocument;\n\tif(!$xml->loadXML($txt))$this->xmlParserError();\n";
+       code+="\t$root=$xml->documentElement;\n";
+       
+       //parse inputs
+       code+=trnInput(trn);
+       
+       //call
+       if(trn.hasCall("php")){
+               code+="\t/*call actual functionality:*/\n";
+               code+="\ttry{"+trn.callFunction("php")+"}catch(Exception $e){$this->handleException($e);}\n";
+       }else{
+               code+="\t/*normally here would be the PHP call, but it is missing from the config*/\n";
+               code+="\t$this->abortNotImplemented();\nreturn;\n";
+               qDebug("Warning: transaction %s does not have a PHP call!",trn.name().toAscii().data());
+       }
+       
+       //encode outputs/handle errors
+       code+=trnOutput(trn);
+       
+       code+="}\n";
+       tf.write(code.toAscii());
+       
+       //getters/setters
+       tf.write(trnGetSet(trn).toAscii());
+       
+       //direct execution
+       tf.write(trnExecute(trn).toAscii());
+       
+       //privileges
+       tf.write(trnPrivileges(trn).toAscii());
+       
+       //end
+       code="\n//end of class\n}\n";
+       tf.write(code.toAscii());
+       tf.write(PHPEND);
+       tf.close();
+       
+       ////
+       //generate loader code
+       code="\tcase \""+trn.name()+"\":$trn=new "+cn+";$trn->handleRequest();break;\n";
+       m_transact.write(code.toAscii());
+}
+
+QString WocPHPServerOut::trnConstruct(const WocTransaction&trn)
+{
+       QString code="public function __construct(){\n\t$this->ainput=array(";
+       QStringList sl=trn.inputNames();
+       for(int i=0;i<sl.size();i++){
+               if(i)code+=",";
+               code+="\""+sl[i]+"\"=>";
+               if(trn.isListType(trn.inputType(sl[i])))code+="array()";
+               else code+="false";
+       }
+       code+=");\n";
+       code+="\t$this->tinput=array(";
+       for(int i=0;i<sl.size();i++){
+               if(i)code+=",";
+               code+="\""+sl[i]+"\"=>\"";
+               code+=trn.inputType(sl[i]);
+               code+="\"";
+       }
+       code+=");\n";
+       
+       
+       code+="\t$this->aoutput=array(";
+       sl=trn.outputNames();
+       for(int i=0;i<sl.size();i++){
+               if(i)code+=",";
+               code+="\""+sl[i]+"\"=>";
+               if(trn.isListType(trn.outputType(sl[i])))code+="array()";
+               else code+="false";
+       }
+       code+=");\n";
+       code+="\t$this->toutput=array(";
+       for(int i=0;i<sl.size();i++){
+               if(i)code+=",";
+               code+="\""+sl[i]+"\"=>\"";
+               code+=trn.outputType(sl[i]);
+               code+="\"";
+       }
+       code+=");\n}\n";
+       return code;
+}
+
+QString WocPHPServerOut::trnInput(const WocTransaction&trn)
+{
+       QString code="\t/*start of input parsing*/\n";
+       code+="\ttry{\n";
+       QStringList sl=trn.inputNames();
+       for(int i=0;i<sl.size();i++){
+               QString t=trn.inputType(sl[i]);
+               if(trn.isAttributeType(t)){
+                       code+="\t\t$this->ainput[\""+sl[i]+"\"]=$root->getAttribute(\""+sl[i]+"\")";
+                       if(trn.isIntType(t))code+="+0";
+                       if(trn.isBoolType(t))code+="==\"yes\"";
+                       code+=";\n";
+               }else{
+                       if(trn.isListType(t)){
+                               QString pt=trn.plainType(t);
+                               code+="\t\tforeach(WObject::elementsByTagName($root,\""+sl[i]+"\") as $el){\n";
+                               if(trn.isObjectType(t)){
+                                       code+="\t\t\t$this->ainput[\""+sl[i]+"\"][]=WO"+pt+"::fromXml($xml,$el);\n";
+                               }else if(trn.isBlobType(t)){
+                                       code+="\t\t\t$this->ainput[\""+sl[i]+"\"][]=base64_decode($el->textContent);\n";
+                               }else{
+                                       code+="\t\t\t$this->ainput[\""+sl[i]+"\"][]=$el->textContent";
+                                       if(trn.isIntType(t))code+="+0";
+                                       if(trn.isBoolType(t))code+="==\"yes\"";
+                                       code+=";\n";
+                               }
+                               code+="\t\t}\n";
+                       }else{
+                               code+="\t\tforeach(WObject::elementsByTagName($root,\""+sl[i]+"\") as $el){\n";
+                               if(trn.isObjectType(t)){
+                                       code+="\t\t\t$this->ainput[\""+sl[i]+"\"]=WO"+t+"::fromXml($xml,$el);\n";
+                               }else if(trn.isBlobType(t)){
+                                       code+="\t\t\t$this->ainput[\""+sl[i]+"\"]=base64_decode($el->textContent);\n";
+                               }else{
+                                       code+="\t\t\t$this->ainput[\""+sl[i]+"\"]=$el->textContent";
+                                       if(trn.isIntType(t))code+="+0";
+                                       code+=";\n";
+                               }
+                               code+="\t\t}\n";
+                       }
+               }
+       }
+       code+="\t}catch(Exception $e){$this->handleException($e);}\n";
+       code+="\t/*end of input parsing*/\n";
+       return code;
+}
+
+QString WocPHPServerOut::trnOutput(const WocTransaction&trn)
+{
+       QStringList sl=trn.outputNames();
+       QString code="\t/*start of output encoding*/\n";
+       code+="\ttry{\n\t\t$xml=new DOMDocument;\n";
+       code+="\t\t$root=$xml->createElement(\"WobResponse\");\n";
+       for(int i=0;i<sl.size();i++){
+               QString t=trn.outputType(sl[i]);
+               if(trn.isAttributeType(t)){
+                       code+="\t\t$root->setAttribute(\""+sl[i]+"\",$this->aoutput[\""+sl[i]+"\"]);\n";
+               }else{
+                       if(trn.isListType(t)){
+                               if(trn.isObjectType(t)){
+                                       code+="\t\tforeach($this->aoutput[\""+sl[i]+"\"] as $o)\n";
+                                       code+="\t\t\tif(is_a($o,\"WO"+trn.plainType(t)+"\"))\n";
+                                       code+="\t\t\t\t$root->appendChild($o->toXml"+trn.typeSerializer(t)+"($xml,\""+sl[i]+"\"));\n";
+                               }else if(trn.isBlobType(t)){
+                                       code+="\t\tforeach($this->aoutput[\""+sl[i]+"\"] as $v)\n";
+                                       code+="\t\t\t$root->appendChild($xml->createElement(\""+sl[i]+"\",base64_encode($v)));\n";
+                               }else{
+                                       code+="\t\tforeach($this->aoutput[\""+sl[i]+"\"] as $v)\n";
+                                       code+="\t\t\t$root->appendChild($xml->createElement(\""+sl[i]+"\",xq($v)));\n";
+                               }
+                       }else{
+                               if(trn.isObjectType(t)){
+                                       code+="\t\t$o=$this->aoutput[\""+sl[i]+"\"];\n";
+                                       code+="\t\tif(is_a($o,\"WO"+trn.plainType(t)+"\"))\n";
+                                       code+="\t\t\t$root->appendChild($o->toXml"+trn.typeSerializer(t)+"($xml,\""+sl[i]+"\"));\n";
+                               }else if(trn.isBlobType(t)){
+                                       code+="\t\t$root->appendChild($xml->createElement(\""+sl[i]+"\",base64_encode($this->aoutput[\""+sl[i]+"\"])));\n";
+                               }else{
+                                       code+="\t\t$root->appendChild($xml->createElement(\""+sl[i]+"\",xq($this->aoutput[\""+sl[i]+"\"])));\n";
+                               }
+                       }
+               }
+       }
+       code+="\t\t$xml->appendChild($root);\n";
+       code+="\t\theader(\"X-WobResponse-Status: Ok\");\n";
+       code+="\t\tprint($xml->saveXml());\n";
+       code+="\t}catch(Exception $e){$this->handleException($e);}\n";
+       code+="\t/*end of output*/\n";
+       return code;
+}
+
+QString WocPHPServerOut::trnGetSet(const WocTransaction&trn)
+{
+       QString code;
+       //getters
+       QStringList sl=trn.inputNames();
+       for(int i=0;i<sl.size();i++){
+               code+="public function get"+sl[i]+"(){return $this->ainput[\""+sl[i]+"\"];}\n";
+       }
+       //setters
+       sl=trn.outputNames();
+       for(int i=0;i<sl.size();i++){
+               QString add;
+               QString t=trn.outputType(sl[i]);
+               code+="public function set"+sl[i]+"($v){\n";
+               if(trn.isListType(t)){
+                       add="public function add"+sl[i]+"($vv){\n";
+                       code+="\t$this->aoutput[\""+sl[i]+"\"]=array();\n";
+                       code+="\tforeach($v as $vv){\n";
+                       if(trn.isIntType(t)){
+                               code+="\t\tif(is_numeric($vv))$this->aoutput[\""+sl[i]+"\"][]=$vv+0;\n";
+                               add+="\tif(is_numeric($vv))$this->aoutput[\""+sl[i]+"\"][]=$vv+0;\n";
+                       }else
+                       if(trn.isBoolType(t)){
+                               code+="\t\tif(is_bool($vv))$this->aoutput[\""+sl[i]+"\"][]=$vv!=false;\n";
+                               add+="\tif(is_bool($vv))$this->aoutput[\""+sl[i]+"\"][]=$vv!=false;\n";
+                       }else
+                       if(trn.isObjectType(t)){
+                               code+="\t\tif(is_a($vv,\"WO"+trn.plainType(t)+"\"))$this->aoutput[\""+sl[i]+"\"][]=$vv;\n";
+                               add+="\tif(is_a($vv,\"WO"+trn.plainType(t)+"\"))$this->aoutput[\""+sl[i]+"\"][]=$vv;\n";
+                       }else{
+                               code+="\t\t$this->aoutput[\""+sl[i]+"\"][]=\"\".$vv;\n";
+                               add+="\t$this->aoutput[\""+sl[i]+"\"][]=\"\".$vv;\n";
+                       }
+                       code+="\t}\n";
+                       add+="}\n";
+               }else{
+                       if(trn.isIntType(t)){
+                               code+="\tif(is_numeric($v))$this->aoutput[\""+sl[i]+"\"]=$v+0;\n";
+                       }else
+                       if(trn.isBoolType(t)){
+                               code+="\tif(is_bool($v))$this->aoutput[\""+sl[i]+"\"]=$v!=false;\n";
+                       }else
+                       if(trn.isObjectType(t)){
+                               code+="\tif(is_a($v,\"WO"+trn.plainType(t)+"\"))$this->aoutput[\""+sl[i]+"\"]=$v;\n";
+                       }else{
+                               code+="\t$this->aoutput[\""+sl[i]+"\"]=\"\".$v;\n";
+                       }
+               }
+               code+="}\n"+add;
+       }
+       
+       
+       return code;
+}
+
+QString WocPHPServerOut::trnExecute(const WocTransaction&trn)
+{
+       QStringList in=trn.inputNames();
+       QString code="static public function execute(";
+       for(int i=0;i<in.size();i++){
+               if(i)code+=",";
+               code+="$"+in[i];
+       }
+       code+=")\n{\n\t$inst=new "+trnClassName(trn)+";\n";
+       for(int i=0;i<in.size();i++)
+               code+="\t$this->ainput[\""+in[i]+"\"]=$"+in[i]+";\n";
+       code+="\tself::$running=\""+trn.name()+"\";\n";
+       code+="\t$inst->do_execute();\n";
+       code+="\tself::$running=\"\";\n";
+       code+="\treturn $inst;\n}\n";
+       code+="private function do_execute(){"+trn.callFunction("php")+"}\n";
+       return code;
+}
+
+QString WocPHPServerOut::trnPrivileges(const WocTransaction&trn)
+{
+       //privilege inventory
+       QString code;
+       code+="static public function privileges(){\n\treturn array(";
+       QString cn=trnClassName(trn);
+       QStringList priv=trn.privileges();
+       for(int i=0;i<priv.size();i++){
+               if(i)code+=",\n\t\t";else code+="\n\t\t";
+               code+="\""+priv[i]+"\"";
+       }
+       code+="\n\t);\n}\n";
+       //constants for use by custom code
+       for(int i=0;i<priv.size();i++)
+               code+="const Priv_"+priv[i]+"=\""+priv[i]+"\";\n";
+       //check method
+       code+="public function havePrivilege($priv){\n";
+       code+="\tif(!in_array($priv,self::privileges()))return false;\n";
+       code+="\treturn "+QString(m_hasrole).replace("%","(\""+trn.name()+":\".$priv)")+";\n}\n";
+       
+       return code;
+}
diff --git a/woc/phpout.h b/woc/phpout.h
new file mode 100644 (file)
index 0000000..186039e
--- /dev/null
@@ -0,0 +1,97 @@
+//
+// C++ Interface: phpout
+//
+// Description: 
+//
+//
+// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2009
+//
+// Copyright: See README/COPYING files that come with this distribution
+//
+//
+
+#ifndef WOC_PHPOUT_H
+#define WOC_PHPOUT_H
+
+#include <QFile>
+
+#include "processor.h"
+
+class QDomElement;
+
+/**generates output for a PHP server side*/
+class WocPHPServerOut:public WocOutput
+{
+       public:
+               /**initializes the output object*/
+               WocPHPServerOut(const QDomElement&);
+       protected:
+               /**writes any last words after parsing finished*/
+               virtual void finalize();
+               /**creates a class*/
+               virtual void newClass(const WocClass&);
+               /**creates a table*/
+               virtual void newTable(const WocTable&);
+               /**creates a transaction*/
+               virtual void newTransaction(const WocTransaction&);
+       private:
+               QString m_basedir,m_subdir,m_fileext;
+               QString m_isauth,m_hasrole,m_username,m_authinit;
+               QFile m_loader,m_schema,m_transact;
+               
+               /**helper: adds a loader line for a class to autoload.php*/
+               void addLoad(QString classname,QString filename);
+               
+               /**helper: generates PHP code to transform a class property to XML*/
+               QString propertyToXml(const WocClass&,QString);
+               /**helper: generate class constructor*/
+               QString classConstruct(const WocClass&);
+               /**helper: generate class internal enums*/
+               QString classEnums(const WocClass&);
+               /**helper: generate class internal props*/
+               QString classProperties(const WocClass&);
+               /**helper: generate class internal serializers*/
+               QString classSerializers(const WocClass&);
+               /**helper: generate class internal deserializers*/
+               QString classDeserializers(const WocClass&);
+               /**helper: generate class internal mappers*/
+               QString classMappings(const WocClass&);
+               /**helper: generate property validator*/
+               QString classPropertyValidator(const WocClass&,QString);
+               /**helper: generate getters for list properties*/
+               QString classPropertyListGetters(const WocClass&,QString);
+               /**helper: generate setters for list properties*/
+               QString classPropertyListSetters(const WocClass&,QString);
+               /**helper: generate getters for scalar properties*/
+               QString classPropertyScalarGetters(const WocClass&,QString);
+               /**helper: generate setters for sclar properties*/
+               QString classPropertyScalarSetters(const WocClass&,QString);
+               
+               /**helper: create info functions (mainly version info) at the start*/
+               void transInfo();
+               /**helper: create info functions (mainly version info) at the end*/
+               void transInfo2();
+               
+               /**helper: create transaction constructor*/
+               QString trnConstruct(const WocTransaction&);
+               /**helper: create transaction input parser*/
+               QString trnInput(const WocTransaction&);
+               /**helper: create transaction output serializer*/
+               QString trnOutput(const WocTransaction&);
+               /**helper: create getters and setters*/
+               QString trnGetSet(const WocTransaction&);
+               /**helper: create direct execution code for web interface*/
+               QString trnExecute(const WocTransaction&);
+               /**helper: create privilege check code for web interface*/
+               QString trnPrivileges(const WocTransaction&);
+               
+               /**helper: return the PHP-class-name of a WocClass*/
+               QString className(const WocClass&c){return "WO"+c.name();}
+               /**helper: return the PHP-class-name of a WocClass plus Abstract if it is abstract*/
+               QString abstractClassName(const WocClass&c){return "WO"+c.name()+QString(c.isAbstract("php")?"Abstract":"");}
+               
+               /**helper: returns the PHP-class-name for a WocTransaction*/
+               QString trnClassName(const WocTransaction&t){return "WTr"+t.name();}
+};
+
+#endif
diff --git a/woc/processor.cpp b/woc/processor.cpp
new file mode 100644 (file)
index 0000000..8da690c
--- /dev/null
@@ -0,0 +1,1223 @@
+//
+// C++ Implementation: processor
+//
+// Description: 
+//
+//
+// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2009
+//
+// Copyright: See README/COPYING files that come with this distribution
+//
+//
+
+#include "processor.h"
+
+#include "phpout.h"
+#include "qtout.h"
+#include "htmlout.h"
+
+#include "domquery.h"
+
+#include <QDir>
+#include <QDomDocument>
+#include <QDomElement>
+#include <QProcess>
+
+QList<QDomElement>elementsByTagName(const QDomElement&root,QString tag)
+{
+       QDomNodeList cn=root.childNodes();
+       QList<QDomElement>ret;
+       for(int i=0;i<cn.size();i++){
+               QDomElement e=cn.at(i).toElement();
+               if(e.isNull())continue;
+               if(e.tagName()!=tag)continue;
+               ret<<e;
+       }
+       return ret;
+}
+
+
+WocProcessor* WocProcessor::inst=0;
+
+WocProcessor::WocProcessor()
+{
+       m_baseDir=QDir(".").absolutePath();
+       m_wobDir=".";
+       m_verComm="0";
+       m_verNeedComm="0";
+       m_verHR="0.0";
+       m_svnTarget=".";
+       m_svnRev="unknown uncertain";
+       m_svnExe="svn";
+       m_dbInst="dbInst";
+       m_dbSchema="dbSchema";
+       m_error=false;
+       m_projname="WobProject";
+       
+       inst=this;
+}
+
+bool WocProcessor::processFile(QString fn)
+{
+       //open file
+       QFile fd(fn);
+       if(!fd.open(QIODevice::ReadOnly)){
+               qDebug("Error: cannot read file %s",fn.toLocal8Bit().data());
+               return false;
+       }
+       //get XML content
+       QDomDocument doc;
+       QString err;int eln,ecl;
+       if(!doc.setContent(&fd,&err,&eln,&ecl)){
+               qDebug("Error: XML parser error in file %s line %i column %i: %s",fn.toLocal8Bit().data(),eln,ecl,err.toLocal8Bit().data());
+               fd.close();
+               return false;
+       }
+       fd.close();
+       QDomElement root=doc.documentElement();
+       if(root.isNull() || root.tagName()!="Wolf"){
+               qDebug("Error: XML File %s is not a Wolf.",fn.toLocal8Bit().data());
+               return false;
+       }
+       //go on, process file
+       qDebug("Info: processing file %s...",fn.toLocal8Bit().data());
+       m_error=false;
+       QDomNodeList nl=root.childNodes();
+       for(int i=0;i<nl.size();i++){
+               if(!nl.at(i).isElement())continue;
+               QDomElement el=nl.at(i).toElement();
+               if(el.isNull())continue;
+               QString tn=el.tagName();
+               if(tn=="Class"){
+                       WocClass cls(el);
+                       if(cls.isValid()){
+                               m_classes.append(cls);
+                               emit newClass(cls);
+                               if(m_error)return false;
+                       }else
+                               return false;
+               }else
+               if(tn=="Table"){
+                       WocTable tbl(el);
+                       if(tbl.isValid()){
+                               m_tables.append(tbl);
+                               emit newTable(tbl);
+                               if(m_error)return false;
+                               if(tbl.isAuditable()){
+                                       WocTable atbl=tbl.auditTable();
+                                       m_tables.append(atbl);
+                                       emit newTable(atbl);
+                                       if(m_error)return false;
+                               }
+                       }else
+                               return false;
+               }else
+               if(tn=="Transaction"){
+                       WocTransaction trn(el);
+                       if(trn.isValid()){
+                               m_transactions.append(trn);
+                               emit newTransaction(trn);
+                               if(m_error)return false;
+                       }else
+                               return false;
+               }else
+               if(tn=="Project"){
+                       if(el.hasAttribute("baseDir"))
+                               m_baseDir=el.attribute("baseDir");
+                       if(el.hasAttribute("wobDir"))
+                               m_wobDir=el.attribute("wobDir");
+                       if(el.hasAttribute("name"))
+                               m_projname=el.attribute("name");
+               }else
+               if(tn=="Include"){
+                       if(!processFile(m_baseDir+"/"+m_wobDir+"/"+el.attribute("file")))
+                               return false;
+               }else
+               if(tn=="DataBase"){
+                       if(el.hasAttribute("instance"))
+                               m_dbInst=el.attribute("instance","dbInst");
+                       if(el.hasAttribute("schema"))
+                               m_dbSchema=el.attribute("schema","dbSchema");
+                       if(el.hasAttribute("version"))
+                               m_dbVer=el.attribute("version");
+                       QList<QDomElement> nl=elementsByTagName(el,"AuditTables");
+                       for(int i=0;i<nl.size();i++)
+                               WocTable::parseAuditStatic(nl.at(i).toElement());
+               }else
+               if(tn=="QtClientOutput"){
+                       new WocQtClientOut(el);
+                       if(m_error)return false;
+               }else
+               if(tn=="PHPServerOutput"){
+                       new WocPHPServerOut(el);
+                       if(m_error)return false;
+               }else
+               if(tn=="HtmlOutput"){
+                       new WocHtmlOut(el);
+                       if(m_error)return false;
+               }else
+               if(tn=="Version"){
+                       if(el.hasAttribute("comm"))
+                               m_verComm=el.attribute("comm");
+                       if(el.hasAttribute("needcomm"))
+                               m_verNeedComm=el.attribute("needcomm");
+                       if(el.hasAttribute("humanReadable"))
+                               m_verHR=el.attribute("humanReadable");
+                       if(el.hasAttribute("svnTarget"))
+                               m_svnTarget=el.attribute("target");
+                       if(el.hasAttribute("svnExe"))
+                               m_svnExe=el.attribute("exe");
+                       callSvn();
+               }else
+               if(tn=="Doc"){
+                       QString s=el.text().trimmed();
+                       if(s!="")m_docstrings<<s;
+               }
+               else{
+                       qDebug("Warning: file %s has unknown element '%s' at line %i column %i", fn.toLocal8Bit().data(), tn.toLocal8Bit().data(), el.lineNumber(), el.columnNumber());
+               }
+       }
+       //TODO: verify classes
+       //TODO: verify transactions
+       
+       //return success
+       return true;
+}
+
+void WocProcessor::callSvn()
+{
+       //svn info
+       QString svntgt=m_baseDir+"/"+m_svnTarget;
+       QProcess svn;
+       svn.setProcessChannelMode(QProcess::MergedChannels);
+       svn.setWorkingDirectory(svntgt);
+       svn.start(m_svnExe,QStringList()<<"info"<<"-R"<<"--xml"<<"--non-interactive"<<".");
+       svn.waitForFinished();
+       if(svn.exitCode()!=0){
+               qDebug("Warning: error while calling svn info.");
+               return;
+       }else{
+               QDomDocument doc;
+               if(!doc.setContent(svn.readAllStandardOutput())){
+                       qDebug("Warning: unable to parse output of svn info.");
+                       return;
+               }
+               //parse it:revisions
+               QStringList rl=MDomQuery(doc,"/*/entry/@revision");
+               if(rl.size()==0){
+                       m_svnRev="unknown";
+               }else{
+                       int minv,maxv;
+                       minv=maxv=rl[0].toInt();
+                       for(int i=1;i<rl.size();i++){
+                               int cv=rl[i].toInt();
+                               if(cv<minv)minv=cv;
+                               if(cv>maxv)maxv=cv;
+                       }
+                       if(minv==maxv)
+                               m_svnRev=QString::number(maxv);
+                       else
+                               m_svnRev=QString::number(minv)+"-"+QString::number(maxv);
+               }
+               //parse for repository path
+               MDomNodeList nl=MDomQuery(doc,"/*/entry");
+               for(int i=0;i<nl.size();i++){
+                       QDomElement el=nl.at(i).toElement();
+                       if(el.isNull())continue;
+                       if(el.attribute("path")=="."){
+                               rl=MDomQuery(el,"url");
+                               if(rl.size()>0)m_svnUrl=rl[0];
+                               rl=MDomQuery(el,"repository/root");
+                               if(rl.size()>0)m_svnRoot=rl[0];
+                               break;//we can stop now
+                       }
+               }
+       }
+       //svn status
+       svn.start(m_svnExe,QStringList()<<"status"<<"--xml"<<"--non-interactive"<<".");
+       svn.waitForFinished();
+       if(svn.exitCode()!=0){
+               qDebug("Warning: error while calling svn status.");
+               m_svnRev+=" uncertain";
+               return;
+       }else{
+               QDomDocument doc;
+               if(!doc.setContent(svn.readAllStandardOutput())){
+                       qDebug("Warning: unable to parse output of svn status.");
+                       m_svnRev+=" uncertain";
+                       return;
+               }
+               QStringList rl=MDomQuery(doc,"/status/target/entry/wc-status/@item");
+               bool ismod=false;
+               for(int i=0;i<rl.size();i++){
+                       if(rl[i]=="unversioned" || rl[i]=="normal")continue;
+                       ismod=true;
+                       break;
+               }
+               if(ismod)m_svnRev+=" modified";
+               else m_svnRev+=" vanilla";
+       }
+       qDebug("Info: SVN version info: \"%s\".",m_svnRev.toAscii().data());
+}
+
+void WocProcessor::finalize()
+{
+       emit sfinalize();
+}
+
+bool WocProcessor::hasTable(QString n)const
+{
+       for(int i=0;i<m_tables.size();i++)
+               if(m_tables[i].name()==n)return true;
+       return false;
+}
+
+WocTable WocProcessor::table(QString n)const
+{
+       for(int i=0;i<m_tables.size();i++)
+               if(m_tables[i].name()==n)return m_tables[i];
+       return WocTable();
+}
+
+bool WocProcessor::hasClass(QString n)const
+{
+       for(int i=0;i<m_classes.size();i++)
+               if(m_classes[i].name()==n)return true;
+       return false;
+}
+
+void WocProcessor::errorFound()
+{
+       m_error=true;
+}
+
+QStringList WocProcessor::transactionNames()const
+{
+       QStringList l;
+       for(int i=0;i<m_transactions.size();i++)
+               l<<m_transactions[i].name();
+       return l;
+}
+
+QStringList WocProcessor::classNames()const
+{
+       QStringList l;
+       for(int i=0;i<m_classes.size();i++)
+               l<<m_classes[i].name();
+       return l;
+}
+
+QStringList WocProcessor::tableNames()const
+{
+       QStringList l;
+       for(int i=0;i<m_tables.size();i++)
+               l<<m_tables[i].name();
+       return l;
+}
+
+QStringList WocProcessor::privilegeNames()const
+{
+       QStringList l;
+       for(int i=0;i<m_transactions.size();i++){
+               QString cn=m_transactions[i].name();
+               QStringList priv=m_transactions[i].privileges();
+               for(int j=0;j<priv.size();j++)
+                       l<<(cn+":"+priv[j]);
+       }
+       return l;
+}
+
+/******************************************************************************
+ * WocOutput
+ ******************************************************************************/
+
+WocOutput::WocOutput()
+{
+       connect(WocProcessor::instance(),SIGNAL(sfinalize()),this,SLOT(finalize()));
+       connect(WocProcessor::instance(),SIGNAL(newClass(const WocClass&)),this,SLOT(newClass(const WocClass&)));
+       connect(WocProcessor::instance(),SIGNAL(newTable(const WocTable&)),this,SLOT(newTable(const WocTable&)));
+       connect(WocProcessor::instance(),SIGNAL(newTransaction(const WocTransaction&)),this,SLOT(newTransaction(const WocTransaction&)));
+       connect(this,SIGNAL(errorFound()),WocProcessor::instance(),SLOT(errorFound()));
+}
+
+WocOutput::~WocOutput(){}
+
+/******************************************************************************
+ * WocClass
+ ******************************************************************************/
+
+WocClass::WocClass(const QDomElement&cls)
+{
+       //scan basics
+       m_valid=true;
+       m_abstract=str2bool(cls.attribute("abstract","0"));
+       WocProcessor *woc=WocProcessor::instance();
+       QRegExp symok("[a-z_][a-z0-9_]*",Qt::CaseInsensitive);
+       m_name=cls.attribute("name");
+       if(m_name==""){
+               qDebug("Error: unnamed class at line %i column %i.",cls.lineNumber(),cls.columnNumber());
+               m_valid=false;
+               return;
+       }
+       if(woc->hasClass(m_name)){
+               qDebug("Error: double definition of class %s at line %i column %i.",m_name.toAscii().data(),cls.lineNumber(),cls.columnNumber());
+               m_valid=false;
+               return;
+       }
+       if(!symok.exactMatch(m_name)){
+               qDebug("Error: Illegal class name %s.",m_name.toAscii().data());
+               m_valid=false;
+               return;
+       }
+       qDebug("Info: parsing class %s",m_name.toAscii().data());
+       m_sbase=cls.attribute("serverbase","WObject");
+       m_cbase=cls.attribute("clientbase","WObject");
+       //scan properties
+       QList<QDomElement> nl=elementsByTagName(cls,"Property");
+       for(int i=0;i<nl.size();i++){
+               QDomElement el=nl.at(i).toElement();
+               if(el.isNull())continue;
+               s_prop p;
+               p.name=el.attribute("name");
+               if(!symok.exactMatch(p.name)){
+                       qDebug("Error: Illegal property %s in class %s.",p.name.toAscii().data(),m_name.toAscii().data());
+                       m_valid=false;
+                       return;
+               }
+               if(hasProperty(p.name)){
+                       qDebug("Error: Double definition of property %s in class %s.",p.name.toAscii().data(),m_name.toAscii().data());
+                       m_valid=false;
+                       return;
+               }
+               p.type=el.attribute("type");
+               p.isid=str2bool(el.attribute("id","0"));
+               p.isabstract=str2bool(el.attribute("abstract","0"));
+               m_props.append(p);
+               //docu
+               QString s=el.text().trimmed();
+               if(s!="")m_propdoc.insert(p.name,s);
+       }
+       //scan enums
+       nl=elementsByTagName(cls,"Enum");
+       for(int i=0;i<nl.size();i++){
+               QDomElement el=nl.at(i).toElement();
+               if(el.isNull())continue;
+               QString nm=el.attribute("name");
+               if(!symok.exactMatch(nm)){
+                       qDebug("Error: Illegal enum type %s in class %s.",nm.toAscii().data(),m_name.toAscii().data());
+                       m_valid=false;
+                       return;
+               }
+               if(hasEnumType(nm)){
+                       qDebug("Error: Double definition of enum type %s in class %s.",nm.toAscii().data(),m_name.toAscii().data());
+                       m_valid=false;
+                       return;
+               }
+               QList<WocEnum>ev;
+               //check whether there is a reference
+               if(el.hasAttribute("refColumn")){
+                       QStringList ref=el.attribute("refColumn").split(":");
+                       if(ref.size()!=2){
+                               qDebug("Error: illegal enum reference in class %s enum %s.",m_name.toAscii().data(),nm.toAscii().data());
+                               m_valid=false;
+                               return;
+                       }
+                       if(!woc->hasTable(ref[0])){
+                               qDebug("Error: enum reference in class %s enum %s points to non-existant table.",m_name.toAscii().data(),nm.toAscii().data());
+                               m_valid=false;
+                               return;
+                       }
+                       WocTable tab=woc->table(ref[0]);
+                       if(!tab.hasColumn(ref[1])){
+                               qDebug("Error: enum reference in class %s enum %s points to non-existant column.",m_name.toAscii().data(),nm.toAscii().data());
+                               m_valid=false;
+                               return;
+                       }
+                       ev=tab.columnEnums(ref[1]);
+               }
+               //scan values
+               QList<QDomElement> nl2=elementsByTagName(el,"Value");
+               int nxval=0;
+               for(int j=0;j<nl2.size();j++){
+                       QDomElement el2=nl2.at(j).toElement();
+                       if(el2.isNull())continue;
+                       QString n=el2.attribute("name");
+                       if(n==""){
+                               qDebug("Warning: anonymous enum value in class %s enum %s. Ignoring it.",m_name.toAscii().data(),nm.toAscii().data());
+                               continue;
+                       }
+                       nxval=el2.attribute("value",QString::number(nxval)).toInt(0,0);
+                       ev.append(WocEnum(n,nxval,el2.text().trimmed()));
+                       nxval++;
+                       //TODO: check that value name does not exist yet
+               }
+               m_enumvals.insert(nm,ev);
+       }
+       //scan mappings
+       nl=elementsByTagName(cls,"Mapping");
+       for(int i=0;i<nl.size();i++){
+               QDomElement el=nl.at(i).toElement();
+               if(el.isNull())continue;
+               QString name=el.attribute("table");
+               if(!symok.exactMatch(name)){
+                       qDebug("Error: Illegal mapping %s in class %s.",name.toAscii().data(),m_name.toAscii().data());
+                       m_valid=false;
+                       return;
+               }
+               if(hasMapping(name)){
+                       qDebug("Error: Double definition of mapping %s in class %s.",name.toAscii().data(),m_name.toAscii().data());
+                       m_valid=false;
+                       return;
+               }
+               QList<s_map>map;
+               QList<QDomElement> nl2=elementsByTagName(el,"Map");
+               for(int j=0;j<nl2.size();j++){
+                       QDomElement el2=nl2.at(j).toElement();
+                       if(el2.isNull())continue;
+                       s_map sm;
+                       sm.column=el2.attribute("column");
+                       sm.property=el2.attribute("property");
+                       if(sm.property=="")sm.property=sm.column;
+                       if(sm.column=="")sm.column=sm.property;
+                       if(sm.column==""){
+                               qDebug("Warning: empty mapping in class %s mapping %s. Ignoring it.",m_name.toAscii().data(),name.toAscii().data());
+                               continue;
+                       }
+                       QList<QDomElement> nl3=elementsByTagName(el2,"Call");
+                       for(int k=0;k<nl3.size();k++){
+                               QDomElement el3=nl3.at(k).toElement();
+                               if(el3.isNull())continue;
+                               QString lang=el3.attribute("lang");
+                               QString meth=el3.attribute("method","false");
+                               sm.method.insert(lang,meth);
+                       }
+                       map.append(sm);
+               }
+               m_maps.insert(name,map);
+       }
+       //docu
+       nl=elementsByTagName(cls,"Doc");
+       for(int i=0;i<nl.size();i++){
+               QString s=nl.at(i).toElement().text().trimmed();
+               if(s!="")m_docstrings<<s;
+       }
+       //check abstraction
+       if(!m_abstract && isAbstract("")){
+               qDebug("Warning: class %s should be declared abstract.",m_name.toAscii().data());
+       }
+       //conditional abstraction (must be after check or we'll get false positives)
+       nl=elementsByTagName(cls,"Abstract");
+       for(int i=0;i<nl.size();i++){
+               QString s=nl.at(i).toElement().attribute("lang").trimmed();
+               if(s!="")m_cabstract<<s;
+       }
+}
+
+bool WocClass::hasProperty(QString p)const
+{
+       for(int i=0;i<m_props.size();i++)
+               if(m_props[i].name==p)return true;
+       return false;
+}
+
+QStringList WocClass::propertyNames()const
+{
+       QStringList r;
+       for(int i=0;i<m_props.size();i++)
+               r<<m_props[i].name;
+       return r;
+}
+
+QString WocClass::propertyType(QString p)const
+{
+       for(int i=0;i<m_props.size();i++)
+               if(m_props[i].name==p)return m_props[i].type;
+       return "";
+}
+
+bool WocClass::propertyIsIdentity(QString p)const
+{
+       for(int i=0;i<m_props.size();i++)
+               if(m_props[i].name==p)return m_props[i].isid;
+       return false;
+}
+
+bool WocClass::propertyIsAbstract(QString p)const
+{
+       for(int i=0;i<m_props.size();i++)
+               if(m_props[i].name==p)return m_props[i].isabstract;
+       return false;
+}
+
+bool WocClass::isAbstract(QString l)const
+{
+       if(m_cabstract.contains(l))return true;
+       for(int i=0;i<m_props.size();i++)
+               if(m_props[i].isabstract)return true;
+       return m_abstract;
+}
+
+QString WocClass::propertyPlainType(QString p)const
+{
+       QString r=propertyType(p);
+       if(r.startsWith("List:"))return r.mid(5);
+       else return r;
+}
+
+const QStringList WocClass::attrtypes=QStringList()<<"astring"<<"int"<<"int32"<<"int64"<<"bool";
+bool WocClass::propertyIsAttribute(QString p)const
+{
+       QString t=propertyPlainType(p);
+       if(attrtypes.contains(t))return true;
+       if(hasEnumType(t))return true;
+       return false;
+}
+
+const QStringList WocClass::elemtypes=QStringList()<<"string"<<"blob";
+bool WocClass::propertyIsElement(QString p)const
+{
+       QString t=propertyPlainType(p);
+       if(elemtypes.contains(t))return true;
+       if(hasEnumType(t))return true;
+       return false;
+}
+
+bool WocClass::propertyIsObject(QString p)const
+{
+       QString t=propertyPlainType(p);
+       //default types take precedence over classes
+       if(attrtypes.contains(t))return false;
+       if(elemtypes.contains(t))return false;
+       //enums shadow classes
+       if(hasEnumType(t))return false;
+       //check that the class exists
+       return WocProcessor::instance()->hasClass(t);
+}
+
+bool WocClass::propertyIsEnum(QString p)const
+{
+       QString t=propertyPlainType(p);
+       //default types take precedence over enums
+       if(attrtypes.contains(t))return false;
+       if(elemtypes.contains(t))return false;
+       //enums
+       return hasEnumType(t);
+}
+
+bool WocClass::propertyIsList(QString p)const
+{
+       if(propertyType(p).startsWith("List:"))return true;
+       else return false;
+}
+
+QMap<QString,QString> WocClass::mapping(QString m)const
+{
+       if(!m_maps.contains(m))return QMap<QString,QString>();
+       QList<s_map> sml=m_maps[m];
+       QMap<QString,QString>ret;
+       for(int i=0;i<sml.size();i++)
+               ret.insert(sml[i].property,sml[i].column);
+       return ret;
+}
+
+QStringList WocClass::mappingProperties(QString m)const
+{
+       if(!m_maps.contains(m))return QStringList();
+       QList<s_map> sml=m_maps[m];
+       QStringList ret;
+       for(int i=0;i<sml.size();i++)
+               ret<<sml[i].property;
+       return ret;
+}
+
+QString WocClass::mapMethod(QString table,QString property,QString lang)const
+{
+       if(!m_maps.contains(table))return "";
+       QList<s_map> sml=m_maps[table];
+       for(int i=0;i<sml.size();i++)
+               if(sml[i].property==property){
+                       if(sml[i].method.contains(lang))return sml[i].method[lang];
+                       else return "";
+               }
+       return "";
+}
+
+
+/******************************************************************************
+ * WocTable
+ ******************************************************************************/
+
+WocTable::WocTable()
+{
+       m_backup=m_valid=m_audit=false;
+}
+
+WocTable::WocTable(const QDomElement&tbl)
+{
+       m_valid=true;
+       //parse XML
+       m_name=tbl.attribute("name");
+       WocProcessor*woc=WocProcessor::instance();
+       QRegExp good("[a-z][a-z0-9_]*",Qt::CaseInsensitive);
+       //check name syntax, check it does not exist yet
+       if(woc->hasTable(m_name)){
+               qDebug("Error: double definition of table %s.",m_name.toAscii().data());
+               m_valid=false;
+               return;
+       }
+       if(!good.exactMatch(m_name)){
+               qDebug("Error: table %s does not have a regular name.",m_name.toAscii().data());
+               m_valid=false;
+               return;
+       }
+       m_backup=str2bool(tbl.attribute("backup","0"));
+       m_base=tbl.attribute("base","WobTable");
+       m_audit=str2bool(tbl.attribute("audit","0"));
+       qDebug("Info: parsing table %s",m_name.toAscii().data());
+       //Columns
+       QList<QDomElement> nl=elementsByTagName(tbl,"Column");
+       for(int i=0;i<nl.size();i++){
+               QDomElement el=nl.at(i).toElement();
+               if(el.isNull())continue;
+               QPair<bool,s_col> cl=parseColumn(el,m_name);
+               if(!cl.first)return;
+               if(hasColumn(cl.second.name)){
+                       qDebug("Error: double definition of column %s in table %s.",cl.second.name.toAscii().data(),m_name.toAscii().data());
+                       m_valid=false;
+                       return;
+               }
+               m_columns.append(cl.second);
+       }
+       //Audit Columns
+       nl=elementsByTagName(tbl,"AuditColumn");
+       for(int i=0;i<nl.size();i++){
+               QDomElement el=nl.at(i).toElement();
+               if(el.isNull())continue;
+               QPair<bool,s_col> cl=parseColumn(el,m_name);
+               if(!cl.first)return;
+               m_auditcolumns.append(cl.second);
+       }
+       
+       //Foreign getter methods
+       nl=elementsByTagName(tbl,"Foreign");
+       for(int i=0;i<nl.size();i++){
+               QDomElement el=nl.at(i).toElement();
+               if(el.isNull())continue;
+               m_foreign.append(QPair<QString,QString>(el.attribute("method"),el.attribute("via")));
+               //TODO: validate foreign getter
+               //docu
+               QString s=el.text().trimmed();
+               if(s!="")m_fordocs.insert(el.attribute("method"),s);
+       }
+       
+       //Presets
+       nl=elementsByTagName(tbl,"Preset");
+       for(int i=0;i<nl.size();i++){
+               QDomElement el=nl.at(i).toElement();
+               if(el.isNull())continue;
+               QMap<QString,QString>ps;
+               QList<QDomElement> nl2=elementsByTagName(el,"V");
+               for(int j=0;j<nl2.size();j++){
+                       QDomElement el2=nl2.at(j).toElement();
+                       if(el2.isNull())continue;
+                       if(!el2.hasAttribute("col") || (!el2.hasAttribute("val") && !el2.hasAttribute("code"))){
+                               qDebug("Error: table definition %s contains illegal preset.",m_name.toAscii().data());
+                               m_valid=false;
+                               return;
+                       }
+                       QString nm=el2.attribute("col");
+                       if(!hasColumn(nm)){
+                               qDebug("Error: table definition %s contains preset for invalid column %s.",m_name.toAscii().data(),nm.toAscii().data());
+                               m_valid=false;
+                               return;
+                       }
+                       QString v;
+                       if(el2.hasAttribute("val"))v="\""+el2.attribute("val").replace("\"","\\\"")+"\"";
+                       else v=el2.attribute("code");
+                       ps.insert(nm,v);
+               }
+               if(ps.size()>0)m_presets.append(ps);
+       }
+       
+       //Docu
+       nl=elementsByTagName(tbl,"Doc");
+       for(int i=0;i<nl.size();i++){
+               QString s=nl.at(i).toElement().text().trimmed();
+               if(s!="")m_docstrings<<s;
+       }
+       
+       //sanity checks
+       //check we have any columns at all
+       if(m_columns.size()==0){
+               qDebug("Error: table %s does not have any columns.",m_name.toAscii().data());
+               m_valid=false;
+               return;
+       }
+       //check that we have a primary key
+       if(primaryColumns().size()==0){
+               qDebug("Warning: table %s does not have any primary key columns.",m_name.toAscii().data());
+       }
+}
+
+
+QPair<bool,WocTable::s_col> WocTable::parseColumn(const QDomElement&el,QString m_name)
+{
+       s_col cl;
+       QRegExp good("[a-z][a-z0-9_]*",Qt::CaseInsensitive);
+       cl.name=el.attribute("name");
+       //check name syntax, check it is not doubled
+       if(!good.exactMatch(cl.name)){
+               qDebug("Error: table %s column %s does not have a regular name.",m_name.toAscii().data(),cl.name.toAscii().data());
+               return QPair<bool,s_col>(false,s_col());
+       }
+       cl.isprime=str2bool(el.attribute("primarykey","0"));
+       cl.isunique=str2bool(el.attribute("unique","0"));
+       cl.isindex=str2bool(el.attribute("index","0"));
+       if(cl.isprime)cl.isnull=false;
+       else cl.isnull=true;
+       if(el.hasAttribute("null"))
+               cl.isnull=str2bool(el.attribute("null"));
+       else if(el.hasAttribute("notnull"))
+               cl.isnull=!str2bool(el.attribute("notnull","0"));
+       cl.type=el.attribute("type");
+       //TODO: validate type
+       cl.foreign=el.attribute("foreignkey");
+       WocProcessor*woc=WocProcessor::instance();
+       //check foreign key exists
+       if(cl.foreign!=""){
+               QStringList fgn=cl.foreign.split(":");
+               if(fgn.size()!=2){
+                       qDebug("Error: table %s column %s: foreign key definition must use syntax 'table:column'",m_name.toAscii().data(),cl.name.toAscii().data());
+                       return QPair<bool,s_col>(false,s_col());
+               }
+               if(!woc->hasTable(fgn[0])){
+                       qDebug("Error: table %s column %s: foreign key target table %s does not exist",m_name.toAscii().data(),cl.name.toAscii().data(),fgn[0].toAscii().data());
+                       return QPair<bool,s_col>(false,s_col());
+               }
+               if(!woc->table(fgn[0]).hasColumn(fgn[1])){
+                       qDebug("Error: table %s column %s: foreign key target table/column %s does not exist",m_name.toAscii().data(),cl.name.toAscii().data(),cl.foreign.toAscii().data());
+                       return QPair<bool,s_col>(false,s_col());
+               }
+       }
+       cl.defaultval=el.attribute("default");
+       //TODO: validate default against type
+       QList<QDomElement> nl2=elementsByTagName(el,"Value");
+       int nxval=0;
+       //enum values
+       for(int j=0;j<nl2.size();j++){
+               QDomElement el2=nl2.at(j).toElement();
+               if(el2.isNull())continue;
+               QString n=el2.attribute("name");
+               if(n==""){
+                       qDebug("Warning: anonymous enum value in table %s column %s. Ignoring it.",m_name.toAscii().data(),cl.name.toAscii().data());
+                       continue;
+               }
+               nxval=el2.attribute("value",QString::number(nxval)).toInt(0,0);
+               cl.enumvals.append(WocEnum(n,nxval,el2.text().trimmed()));
+               nxval++;
+       }
+       //default calls
+       nl2=elementsByTagName(el,"Call");
+       for(int j=0;j<nl2.size();j++){
+               QDomElement el2=nl2.at(j).toElement();
+               if(el2.isNull())continue;
+               QString lang=el2.attribute("lang");
+               QString meth=el2.attribute("method","false");
+               cl.methodcalls.insert(lang,meth);
+       }
+       //docu
+       QDomNodeList nl3=el.childNodes();
+       for(int j=0;j<nl3.size();j++){
+               QDomNode n=nl3.at(j);
+               if(n.isText()||n.isCDATASection())cl.doc+=" "+n.nodeValue();
+               else
+               if(n.isElement()&&n.nodeName()=="Doc")cl.doc+=" "+n.toElement().text();
+       }
+       cl.doc=cl.doc.trimmed();
+       
+       return QPair<bool,s_col>(true,cl);
+}
+
+QStringList WocTable::columns()const
+{
+       QStringList r;
+       for(int i=0;i<m_columns.size();i++)
+               r<<m_columns[i].name;
+       return r;
+}
+
+QStringList WocTable::auditColumns()const
+{
+       QStringList r;
+       for(int i=0;i<m_staticauditcolumns.size();i++)
+               r<<m_staticauditcolumns[i].name;
+       for(int i=0;i<m_auditcolumns.size();i++)
+               r<<m_auditcolumns[i].name;
+       return r;
+}
+
+QStringList WocTable::primaryColumns()const
+{
+       QStringList r;
+       for(int i=0;i<m_columns.size();i++)
+               if(m_columns[i].isprime)
+                       r<<m_columns[i].name;
+       return r;
+}
+
+QString WocTable::columnType(QString c)const
+{
+       for(int i=0;i<m_columns.size();i++)
+               if(m_columns[i].name==c)
+                       return m_columns[i].type;
+       return "";
+}
+
+bool WocTable::columnIsNull(QString c)const
+{
+       for(int i=0;i<m_columns.size();i++)
+               if(m_columns[i].name==c)
+                       return m_columns[i].isnull;
+       return false;
+}
+
+bool WocTable::columnIsPrimary(QString c)const
+{
+       for(int i=0;i<m_columns.size();i++)
+               if(m_columns[i].name==c)
+                       return m_columns[i].isprime;
+       return false;
+}
+
+bool WocTable::columnHasDefault(QString c)const
+{
+       for(int i=0;i<m_columns.size();i++)
+               if(m_columns[i].name==c)
+                       return m_columns[i].defaultval!="";
+       return false;
+}
+
+QString WocTable::columnDefault(QString c)const
+{
+       for(int i=0;i<m_columns.size();i++)
+               if(m_columns[i].name==c)
+                       return m_columns[i].defaultval;
+       return "";
+}
+
+bool WocTable::hasColumn(QString c)const
+{
+       for(int i=0;i<m_columns.size();i++)
+               if(m_columns[i].name==c)
+                       return true;
+       return false;
+}
+
+QList<WocEnum> WocTable::columnEnums(QString c)const
+{
+       for(int i=0;i<m_columns.size();i++)
+               if(m_columns[i].name==c)
+                       return m_columns[i].enumvals;
+       return QList<WocEnum>();
+}
+
+QList<WocEnum> WocTable::getEnums()const
+{
+       QList<WocEnum> r;
+       for(int i=0;i<m_columns.size();i++)
+               for(int j=0;j<m_columns[i].enumvals.size();j++)
+                       r.append(m_columns[i].enumvals[j]);
+       return r;
+}
+bool WocTable::columnIsForeign(QString c)const
+{
+       for(int i=0;i<m_columns.size();i++)
+               if(m_columns[i].name==c)
+                       return m_columns[i].foreign!="";
+       return false;
+}
+
+QString WocTable::columnForeign(QString c)const
+{
+       for(int i=0;i<m_columns.size();i++)
+               if(m_columns[i].name==c)
+                       return m_columns[i].foreign;
+       return "";
+}
+
+bool WocTable::columnIsIndexed(QString c)const
+{
+       for(int i=0;i<m_columns.size();i++)
+               if(m_columns[i].name==c)
+                       return m_columns[i].isindex;
+       return false;
+}
+
+bool WocTable::columnIsUnique(QString c)const
+{
+       for(int i=0;i<m_columns.size();i++)
+               if(m_columns[i].name==c)
+                       return m_columns[i].isunique;
+       return false;
+}
+
+QString WocTable::columnDoc(QString c)const
+{
+       for(int i=0;i<m_columns.size();i++)
+               if(m_columns[i].name==c)
+                       return m_columns[i].doc;
+       return "";
+}
+
+
+QStringList WocTable::foreigns()const
+{
+       QStringList r;
+       for(int i=0;i<m_foreign.size();i++)
+               r<<m_foreign[i].first;
+       return r;
+}
+
+QString WocTable::foreignQuery(QString f)const
+{
+       for(int i=0;i<m_foreign.size();i++)
+               if(m_foreign[i].first==f)
+                       return m_foreign[i].second;
+       return "";
+}
+
+bool WocTable::haveForeign(QString f)const
+{
+       for(int i=0;i<m_foreign.size();i++)
+               if(m_foreign[i].first==f)
+                       return true;
+       return false;
+}
+
+QString WocTable::columnCall(QString col,QString lang)const
+{
+       for(int i=0;i<m_columns.size();i++){
+               s_col cl=m_columns[i];
+               if(cl.name==col){
+                       if(!cl.methodcalls.contains(lang))return "";
+                       else return cl.methodcalls[lang];
+               }
+       }
+       return "";
+}
+
+QList<WocTable::s_col>WocTable::m_staticauditcolumns;
+void WocTable::parseAuditStatic(const QDomElement&el)
+{
+       QList<QDomElement> nl=elementsByTagName(el,"Column");
+       for(int i=0;i<nl.size();i++){
+               QDomElement el2=nl.at(i).toElement();
+               if(el2.isNull())continue;
+               QPair<bool,s_col>cl=parseColumn(el2,"(global audit settings)");
+               if(!cl.first){
+                       WocProcessor::instance()->errorFound();
+                       return;
+               }
+               m_staticauditcolumns.append(cl.second);
+       }
+}
+
+WocTable WocTable::auditTable()const
+{
+       WocTable adt;
+       adt.m_valid=m_valid;
+       adt.m_backup=m_backup;
+       adt.m_audit=false;
+       adt.m_name=m_name+"_audit";//enhance the name
+       adt.m_base="WobTable";//revert to default
+       adt.m_foreign=m_foreign;
+       adt.m_fordocs=m_fordocs;
+       adt.m_docstrings=m_docstrings;
+       //these stay empty: m_presets, m_auditcolumns
+       
+       //now for the complicated stuff
+       //create primary key
+       s_col cl;
+       cl.name="auditid";cl.type="seq64";cl.doc="additional primary key for auditing";
+       cl.isindex=cl.isunique=cl.isnull=false;cl.isprime=true;
+       adt.m_columns.append(cl);
+       //copy common columns
+       adt.m_columns.append(m_staticauditcolumns);
+       //copy normal columns
+       for(int i=0;i<m_columns.size();i++){
+               //get and reset
+               cl=m_columns[i];
+               cl.isprime=cl.isunique=false;
+               cl.methodcalls.clear();
+               if(cl.type.left(3)=="seq")cl.type="int"+cl.type.mid(3);
+               //no foreign keys
+               cl.foreign="";
+               //add
+               adt.m_columns.append(cl);
+       }
+       //copy local audit columns
+       adt.m_columns.append(m_auditcolumns);
+       
+       //return result
+       return adt;
+}
+
+
+/******************************************************************************
+ * WocTransaction
+ ******************************************************************************/
+
+WocTransaction::WocTransaction(const QDomElement&root)
+{
+       m_valid=true;
+       m_mode=Checked;
+       QRegExp rval("[a-z][a-z0-9_]*",Qt::CaseInsensitive);
+       //get basics
+       m_name=root.attribute("name");
+       if(!rval.exactMatch(m_name)){
+               qDebug("Error: invalid transaction name %s.",m_name.toAscii().data());
+               m_valid=false;
+               return;
+       }
+       qDebug("Info: parsing transaction %s",m_name.toAscii().data());
+       if(root.hasAttribute("mode")){
+               QString m=root.attribute("mode").toLower();
+               if(m=="checked")m_mode=Checked;else
+               if(m=="auth")m_mode=Auth;else
+               if(m=="open")m_mode=Open;
+               else{
+                       qDebug("Error: invalid transaction mode %s in transaction %s, must be checked, auth, or open.",m.toAscii().data(),m_name.toAscii().data());
+                       m_valid=false;
+                       return;
+               }
+       }
+       //input tag
+       QList<QDomElement> nl=elementsByTagName(root,"Input");
+       for(int i=0;i<nl.size();i++){
+               QDomElement el=nl.at(i).toElement();
+               if(el.isNull())continue;
+               //variables
+               QList<QDomElement> nl2=elementsByTagName(el,"Var");
+               for(int j=0;j<nl2.size();j++){
+                       QDomElement el2=nl2.at(j).toElement();
+                       if(el2.isNull())continue;
+                       QString nm=el2.attribute("name");
+                       if(!rval.exactMatch(nm)){
+                               qDebug("Error: invalid input variable %s in transaction %s.",nm.toAscii().data(),m_name.toAscii().data());
+                               m_valid=false;
+                               return;
+                       }
+                       if(hasInput(nm)){
+                               qDebug("Error: double definition of input variable %s in transaction %s.",nm.toAscii().data(),m_name.toAscii().data());
+                               m_valid=false;
+                               return;
+                       }
+                       QString tp=el2.attribute("type");
+                       //TODO: validate type
+                       m_input.append(QPair<QString,QString>(nm,tp));
+                       //docu
+                       QString s=el2.text().trimmed();
+                       if(s!="")m_indoc.insert(nm,s);
+               }
+       }
+       //call tag
+       nl=elementsByTagName(root,"Call");
+       for(int j=0;j<nl.size();j++){
+               QDomElement el=nl.at(j).toElement();
+               if(el.isNull())continue;
+               QString nm=el.attribute("lang");
+               if(hasCall(nm)){
+                       qDebug("Error: double definition of input variable %s in transaction %s.",nm.toAscii().data(),m_name.toAscii().data());
+                       m_valid=false;
+                       return;
+               }
+               QString mt=el.attribute("method");
+               m_call.insert(nm,mt);
+       }
+       //output
+       nl=elementsByTagName(root,"Output");
+       for(int i=0;i<nl.size();i++){
+               QDomElement el=nl.at(i).toElement();
+               if(el.isNull())continue;
+               //variables
+               QList<QDomElement> nl2=elementsByTagName(el,"Var");
+               for(int j=0;j<nl2.size();j++){
+                       QDomElement el2=nl2.at(j).toElement();
+                       if(el2.isNull())continue;
+                       QString nm=el2.attribute("name");
+                       if(!rval.exactMatch(nm)){
+                               qDebug("Error: invalid output variable %s in transaction %s.",nm.toAscii().data(),m_name.toAscii().data());
+                               m_valid=false;
+                               return;
+                       }
+                       if(hasOutput(nm)){
+                               qDebug("Error: double definition of output variable %s in transaction %s.",nm.toAscii().data(),m_name.toAscii().data());
+                               m_valid=false;
+                               return;
+                       }
+                       QString tp=el2.attribute("type");
+                       //TODO: validate type
+                       m_output.append(QPair<QString,QString>(nm,tp));
+                       //docu
+                       QString s=el2.text().trimmed();
+                       if(s!="")m_outdoc.insert(nm,s);
+               }
+       }
+       //docu
+       nl=elementsByTagName(root,"Doc");
+       for(int i=0;i<nl.size();i++){
+               QString s=nl.at(i).toElement().text().trimmed();
+               if(s!="")m_docstrings<<s;
+       }
+       //privileges
+       nl=elementsByTagName(root,"Privilege");
+       for(int i=0;i<nl.size();i++){
+               QString s=nl.at(i).toElement().attribute("name").trimmed();
+               if(s!="")m_privileges<<s;
+               m_privdoc.insert(s,nl.at(i).toElement().text().trimmed());
+       }
+}
+
+bool WocTransaction::hasInput(QString v)const
+{
+       for(int i=0;i<m_input.size();i++)
+               if(m_input[i].first==v)return true;
+       return false;
+}
+
+QStringList WocTransaction::inputNames()const
+{
+       QStringList r;
+       for(int i=0;i<m_input.size();i++)
+               r<<m_input[i].first;
+       return r;
+}
+
+QString WocTransaction::inputType(QString v)const
+{
+       for(int i=0;i<m_input.size();i++)
+               if(m_input[i].first==v)return m_input[i].second;
+       return "";
+}
+
+bool WocTransaction::hasOutput(QString v)const
+{
+       for(int i=0;i<m_output.size();i++)
+               if(m_output[i].first==v)return true;
+       return false;
+}
+
+QStringList WocTransaction::outputNames()const
+{
+       QStringList r;
+       for(int i=0;i<m_output.size();i++)
+               r<<m_output[i].first;
+       return r;
+}
+
+QString WocTransaction::outputType(QString v)const
+{
+       for(int i=0;i<m_output.size();i++)
+               if(m_output[i].first==v)return m_output[i].second;
+       return "";
+}
diff --git a/woc/processor.h b/woc/processor.h
new file mode 100644 (file)
index 0000000..845cbbe
--- /dev/null
@@ -0,0 +1,462 @@
+//
+// C++ Interface: processor
+//
+// Description: 
+//
+//
+// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2009
+//
+// Copyright: See README/COPYING files that come with this distribution
+//
+//
+
+#ifndef WOC_PROCESSOR_H
+#define WOC_PROCESSOR_H
+
+#include <QList>
+#include <QMap>
+#include <QObject>
+#include <QPair>
+#include <QStringList>
+
+class QDomElement;
+
+QList<QDomElement>elementsByTagName(const QDomElement&,QString);
+
+inline bool str2bool(QString s)
+{
+       bool b;
+       int i=s.toInt(&b,0);
+       if(b)return i?true:false;
+       s=s.toLower();
+       if(s=="yes"||s=="y"||s=="on"||s=="true"||s=="t")return true;
+       return false;
+}
+
+
+/**helper structure to store enums in classes and tables*/
+struct WocEnum {
+       QString name,doc;
+       int val;
+       WocEnum(){val=0;}
+       WocEnum(QString n,int v,QString d=""){name=n;val=v;doc=d;}
+};
+
+/**stores a communication class including serialization and deserialization information*/
+class WocClass
+{
+       public:
+               /**parses XML to create itself*/
+               WocClass(const QDomElement&);
+               
+               /**returns whether parsing was successful and this instance represents a valid communication class*/
+               bool isValid()const{return m_valid;}
+               
+               /**returns the class name*/
+               QString name()const{return m_name;}
+               /**returns the name of the class it is derived from (on the server side) - default: WObject*/
+               QString serverBaseClass()const{return m_sbase;}
+               /**returns the parent class of the class on client side- default: WObject*/
+               QString clientBaseClass()const{return m_cbase;}
+               
+               /**returns true of it has a property with this name*/
+               bool hasProperty(QString)const;
+               /**returns a list of all property names*/
+               QStringList propertyNames()const;
+               /**returns the type string of the property, including "List:" if applicable*/
+               QString propertyType(QString)const;
+               /**returns the plain type string of the property without "List:"*/
+               QString propertyPlainType(QString)const;
+               /**returns whether this property identifies the object instance*/
+               bool propertyIsIdentity(QString)const;
+               /**returns whether this property is abstract*/
+               bool propertyIsAbstract(QString)const;
+               /**returns whether this property is serialized as XML attribute*/
+               bool propertyIsAttribute(QString)const;
+               /**returns whether this property is serialized as XML element*/
+               bool propertyIsElement(QString)const;
+               /**returns whether this property has an enum type*/
+               bool propertyIsEnum(QString)const;
+               /**returns whether the property is an object*/
+               bool propertyIsObject(QString)const;
+               /**returns whether the property is a list of values (false for scalars)*/
+               bool propertyIsList(QString)const;
+               /**returns whether the property type is integer*/
+               bool propertyIsInt(QString p)const{QString pt=propertyPlainType(p);return pt=="int" || pt=="int32" || pt=="int64";}
+               /**returns whether the property type is boolean*/
+               bool propertyIsBool(QString p)const{return propertyPlainType(p)=="bool";}
+               /**returns whether the property type is string*/
+               bool propertyIsString(QString p)const{QString pt=propertyPlainType(p);return pt=="string"||pt=="astring";}
+               /**returns whether the property type is blob*/
+               bool propertyIsBlob(QString p)const{QString pt=propertyPlainType(p);return pt=="blob";}
+               
+               /**returns whether the class is abstract in the requested language (needs to be customized); it is automatically abstract if any property is abstract*/
+               bool isAbstract(QString)const;
+               /**returns the languages in which the class is conditionally abstract*/
+               QStringList abstractLangs()const{return m_cabstract;}
+               
+               /**returns the names of all enum types of this class*/
+               QStringList enumTypes()const{return m_enumvals.keys();}
+               /**returns true if the given enum type exists in this class*/
+               bool hasEnumType(QString t)const{return m_enumvals.contains(t);}
+               /**returns a list of enum values as name-value pairs*/
+               QList<WocEnum> enumValues(QString t)const{return m_enumvals[t];}
+               
+               /**returns true if the given mapping exists*/
+               bool hasMapping(QString m)const{return m_maps.contains(m);}
+               /**returns the names of all tables for which a mapping exists*/
+               QStringList mappingTables()const{return m_maps.keys();}
+               /**returns the (correctly ordered) properties for a mapping*/
+               QStringList mappingProperties(QString)const;
+               /**returns the specific mapping; map key=property, map value=column*/
+               QMap<QString,QString> mapping(QString m)const;
+               /**returns the method for a specific mapping or an empty string if it does not exist in the specified language*/
+               QString mapMethod(QString table,QString property,QString lang)const;
+               
+               /**returns documentation for this class*/
+               QStringList docStrings()const{return m_docstrings;}
+               /**returns documentation for a property*/
+               QString propDoc(QString p)const
+               {if(m_propdoc.contains(p))return m_propdoc[p];else return "";}
+               
+       private:
+               //valid: parsing the WOLF succeeded
+               //abstract: the class is declared abstract (isAbstract may return true even if this is false)
+               //cabstract: conditional abstract - just for one or two languages; abstract overrides cabstract!
+               bool m_valid,m_abstract;
+               QStringList m_cabstract;
+               //name: class name
+               //base: name of parent class (s=server, c=client)
+               QString m_name,m_sbase,m_cbase;
+               //property info
+               struct s_prop{
+                       QString name,type;
+                       bool isid,isabstract;
+               };
+               QList<s_prop> m_props;
+               //mappings: "table-name" => List of ("column-name","property-name")
+               struct s_map{
+                       QString column,property;
+                       QMap<QString,QString>method;//lang->method
+               };
+               QMap<QString,QList<s_map> >m_maps;
+               //enum types: "type-name" => List of ("constant-name",int-constant-value)
+               QMap<QString,QList<WocEnum> >m_enumvals;
+               //serializers: "name" => List of properties (syntax Objects: "propertyname/Serializer"
+//             QMap<QString,QStringList> m_serial;
+
+               //docu
+               QStringList m_docstrings;
+               QMap<QString,QString>m_propdoc;
+               
+               //helper: contains predefined types sorted by serialization type
+               static const QStringList attrtypes,elemtypes;
+};
+
+/**stores the internal representation of a database table and its abstraction class*/
+class WocTable
+{
+       public:
+               /**initializes an invalid table*/
+               WocTable();
+               /**initializes a table from XML*/
+               WocTable(const QDomElement&);
+               
+               /**returns whether this instance is valid, ie. whether parsing was successful*/
+               bool isValid()const{return m_valid;}
+               
+               /**returns the table name*/
+               QString name()const{return m_name;}
+               /**returns whether the table is marked for backup*/
+               bool inBackup()const{return m_backup;}
+               /**returns the parent class of the table class - default: WobTable*/
+               QString baseClass()const{return m_base;}
+               
+               /**returns whether the table has a column with this name*/
+               bool hasColumn(QString)const;
+               /**returns a list of all defined column names*/
+               QStringList columns()const;
+               /**returns the list of all primary key columns*/
+               QStringList primaryColumns()const;
+               /**returns the data type of the column*/
+               QString columnType(QString)const;
+               /**returns whether the column allows NULLs*/
+               bool columnIsNull(QString)const;
+               /**returns whether the column is part of the primary key*/
+               bool columnIsPrimary(QString)const;
+               /**returns whether the column has a default*/
+               bool columnHasDefault(QString)const;
+               /**returns the default value of the column (empty string if there is none)*/
+               QString columnDefault(QString)const;
+               /**returns whether the column is a foreign key*/
+               bool columnIsForeign(QString)const;
+               /**returns the foreign key reference of the column in the format table:column*/
+               QString columnForeign(QString)const;
+               /**returns whether the column has an index*/
+               bool columnIsIndexed(QString)const;
+               /**returns whether the column has a unique constraint*/
+               bool columnIsUnique(QString)const;
+               /**returns enum definitions of the column - each pair contains the symbolic name in first and the assigned integer value in second*/
+               QList<WocEnum> columnEnums(QString)const;
+               /**returns the insert call of a column for a specific language; empty string if there is none*/
+               QString columnCall(QString col,QString lang)const;
+               
+               /**returns all enum definitions of the table; see also columnEnums */
+               QList<WocEnum> getEnums()const;
+               
+               /**returns a list of all foreign definitions - methods that return data from other tables*/
+               QStringList foreigns()const;
+               /**returns the definition of a specific foreign table query method*/
+               QString foreignQuery(QString)const;
+               /**returns whether a foreign table query method exists*/
+               bool haveForeign(QString)const;
+               
+               /**returns a list of all preset values (to be generated when the DB is created);
+               each entry in the list is a dictionary with the column name as key and the intended preset value as value - each entry of the list is one DB row, each key-value-pair in the map is one preset value in that row*/
+               QList<QMap<QString,QString> > presets()const{return m_presets;}
+               
+               /**parses the static part of auditing*/
+               static void parseAuditStatic(const QDomElement&);
+               /**returns whether the table is auditable*/
+               bool isAuditable()const{return m_audit;}
+               /**creates and returns the table instance that represents the audit table*/
+               WocTable auditTable()const;
+               /**returns the names of audit columns (except auditid)*/
+               QStringList auditColumns()const;
+               
+               /**returns table documentation*/
+               QStringList docStrings()const{return m_docstrings;}
+               /**returns column documentation*/
+               QString columnDoc(QString c)const;
+               /**returns foreign getter documentation*/
+               QString foreignDoc(QString c)const
+               {if(m_fordocs.contains(c))return m_fordocs[c];else return "";}
+               
+       private:
+               bool m_valid,m_backup,m_audit;
+               QString m_name,m_base;
+               struct s_col {
+                       QString name,type,foreign,defaultval,doc;
+                       bool isnull,isprime,isindex,isunique;
+                       QList<WocEnum>enumvals;
+                       QMap<QString,QString>methodcalls;
+               };
+               QList<s_col>m_columns,m_auditcolumns;
+               static QList<s_col>m_staticauditcolumns;
+               QList<QPair<QString,QString> >m_foreign;
+               QList<QMap<QString,QString> >m_presets;
+               
+               QStringList m_docstrings;
+               QMap<QString,QString>m_fordocs;
+               
+               //helper method: parses a single column element
+               static QPair<bool,s_col> parseColumn(const QDomElement&,QString);
+};
+
+/**internal representation of a transaction*/
+class WocTransaction
+{
+       public:
+               /**initializes a transaction from XML*/
+               WocTransaction(const QDomElement&);
+               /**returns whether parsing it was successful*/
+               bool isValid()const{return m_valid;}
+               
+               /**returns the name of the transaction*/
+               QString name()const{return m_name;}
+               
+               /**returns whether an input variable exists*/
+               bool hasInput(QString v)const;
+               /**returns the names of all inputs in the order of definition*/
+               QStringList inputNames()const;
+               /**returns the type of an input variable*/
+               QString inputType(QString)const;
+               
+               /**returns whether an output variable exists*/
+               bool hasOutput(QString v)const;
+               /**returns the names of all outputs in the order of definition*/
+               QStringList outputNames()const;
+               /**returns the type of an output variable*/
+               QString outputType(QString)const;
+               
+               /**returns whether a specific language binding exists for a call*/
+               bool hasCall(QString c)const{return m_call.contains(c);}
+               /**returns the called function*/
+               QString callFunction(QString c)const{return m_call[c];}
+               
+               /**authentication mode*/
+               enum AuthMode {
+                       /**default: need a valid session and the privilege*/
+                       Checked,
+                       /**only need to be authenticated, every valid user can do it*/
+                       Auth,
+                       /**available even to anonymous/unauthenticated users*/
+                       Open
+               };
+               AuthMode authMode()const{return m_mode;}
+               
+               /**returns true if the type given is a list*/
+               bool isListType(QString t)const{return t.startsWith("List:");}
+               /**returns the type without list or xml qualifiers*/
+               QString plainType(QString t)const{
+                       if(t.startsWith("List:"))t=t.mid(5);
+                       QStringList l=t.split("/",QString::SkipEmptyParts);
+                       if(l.size()>0)t=l[0];else t="unknown";
+                       return t;
+               }
+               /**returns the XML serializer for Object types*/
+               QString typeSerializer(QString t)const{
+                       QStringList l=t.split("/",QString::SkipEmptyParts);
+                       if(l.size()>1)return l[1];
+                       else return "";
+               }
+               /**returns true if the type is integer*/
+               bool isIntType(QString t)const{QString pt=plainType(t);return pt=="int" || pt=="int32" || pt=="int64";}
+               /**returns true if the type is boolean*/
+               bool isBoolType(QString t)const{return plainType(t)=="bool";}
+               /**returns true if the type is a string*/
+               bool isStringType(QString t)const{QString p=plainType(t);return p=="astring"||p=="string";}
+               /**returns true if the type is a blob*/
+               bool isBlobType(QString t)const{QString p=plainType(t);return p=="blob";}
+               /**returns true if the type is to be encoded as attribute*/
+               bool isAttributeType(QString t)const{return t=="astring"||t=="int"||t=="int32"||t=="int64"||t=="bool";}
+               /**returns true if the type is to be encoded as element*/
+               bool isElementType(QString t)const{return !isAttributeType(t);}
+               /**return true if the type is an object type*/
+               bool isObjectType(QString t)const{QString p=plainType(t);return p!="astring"&&p!="string"&&p!="int"&&p!="int32"&&p!="int64"&&p!="bool"&&p!="blob";}
+               
+               /**return the documentation of the transaction*/
+               QStringList docStrings()const{return m_docstrings;}
+               /**return docu of input element*/
+               QString inputDoc(QString v)const
+               {if(m_indoc.contains(v))return m_indoc[v];else return "";}
+               /**return docu of output element*/
+               QString outputDoc(QString v)const
+               {if(m_outdoc.contains(v))return m_outdoc[v];else return "";}
+               /**return docu of a privilege*/
+               QString privilegeDoc(QString p)const
+               {if(m_privdoc.contains(p))return m_privdoc[p];else return "";}
+               
+               /**return privileges that exist inside this transaction*/
+               QStringList privileges()const{return m_privileges;}
+       private:
+               QString m_name;
+               bool m_valid;
+               AuthMode m_mode;
+               QMap<QString,QString> m_call;
+               QList<QPair<QString,QString> >m_input,m_output;
+               QStringList m_privileges;
+               //docu
+               QStringList m_docstrings;
+               QMap<QString,QString>m_indoc,m_outdoc,m_privdoc;
+};
+
+/**base class of all output generators*/
+class WocOutput:public QObject
+{
+       Q_OBJECT
+       public:
+               /**the constructor should set up and initialize the environment of the generator*/
+               WocOutput();
+               /**currently there is no guarantee that the destructor is ever called.*/
+               virtual ~WocOutput();
+               
+       protected slots:
+               /**called whenever the parser finds a new class in the XML input; some references might not exist yet*/
+               virtual void newClass(const WocClass&)=0;
+               /**called whenever the parser finds a new table in the XML input; the parser guarantees that tables it depends on already exist (it will throw an error otherwise)*/
+               virtual void newTable(const WocTable&)=0;
+               /**called whenever the parser finds a new transaction; the parser guarantees that all referenced types exist (it will throw an error otherwise)*/
+               virtual void newTransaction(const WocTransaction&)=0;
+               /**called when the parsing is complete: it should clean up the generators environment; it may not be called if woc stops with an error*/
+               virtual void finalize()=0;
+       signals:
+               void errorFound();
+};
+
+/**central processing singleton*/
+class WocProcessor:public QObject
+{
+       Q_OBJECT
+       public:
+               WocProcessor();
+               
+               /**called from main loop to parse a file*/
+               bool processFile(QString);
+               /**called from main loop to finalize its work*/
+               void finalize();
+               
+               /**returns the instance of the processor (if it exists yet)*/
+               static WocProcessor* instance(){return inst;}
+               
+               /**returns the base directory of the project (all other pathes are relative to it)*/
+               QString baseDir()const{return m_baseDir;}
+               /**returns the directory where WOLFs are found, should normally not be used outside this class*/
+               QString wobDir()const{return m_wobDir;}
+               /**returns the project name (default="WobProject")*/
+               QString projectName()const{return m_projname;}
+               /**returns the current communication protocol version*/
+               QString verComm()const{return m_verComm;}
+               /**returns the communication protocol version that is at least needed to be compatible*/
+               QString verNeedComm()const{return m_verNeedComm;}
+               /**returns a human readable version string*/
+               QString verHR()const{return m_verHR;}
+               /**returns the SVN revision of the project: it contains two tokens, the first one is the current revision or min-max if the working copy contains mixed revisions; the second token contains the word "vanilla" if there are no local modifications, or "modified" if there are local modifications*/
+               QString svnRevision()const{return m_svnRev;}
+               /**returns the SVN Repository Root*/
+               QString svnRepositoryRoot()const{return m_svnRoot;}
+               /**returns the complete SVN Repository URL*/
+               QString svnRepositoryUrl()const{return m_svnUrl;}
+               /**returns the variable name that will contain the database driver instance*/
+               QString dbInst()const{return m_dbInst;}
+               /**returns the variable name that will contain the database schema object*/
+               QString dbSchema()const{return m_dbSchema;}
+               /**returns the database schema version*/
+               QString dbVersion()const{return m_dbVer;}
+               
+               /**returns whether a table exists*/
+               bool hasTable(QString)const;
+               /**returns the requested table*/
+               WocTable table(QString)const;
+               
+               /**returns whether a class exists*/
+               bool hasClass(QString)const;
+               
+               /**returns a list of transaction names*/
+               QStringList transactionNames()const;
+               /**returns a list of class names*/
+               QStringList classNames()const;
+               /**returns a list of table names*/
+               QStringList tableNames()const;
+               
+               /**returns global docu*/
+               QStringList docStrings()const{return m_docstrings;}
+               
+               /**returns the qualified names of all privileges*/
+               QStringList privilegeNames()const;
+       signals:
+               void sfinalize();
+               void newClass(const WocClass&);
+               void newTable(const WocTable&);
+               void newTransaction(const WocTransaction&);
+       public slots:
+               void errorFound();
+       private:
+               QString m_baseDir,m_wobDir,m_verComm,m_verNeedComm,m_verHR,m_projname;
+               QString m_svnTarget,m_svnRev,m_svnExe,m_svnRoot,m_svnUrl;
+               QString m_dbInst,m_dbSchema,m_dbVer;
+               QStringList m_docstrings;
+               bool m_error;
+               
+               QList<WocTable> m_tables;
+               QList<WocClass> m_classes;
+               QList<WocTransaction> m_transactions;
+               
+               static WocProcessor*inst;
+               
+               /**helper: calls SVN and parses its output*/
+               void callSvn();
+};
+
+
+#endif
diff --git a/woc/qtout.cpp b/woc/qtout.cpp
new file mode 100644 (file)
index 0000000..3762836
--- /dev/null
@@ -0,0 +1,821 @@
+//
+// C++ Implementation: qtout
+//
+// Description: 
+//
+//
+// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2009
+//
+// Copyright: See README/COPYING files that come with this distribution
+//
+//
+
+#include "qtout.h"
+
+#include <QDir>
+#include <QDomElement>
+
+//start section of all header files
+static const QByteArray HDRSTART=
+       "//AUTOMATICALLY GENERATED FILE\n"
+       "//DO NOT EDIT THIS FILE DIRECTLY, USE THE XML SOURCE!\n"
+       "#ifndef WOBGEN_%\n"
+       "#define WOBGEN_%\n\n";
+//end section of all header files
+static const QByteArray HDREND="\n//END OF AUTOMATICALLY GENERATED FILE\n#endif\n";
+
+//start section of all source files
+static const QByteArray SRCSTART=
+       "//AUTOMATICALLY GENERATED FILE\n"
+       "//DO NOT EDIT THIS FILE DIRECTLY, USE THE XML SOURCE!\n"
+       "#include \"%.h\"\n"
+       "#include <QDomElement>\n"
+       "#include <QDomDocument>\n";
+//end section of all source files
+static const QByteArray SRCEND="\n//END OF AUTOMATICALLY GENERATED FILE\n";
+
+WocQtClientOut::WocQtClientOut(QDomElement&el)
+{
+       qDebug("Info: creating Qt Client Output Generator.");
+       m_basedir=WocProcessor::instance()->baseDir()+"/"+el.attribute("sourceDir",".");
+       m_subdir=el.attribute("subDir","qtwob");
+       m_clean=str2bool(el.attribute("clean","0"));
+       m_prefix=el.attribute("classPrefix","Wob");
+       //get/create directory
+       QDir d(m_basedir+"/"+m_subdir);
+       if(!d.exists())QDir(".").mkpath(m_basedir+"/"+m_subdir);
+       
+       //create project file
+       m_pri.setFileName(m_basedir+"/"+m_subdir+"/"+el.attribute("priInclude","qtwob.pri"));
+       if(!m_pri.open(QIODevice::WriteOnly|QIODevice::Truncate)){
+               qDebug("Error: cannot create Qt project file %s.",m_pri.fileName().toAscii().data());
+               emit errorFound();
+               return;
+       }
+       m_pri.write(QByteArray("#AUTOMATICALLY GENERATED FILE - DONT CHANGE!\n"));
+       
+       //create interface class
+       WocProcessor*woc=WocProcessor::instance();
+       QString pn=woc->projectName();
+       m_ifacecpp.setFileName(m_basedir+"/"+m_subdir+"/"+m_prefix+"Interface.cpp");
+       if(!m_ifacecpp.open(QIODevice::WriteOnly|QIODevice::Truncate)){
+               qDebug("Error: cannot create Qt interface file %s.",m_ifacecpp.fileName().toAscii().data());
+               emit errorFound();
+               return;
+       }
+       m_ifacecpp.write(QByteArray(SRCSTART).replace('%',m_prefix+"Interface"));
+       m_iface.setFileName(m_basedir+"/"+m_subdir+"/"+m_prefix+"Interface.h");
+       if(!m_iface.open(QIODevice::WriteOnly|QIODevice::Truncate)){
+               qDebug("Error: cannot create Qt interface file %s.",m_iface.fileName().toAscii().data());
+               emit errorFound();
+               return;
+       }
+       m_iface.write(QByteArray(HDRSTART).replace("%",m_prefix.toAscii()+"INTERFACE_H"));
+       m_iface.write(QByteArray("#include \""+m_prefix.toAscii()+"Include.h\"\n#include \"WInterface.h\"\n"));
+       m_iface.write(QByteArray("#include <QStringList>\n"));
+       m_iface.write(QString("class "+m_prefix+"Interface:public WInterface\n{\n Q_OBJECT\n").toAscii());
+       m_iface.write(QString(" public:\n  "+m_prefix+"Interface(QString name=\""+pn+"\"):WInterface(name){}\n").toAscii());
+       m_iface.write(QString("  static "+m_prefix+"Interface*instance(QString name=\""+pn+"\")\n\t{return qobject_cast<"+m_prefix+"Interface*>(WInterface::instance(name));}\n\n").toAscii());
+       m_iface.write(QString("  static QString commVersion(){return \""+woc->verComm()+"\";}\n").toAscii());
+       m_iface.write(QString("  static QString needCommVersion(){return \""+woc->verNeedComm()+"\";}\n").toAscii());
+       m_iface.write(QString("  static QString version(){return \""+woc->verHR()+"\";}\n").toAscii());
+       m_iface.write(QString("  static QString svnVersion(){return \""+woc->svnRevision()+"\";}\n\n").toAscii());
+       m_iface.write(QString("  static QString svnRepositoryRoot(){return \""+woc->svnRepositoryRoot()+"\";}\n\n").toAscii());
+       m_iface.write(QString("  static QString svnRepositoryUrl(){return \""+woc->svnRepositoryUrl()+"\";}\n\n").toAscii());
+       
+       //create all includer
+       m_hdr.setFileName(m_basedir+"/"+m_subdir+"/"+m_prefix+"Include.h");
+       if(!m_hdr.open(QIODevice::WriteOnly|QIODevice::Truncate)){
+               qDebug("Error: cannot create Qt header file %s.",m_hdr.fileName().toAscii().data());
+               emit errorFound();
+               return;
+       }
+       m_hdr.write(QByteArray(HDRSTART).replace("%",m_prefix.toAscii()+"INCLUDE_H"));
+       addFile(m_prefix.toAscii()+"Interface");
+}
+
+WocQtClientOut::~WocQtClientOut(){}
+
+void WocQtClientOut::finalize()
+{
+       trnList();
+       m_ifacecpp.write(SRCEND);
+       m_ifacecpp.close();
+       m_iface.write(QByteArray("};\n")+HDREND);
+       m_iface.close();
+       m_pri.write(QByteArray("\n#END OF AUTOGENERATED PRI FILE\n"));
+       m_pri.close();
+       m_hdr.write(HDREND);
+       m_hdr.close();
+       //cleanup directory (remove untouched normal files, assume remainder is harmless)
+       QDir d(m_basedir+"/"+m_subdir);
+       if(m_clean){
+               QStringList ent=d.entryList(QDir::Files);
+               for(int i=0;i<ent.size();i++)
+                       if(!MFile::touchedFile(m_basedir+"/"+m_subdir+"/"+ent[i])){
+                               qDebug("Info: removing old file %s",ent[i].toAscii().data());
+                               d.remove(ent[i]);
+                       }
+       }
+}
+
+void WocQtClientOut::newTable(const WocTable&){/*not needed, Qt client is a few levels higher*/}
+
+void WocQtClientOut::newClass(const WocClass&cls)
+{
+       QString cn=m_prefix+"O"+cls.name();
+       QString cna=cn;
+       if(cls.isAbstract("qt"))cna+="Abstract";
+       addFile(cna);
+       MFile hdr(m_basedir+"/"+m_subdir+"/"+cna+".h");
+       MFile src(m_basedir+"/"+m_subdir+"/"+cna+".cpp");
+       if(!hdr.open(QIODevice::WriteOnly|QIODevice::Truncate) ||
+          !src.open(QIODevice::WriteOnly|QIODevice::Truncate)){
+               qDebug("Error: cannot create class files for class %s.",cn.toAscii().data());
+               emit errorFound();
+               return;
+       }
+       //lead in
+       hdr.write(QByteArray(HDRSTART).replace("%",cna.toAscii()));
+       src.write(QByteArray(SRCSTART).replace("%",cna.toAscii()));
+       
+       QString hcd;
+       QString scd;
+       //includes
+       hcd="#include \""+cls.clientBaseClass()+".h\"\n#include <QCoreApplication>\n\n";
+       QStringList k=cls.propertyNames();
+       for(int i=0;i<k.size();i++)
+               if(cls.propertyIsObject(k[i]))
+                       hcd+="#include \""+m_prefix+"O"+cls.propertyPlainType(k[i])+".h\"\n";
+
+       //class declaration
+       hcd+="class "+cna+":public "+cls.clientBaseClass()+"\n{\n";
+       hdr.write(hcd.toAscii());
+       
+       //enums
+       classEnums(cls,hdr,src,cna);
+       
+       //properties
+       classProperties(cls,hdr,src);
+       
+       //serializer
+       classSerializers(cls,hdr,src,cna);
+       
+       //deserializer (constructor)
+       classDeserializer(cls,hdr,src,cna);
+       
+       //lead out
+       hdr.write(QByteArray("\n};\n"));
+       hdr.write(QByteArray(HDREND).replace("%",cna.toAscii()));
+       src.write(QByteArray(SRCEND).replace("%",cna.toAscii()));
+}
+
+void WocQtClientOut::classEnums(const WocClass&cls,MFile&hdr,MFile&src,QString cn)
+{
+       QStringList k=cls.enumTypes();
+       if(k.size()==0)return;
+       QString hcd="  public:\n";
+       QString scd;
+       for(int i=0;i<k.size();i++){
+               //type
+               hcd+="\tenum "+k[i]+"{";
+               QList<WocEnum>ev=cls.enumValues(k[i]);
+               if(ev.size()<1){
+                       qDebug("Error: enums must have at least one member - class %s enum %s",cls.name().toAscii().data(),k[i].toAscii().data());
+                       emit errorFound();
+                       return;
+               }
+               for(int j=0;j<ev.size();j++){
+                       if(j)hcd+=",";
+                       hcd+="\n\t\t"+ev[j].name+"="+QString::number(ev[j].val);
+               }
+               hcd+="\n\t};\n";
+               //string converters
+               hcd+="\tstatic "+k[i]+" str2"+k[i]+"(QString,bool*ok=0);\n";
+               hcd+="\tstatic QString "+k[i]+"2str("+k[i]+");\n";
+               scd+=cn+"::"+k[i]+" "+cn+"::str2"+k[i]+"(QString s,bool*ok)\n{\n";
+               scd+="\ts=s.toLower();if(ok)*ok=true;\n";
+               for(int j=0;j<ev.size();j++){
+                       scd+="\tif(s==\""+ev[j].name.toLower()+"\")return "+ev[j].name+";\n";
+               }
+               scd+="\tif(ok)*ok=false;\n\treturn "+ev[0].name+";\n}\n";
+               scd+="QString "+cn+"::"+k[i]+"2str("+k[i]+" e)\n{\n";
+               for(int j=0;j<ev.size();j++){
+                       scd+="\tif(e=="+ev[j].name+")return \""+ev[j].name+"\";\n";
+               }
+               scd+="\treturn \"\";\n}\n";
+               //localized string converters
+               hcd+="\tstatic "+k[i]+" locstr2"+k[i]+"(QString,bool*ok=0);\n";
+               hcd+="\tstatic QString "+k[i]+"2locstr("+k[i]+");\n";
+               scd+=cn+"::"+k[i]+" "+cn+"::locstr2"+k[i]+"(QString s,bool*ok)\n{\n";
+               scd+="\ts=s.toLower();if(ok)*ok=true;\n";
+               for(int j=0;j<ev.size();j++){
+                       scd+="\tif(s==QCoreApplication::translate(\""+cn+"\",\""+ev[j].name+"\").toLower())return "+ev[j].name+";\n";
+               }
+               scd+="\tif(ok)*ok=false;\n\treturn "+ev[0].name+";\n}\n";
+               scd+="QString "+cn+"::"+k[i]+"2locstr("+k[i]+" e)\n{\n";
+               for(int j=0;j<ev.size();j++){
+                       scd+="\tif(e=="+ev[j].name+")return QCoreApplication::translate(\""+cn+"\",\""+ev[j].name+"\");\n";
+               }
+               scd+="\treturn \"\";\n}\n";
+       }
+       hdr.write(hcd.toAscii());
+       src.write(scd.toAscii());
+}
+
+void WocQtClientOut::classProperties(const WocClass&cls,MFile&hdr,MFile&)
+{
+       QStringList k=cls.propertyNames();
+       if(k.size()==0)return;
+       QString hcd,scd;
+       //declare members
+       hcd="  protected:\n";
+       for(int i=0;i<k.size();i++){
+               hcd+="\t"+qttype(cls,k[i])+" mp_"+k[i]+";\n";
+       }
+       //declare getters
+       hcd+="  public:\n";
+       for(int i=0;i<k.size();i++){
+               hcd+="\tvirtual "+qttype(cls,k[i])+" "+k[i]+"()const{return mp_"+k[i]+";}\n";
+       }
+       
+       //declare setters
+       for(int i=0;i<k.size();i++){
+               hcd+="\tvirtual void set"+k[i]+"("+qttype(cls,k[i])+" s){mp_"+k[i]+"=s;}\n";
+               if(cls.propertyIsList(k[i])){
+                       hcd+="\tvirtual void clear"+k[i]+"(){mp_"+k[i]+".clear();}\n";
+                       hcd+="\tvirtual void add"+k[i]+"("+qttype(cls,k[i],false)+" a){mp_"+k[i]+".append(a);}\n";
+               }
+       }
+       //write
+       hdr.write(hcd.toAscii());
+}
+
+QString WocQtClientOut::qttype(const WocClass&cls,QString p,bool dolist)
+{
+       QString r;
+       if(cls.propertyIsList(p)&&dolist)r="QList<";
+       else r="Nullable<";
+       if(cls.propertyIsString(p))r+="QString";else
+       if(cls.propertyIsInt(p))r+="qint64";else
+       if(cls.propertyIsBool(p))r+="bool";else
+       if(cls.propertyIsBlob(p))r+="QByteArray";else
+       if(cls.propertyIsObject(p))r+=m_prefix+"O"+cls.propertyPlainType(p);
+       else r+=cls.propertyPlainType(p);
+       r+=">";
+       return r;
+}
+
+void WocQtClientOut::classDeserializer(const WocClass&cls,MFile&hdr,MFile&src,QString cn)
+{
+       QString hcd,scd;
+       hcd="  public:\n";
+       //this is needed to allow WObjects to be Nullable and some other operations...
+       hcd+="\t"+cn+"(){}\n";
+
+       //implement copiers
+       QStringList k=cls.propertyNames();
+       hcd+="\t"+cn+"(const "+cn+"&);\n";
+       scd+=cn+"::"+cn+"(const "+cn+"&o)\n\t:WObject()\n{\n";
+       for(int i=0;i<k.size();i++)
+               scd+="\tmp_"+k[i]+"=o.mp_"+k[i]+";\n";
+       scd+="}\n";
+       hcd+="\t"+cn+"& operator=(const "+cn+"&);\n";
+       scd+=cn+"& "+cn+"::operator=(const "+cn+"&o)\n{\n";
+       for(int i=0;i<k.size();i++)
+               scd+="\tmp_"+k[i]+"=o.mp_"+k[i]+";\n";
+       scd+="\treturn *this;\n}\n";
+
+       //implement deserializer (as constructor)
+       hcd+="\t"+cn+"(const QDomElement&);\n";
+       scd+=cn+"::"+cn+"(const QDomElement&root)\n\t:WObject()\n{\n";
+       scd+="\tQList<QDomElement> nl;\n";
+       for(int i=0;i<k.size();i++){
+               if(cls.propertyIsList(k[i])){
+                       scd+="\tnl=elementsByTagName(root,\""+k[i]+"\");\n";
+                       scd+="\tfor(int i=0;i<nl.size();i++){\n\t\tQDomElement el=nl.at(i).toElement();\n";
+                       scd+="\t\tif(el.isNull())continue;\n";
+                       if(cls.propertyIsInt(k[i])){
+                               scd+="\t\tbool b;\n\t\tint ct=el.text().toInt(&b);\n";
+                               scd+="\t\tif(b)add"+k[i]+"(ct);\n";
+                               scd+="\t\telse throw WDeserializerException(QCoreApplication::translate(\"WobTransaction\",\"Class '%1' property '%2' is integer list, but non-integer was found.\").arg(\""+cn+"\").arg(\""+k[i]+"\"));\n";
+                       }else
+                       if(cls.propertyIsBool(k[i])){
+                               scd+="\t\tadd"+k[i]+"(el.text()==\"yes\");\n";
+                       }else
+                       if(cls.propertyIsString(k[i])){
+                               scd+="\t\tadd"+k[i]+"(el.text());\n";
+                       }else
+                       if(cls.propertyIsBlob(k[i])){
+                               scd+="\t\tadd"+k[i]+"(QByteArray::fromBase64(el.text().toAscii()));\n";
+                       }else
+                       if(cls.propertyIsEnum(k[i])){
+                               scd+="\t\tbool b;\n";
+                               QString pt=cls.propertyPlainType(k[i]);
+                               scd+="\t\t"+pt+" ct=str2"+pt+"(root.attribute(\""+k[i]+"\"),&b);\n";
+                               scd+="\t\tif(b)add"+k[i]+"(ct);\n";
+                               scd+="\t\telse throw WDeserializerException(QCoreApplication::translate(\"WobTransaction\",\"Class '%1' property '%2' is enum list, invalid value was found.\").arg(\""+cn+"\").arg(\""+k[i]+"\"));\n";
+                       }else
+                       if(cls.propertyIsObject(k[i])){
+                               scd+="\t\tadd"+k[i]+"("+m_prefix+"O"+cls.propertyPlainType(k[i])+"(el));\n";
+                       }else{
+                               scd+="#error \"Internal Generator error.\"\n";
+                               qDebug("Error: unable to generate code for property %s of type %s.",k[i].toAscii().data(),cls.propertyType(k[i]).toAscii().data());
+                               emit errorFound();
+                               return;
+                       }
+                       scd+="\t}\n";
+               }else{
+                       //non-list types
+                       //attribute stored types
+                       if(cls.propertyIsAttribute(k[i])){
+                               scd+="\tif(root.hasAttribute(\""+k[i]+"\")){\n";
+                               if(cls.propertyIsInt(k[i])){
+                                       scd+="\t\tbool b;\n\t\tint ct=root.attribute(\""+k[i]+"\").toInt(&b);\n";
+                                       scd+="\t\tif(b)set"+k[i]+"(ct);\n";
+                                       scd+="\t\telse throw WDeserializerException(QCoreApplication::translate(\"WobTransaction\",\"Class '%1' property '%2' is integer, but non-integer was found.\").arg(\""+cn+"\").arg(\""+k[i]+"\"));\n";
+                               }else
+                               if(cls.propertyIsBool(k[i])){
+                                       scd+="\t\tset"+k[i]+"(root.attribute(\""+k[i]+"\")==\"yes\");\n";
+                               }else
+                               if(cls.propertyIsString(k[i])){
+                                       scd+="\t\tset"+k[i]+"(root.attribute(\""+k[i]+"\"));\n";
+                               }else
+                               if(cls.propertyIsEnum(k[i])){
+                                       scd+="\t\tbool b;\n";
+                                       QString pt=cls.propertyPlainType(k[i]);
+                                       scd+="\t\t"+pt+" ct=str2"+pt+"(root.attribute(\""+k[i]+"\"),&b);\n";
+                                       scd+="\t\tif(b)set"+k[i]+"(ct);\n";
+                                       scd+="\t\telse throw WDeserializerException(QCoreApplication::translate(\"WobTransaction\",\"Class '%1' property '%2' is enum, invalid value was found.\").arg(\""+cn+"\").arg(\""+k[i]+"\"));\n";
+                               }else{
+                                       scd+="#error \"Internal Generator error.\"\n";
+                                       qDebug("Error: unable to generate code for property %s of type %s.",k[i].toAscii().data(),cls.propertyType(k[i]).toAscii().data());
+                                       emit errorFound();
+                                       return;
+                               }
+                               scd+="\t}\n";
+                       }else{
+                               //element stored types...
+                               scd+="\tnl=elementsByTagName(root,\""+k[i]+"\");\n";
+                               scd+="\tif(nl.size()>0){\n";
+                               if(cls.propertyIsString(k[i])){
+                                       scd+="\t\tset"+k[i]+"(nl.at(0).toElement().text());\n";
+                               }else
+                               if(cls.propertyIsBlob(k[i])){
+                                       scd+="\t\tset"+k[i]+"(QByteArray::fromBase64(nl.at(0).toElement().text().toAscii()));\n";
+                               }else
+                               if(cls.propertyIsObject(k[i])){
+                                       scd+="\t\tset"+k[i]+"("+m_prefix+"O"+cls.propertyPlainType(k[i])+"(nl.at(0).toElement()));\n";
+                               }else{
+                                       scd+="#error \"Internal Generator error.\"\n";
+                                       qDebug("Error: unable to generate code for property %s of type %s.",k[i].toAscii().data(),cls.propertyType(k[i]).toAscii().data());
+                                       emit errorFound();
+                                       return;
+                               }
+                               scd+="\t}\n";
+                       }
+               }
+       }
+       scd+="}\n";
+       
+       //write it...
+       hdr.write(hcd.toAscii());
+       src.write(scd.toAscii());
+}
+
+void WocQtClientOut::classSerializers(const WocClass&cls,MFile&hdr,MFile&src,QString cn)
+{
+       QString hcd="  public:\n";
+       QString scd;
+       hcd+="\tQString toString();\n";
+       scd+="QString "+cn+"::toString()\n{\n";
+       scd+="\tQDomDocument doc;\n\tdoc.appendChild(toXml(doc));\n";
+       scd+="\treturn doc.toString();\n}\n";
+       hcd+="\tQDomElement toXml(QDomDocument&,QString name=\""+cls.name()+"\");\n";
+       scd+="QDomElement "+cn+"::toXml(QDomDocument&doc,QString name)\n{\n";
+       scd+="\tQDomElement r=doc.createElement(name);\n";
+       QStringList p=cls.propertyNames();
+       for(int j=0;j<p.size();j++){
+               QStringList pv=p[j].split("/",QString::SkipEmptyParts);
+               if(pv.size()<1){
+                       qDebug("Error: encountered empty property while creating serializer for class %s.",cls.name().toAscii().data());
+                       emit errorFound();
+                       return;
+               }
+               QString prop=pv[0];
+               QString var;
+               if(pv.size()>1)var=pv[1];
+               //is it a list
+               if(cls.propertyIsList(prop)){
+                       scd+="\tfor(int i=0;i<mp_"+prop+".size();i++){\n";
+                       if(cls.propertyIsObject(prop))
+                               scd+="\t\tr.appendChild(mp_"+prop+"[i].toXml"+var+"(doc,\""+prop+"\"));\n";
+                       else
+                       if(cls.propertyIsEnum(prop)){
+                               scd+="\t\tQDomElement el=doc.createElement(\""+prop+"\");\n";
+                               scd+="\t\tel.appendChild(doc.createTextNode("+cls.propertyPlainType(prop)+"2str(mp_"+prop+"[i])));\n";
+                               scd+="\t\tr.appendChild(el);\n";
+                       }else
+                       if(cls.propertyIsString(prop)){
+                               scd+="\t\tQDomElement el=doc.createElement(\""+prop+"\");\n";
+                               scd+="\t\tel.appendChild(doc.createTextNode(mp_"+prop+"[i]));\n";
+                               scd+="\t\tr.appendChild(el);\n";
+                       }else
+                       if(cls.propertyIsBool(prop)){
+                               scd+="\t\tQDomElement el=doc.createElement(\""+prop+"\");\n";
+                               scd+="\t\tel.appendChild(doc.createTextNode(mp_"+prop+"[i]?\"yes\":\"no\"));\n";
+                               scd+="\t\tr.appendChild(el);\n";
+                       }else
+                       if(cls.propertyIsBlob(prop)){
+                               scd+="\t\tQDomElement el=doc.createElement(\""+prop+"\");\n";
+                               scd+="\t\tel.appendChild(doc.createTextNode(mp_"+prop+"[i].toBase64()));\n";
+                               scd+="\t\tr.appendChild(el);\n";
+                       }else
+                       if(cls.propertyIsInt(prop)){
+                               scd+="\t\tQDomElement el=doc.createElement(\""+prop+"\");\n";
+                               scd+="\t\tel.appendChild(doc.createTextNode(QString::number(mp_"+prop+"[i])));\n";
+                               scd+="\t\tr.appendChild(el);\n";
+                       }else{
+                               qDebug("Error: cannot generate serializer for class %s property %s.",cls.name().toAscii().data(),prop.toAscii().data());
+                               emit errorFound();
+                               return;
+                       }
+                       scd+="\t}\n";
+               }else{
+                       //non lists
+                       scd+="\tif(!mp_"+prop+".isNull()){\n";
+                       if(cls.propertyIsAttribute(prop)){
+                               if(cls.propertyIsBool(prop))
+                                       scd+="\t\tr.setAttribute(\""+prop+"\",mp_"+prop+".value()?\"yes\":\"no\");\n";
+                               else
+                                       scd+="\t\tr.setAttribute(\""+prop+"\",mp_"+prop+".value());\n";
+                       }else{
+                               if(cls.propertyIsObject(prop)){
+                                       scd+="\t\tr.appendChild(mp_"+prop+".value().toXml"+var+"(doc,\""+prop+"\"));\n";
+                               }else
+                               if(cls.propertyIsString(prop)){
+                                       scd+="\t\tQDomElement el=doc.createElement(\""+prop+"\");\n";
+                                       scd+="\t\tel.appendChild(doc.createTextNode(mp_"+prop+".value()));\n";
+                                       scd+="\t\tr.appendChild(el);\n";
+                               }else
+                               if(cls.propertyIsBlob(prop)){
+                                       scd+="\t\tQDomElement el=doc.createElement(\""+prop+"\");\n";
+                                       scd+="\t\tel.appendChild(doc.createTextNode(mp_"+prop+".value().toBase64()));\n";
+                                       scd+="\t\tr.appendChild(el);\n";
+                               }else{
+                                       qDebug("Error: cannot generate serializer for class %s property %s.",cls.name().toAscii().data(),prop.toAscii().data());
+                                       emit errorFound();
+                                       return;
+                               }
+                       }
+                       scd+="\t}\n";
+               }
+       }
+       scd+="\treturn r;\n}\n";
+       hdr.write(hcd.toAscii());
+       src.write(scd.toAscii());
+}
+
+void WocQtClientOut::newTransaction(const WocTransaction&trn)
+{
+       QString cn=m_prefix+"T"+trn.name();
+       addFile(cn);
+       MFile hdr(m_basedir+"/"+m_subdir+"/"+cn+".h");
+       MFile src(m_basedir+"/"+m_subdir+"/"+cn+".cpp");
+       if(!hdr.open(QIODevice::WriteOnly|QIODevice::Truncate) ||
+          !src.open(QIODevice::WriteOnly|QIODevice::Truncate)){
+               qDebug("Error: cannot create class files for transaction %s.",cn.toAscii().data());
+               emit errorFound();
+               return;
+       }
+       //basics
+       QStringList in=trn.inputNames();
+       QStringList out=trn.outputNames();
+       //lead in
+       hdr.write(QByteArray(HDRSTART).replace("%",cn.toAscii()));
+       src.write(QByteArray(SRCSTART).replace("%",cn.toAscii()));
+       QString hcd;
+       QString scd;
+       hcd="#include \"WTransaction.h\"\n";
+       scd+="#include \"WInterface.h\"\n#include <QCoreApplication>\n";
+       for(int i=0;i<in.size();i++){
+               QString tp=qtobjtype(trn,in[i],In);
+               if(tp!="")hcd+="#include <"+tp+".h>\n";
+       }
+       for(int i=0;i<out.size();i++){
+               QString tp=qtobjtype(trn,out[i],Out);
+               if(tp!="")hcd+="#include <"+tp+".h>\n";
+       }
+       hcd+="\nclass "+cn+":public WTransaction\n{\n";
+       hdr.write(hcd.toAscii());
+       
+       //create properties
+       QString inlist,clist;
+       hcd="  private:\n";
+       for(int i=0;i<in.size();i++){
+               hcd+="\t"+qttype(trn,in[i],In)+"in_"+in[i]+";\n";
+               if(i){inlist+=",";clist+=",";}
+               inlist+="const "+qttype(trn,in[i],In)+"&a"+in[i];
+               clist+="a"+in[i];
+       }
+       for(int i=0;i<out.size();i++)
+               hcd+="\t"+qttype(trn,out[i],Out)+"out_"+out[i]+";\n";
+       hdr.write(hcd.toAscii());
+       
+       //query method decl
+       hdr.write(QByteArray("\tvoid netquery();\n"));
+       
+       //global interface code
+       QString sif="  "+cn+" query"+trn.name()+"("+inlist+")\n\t";
+       sif+="{return "+cn+"::query("+clist+(clist!=""?",":"")+"name());}\n";
+       m_iface.write(sif.toAscii());
+               
+       //create constructors
+       if(inlist!="")inlist+=",";
+       inlist+="QString iface";
+       if(clist!="")clist+=",";
+       clist+="iface";
+       QString defparm="=\""+WocProcessor::instance()->projectName()+"\"";
+       //define parametric constructor
+       hcd="  public:\n";
+       hcd+="\t"+cn+"("+inlist+defparm+");\n";
+       //parametric constructor implementation
+       scd+=cn+"::"+cn+"("+inlist+")\n\t:WTransaction(iface)\n{\n";
+       for(int i=0;i<in.size();i++){
+               scd+="\tin_"+in[i]+"=a"+in[i]+";\n";
+       }
+       scd+="}\n\n";
+       //decl copy constructor
+       hcd+="\t"+cn+"(const "+cn+"&);\n";
+       //copy constructor implementation
+       scd+=cn+"::"+cn+"(const "+cn+"&t)\n\t:WTransaction(t)\n{\n";
+       for(int i=0;i<in.size();i++){
+               scd+="\tin_"+in[i]+"=t.in_"+in[i]+";\n";
+       }
+       scd+="}\n\n";
+       //decl copy operator
+       hcd+="\t"+cn+"& operator=(const "+cn+"&);\n";
+       //copy operator implemented
+       scd+=cn+"& "+cn+"::operator=(const "+cn+"&t)\n{\n";
+       scd+="\tWTransaction::operator=(t);\n";
+       for(int i=0;i<in.size();i++){
+               scd+="\tin_"+in[i]+"=t.in_"+in[i]+";\n";
+       }
+       for(int i=0;i<out.size();i++){
+               scd+="\tout_"+out[i]+"=t.out_"+out[i]+";\n";
+       }
+       scd+="\treturn *this;\n}\n\n";
+       
+       //query method implemented
+       scd+="void "+cn+"::netquery()\n{\n";
+       scd+="\tWTLog log(this,\""+trn.name()+"\");\n";
+       scd+="\tQDomDocument doc;QDomElement root=doc.createElement(\"WobRequest\");\n";
+       scd+="\tQDomElement tmp;\n";
+       scd+="\tWInterface *iface=WInterface::instance(m_iface);\n";
+       scd+="\tif(iface==0){m_errtype=\"_iface\";m_errstr=\"interface not found\";m_stage=Error;log.setError(m_errstr);return;}\n";
+       //encode input
+       scd+=trnInput(trn);
+       scd+="\tdoc.appendChild(root);\n";
+       //query
+       scd+="\tQByteArray rba=executeQuery(\""+trn.name()+"\",doc.toByteArray());\n";
+       //decode output
+       scd+=trnOutput(trn);
+       scd+="}\n";
+       hdr.write(hcd.toAscii());
+       src.write(scd.toAscii());
+       
+       
+       //create getters
+       hcd="";
+       for(int i=0;i<out.size();i++){
+               QString tp=qttype(trn,out[i],Out);
+               hcd+="\t"+tp+" get"+out[i]+"(){return out_"+out[i]+";}\n";
+       }
+       //create queries
+       scd="";
+       hcd+="\tstatic "+cn+" query("+inlist+defparm+");\n";
+       scd+=cn+" "+cn+"::query("+inlist+")\n{\n";
+       scd+="\t"+cn;
+       if(clist!="")scd+=" r("+clist+");\n";else scd+=" r;";
+       scd+="\tr.netquery();\n\treturn r;\n}\n";
+       
+       //lead out
+       hdr.write(hcd.toAscii());
+       src.write(scd.toAscii());
+       hdr.write(QByteArray("\n};\n"));
+       hdr.write(QByteArray(HDREND).replace("%",cn.toAscii()));
+       src.write(QByteArray(SRCEND).replace("%",cn.toAscii()));
+}
+
+QString WocQtClientOut::trnInput(const WocTransaction&trn)
+{
+       QString code="\t/*start of input encoding*/\n";
+       QStringList sl=trn.inputNames();
+       for(int i=0;i<sl.size();i++){
+               QString t=trn.inputType(sl[i]);
+               if(trn.isAttributeType(t)){
+                       code+="\troot.setAttribute(\""+sl[i]+"\",in_"+sl[i];
+                       if(trn.isBoolType(t))
+                               code+="?\"yes\":\"no\"";
+                       code+=");\n";
+               }else{
+                       if(trn.isListType(t)){
+                               QString pt=trn.plainType(t);
+                               code+="\tfor(int i=0;i<in_"+sl[i]+".size();i++){\n";
+                               if(trn.isObjectType(t)){
+                                       code+="\t\ttmp=in_"+sl[i]+"[i].toXml(doc,\""+sl[i]+"\");\n";
+                               }else{
+                                       code+="\t\ttmp=doc.createElement(\""+sl[i]+"\");\n";
+                                       code+="\t\ttmp.appendChild(doc.createTextNode(";
+                                       if(trn.isIntType(t))
+                                               code+="QString::number(in_"+sl[i]+"[i])";
+                                       else
+                                       if(trn.isBoolType(t))
+                                               code+="in_"+sl[i]+"[i]?\"yes\":\"no\"";
+                                       else
+                                       if(trn.isBlobType(t))
+                                               code+="in_"+sl[i]+".toBase64()";
+                                       else
+                                               code+="in_"+sl[i]+"[i]";
+                                       code+="));\n";
+                               }
+                               code+="\t\troot.appendChild(tmp);\n";
+                               code+="\t}\n";
+                       }else{
+                               if(trn.isObjectType(t)){
+                                       code+="\troot.appendChild(in_"+sl[i]+".toXml(doc,\""+sl[i]+"\"));\n";
+                               }else{
+                                       code+="\ttmp=doc.createElement(\""+sl[i]+"\");\n";
+                                       code+="\ttmp.appendChild(doc.createTextNode(";
+                                       if(trn.isIntType(t))
+                                               code+="QString::number(in_"+sl[i]+")";
+                                       else
+                                       if(trn.isBlobType(t))
+                                               code+="in_"+sl[i]+".toBase64()";
+                                       else
+                                               code+="in_"+sl[i];
+                                       code+="));\n\troot.appendChild(tmp);\n";
+                               }
+                       }
+               }
+       }
+       code+="\t/*end of input encoding*/\n";
+       return code;
+}
+
+QString WocQtClientOut::trnOutput(const WocTransaction&trn)
+{
+       QStringList sl=trn.outputNames();
+       QString code="\t/*start of output decoding*/\n";
+       //basic XML parsing
+       code+="\tif(rba.isEmpty())return;\n";
+       code+="\tdoc=QDomDocument();\n";
+       code+="\tQString emsg;int eln,ecl;\n";
+       code+="\tif(!doc.setContent(rba,&emsg,&eln,&ecl)){\n";
+       code+="\t\tm_stage=Error;m_errtype=\"_iface\";m_errstr=QString(QCoreApplication::translate(\"WobTransaction\",\"XML result parser error line %1 col %2: %3\")).arg(eln).arg(ecl).arg(emsg);\n\t\tlog.setError(m_errstr);\n\t\treturn;\n\t}\n";
+       code+="\troot=doc.documentElement();\n";
+       //decide where to go, error handling
+       code+="\tif(m_wobstatus!=\"ok\"){\n\t\tm_stage=Error;m_errtype=\"_server\";m_errstr=\"unknown server error\";\n";
+       code+="\t\tQList<QDomElement> nl=elementsByTagName(root,\"Error\");\n";
+       code+="\t\tif(nl.size()==0){\n\t\t\tlog.setError(m_errstr);\n\t\t\treturn;\n\t\t}\n";
+       code+="\t\ttmp=nl.at(0).toElement();\n";
+       code+="\t\tm_errtype=tmp.attribute(\"type\",\"_server\");\n";
+       code+="\t\tm_errstr=tmp.text();\n\t\tlog.setError(m_errstr);\n\t\treturn;\n\t}\n";
+       code+="\tQList<QDomElement> nl;\n";
+       //parse parameters
+       for(int i=0;i<sl.size();i++){
+               QString t=trn.outputType(sl[i]);
+               if(trn.isAttributeType(t)){
+                       code+="\tout_"+sl[i]+"=root.attribute(\""+sl[i]+"\")";
+                       if(trn.isIntType(t))code+=".toInt()";else
+                       if(trn.isBoolType(t))code+="==\"yes\"";
+                       code+=";\n";
+               }else{
+                       code+="\tnl=elementsByTagName(root,\""+sl[i]+"\");\n";
+                       if(trn.isListType(t)){
+                               code+="\tfor(int i=0;i<nl.size();i++){\n";
+                               if(trn.isObjectType(t)){
+                                       code+="\t\ttry{out_"+sl[i]+".append("+qtobjtype(trn,sl[i],Out)+"(nl.at(i).toElement()));}catch(WException e){m_stage=Error;m_errtype=e.component();m_errstr=e.error();log.setError(m_errstr);}\n";
+                               }else if(trn.isIntType(t)){
+                                       code+="\t\tout_"+sl[i]+".append(nl.at(i).toElement().text().toInt());\n";
+                               }else if(trn.isBoolType(t)){
+                                       code+="\t\tout_"+sl[i]+".append(nl.at(i).toElement().text()==\"yes\");\n";
+                               }else if(trn.isBlobType(t)){
+                                       code+="\t\tout_"+sl[i]+".append(QByteArray::fromBase64(nl.at(i).toElement().text().toAscii()));\n";
+                               }else{//can only be string
+                                       code+="\t\tout_"+sl[i]+".append(nl.at(i).toElement().text());\n";
+                               }
+                               code+="\t}\n";
+                       }else{
+                               code+="\tif(nl.size()>0){\n";
+                               if(trn.isObjectType(t)){
+                                       code+="\t\ttry{out_"+sl[i]+"="+qtobjtype(trn,sl[i],Out)+"(nl.at(0).toElement());}catch(WException e){m_stage=Error;m_errtype=e.component();m_errstr=e.error();log.setError(m_errstr);}\n";
+                               }else if(trn.isBlobType(t)){
+                                       code+="\t\tout_"+sl[i]+"=QByteArray::fromBase64(nl.at(0).toElement().text().toAscii());\n";
+                               }else{//can only be string
+                                       code+="\t\tout_"+sl[i]+"=nl.at(0).toElement().text();\n";
+                               }
+                               code+="\t}\n";
+                       }
+               }
+       }
+       code+="\t/*end of output*/\n";
+       return code;
+}
+
+void WocQtClientOut::trnList()
+{
+       QString code;
+       //header
+       code+="  enum Right {\n    NoRight";
+       QStringList r=WocProcessor::instance()->transactionNames();
+       QStringList p=WocProcessor::instance()->privilegeNames();
+       QStringList pp=p;
+       for(int i=0;i<r.size();i++)
+               code+=",\n    R"+r[i];
+       for(int i=0;i<p.size();i++)
+               code+=",\n    P"+pp[i].replace(':',"_");
+       code+="\n  };\n";
+       code+="  static QString rightToString(Right);\n";
+       code+="  static QString rightToLocalString(Right);\n";
+       code+="  static Right stringToRight(QString);\n";
+       code+="  static QStringList allKnownRightsString();\n";
+       code+="  static QList<Right> allKnownRights();\n";
+       m_iface.write(code.toAscii());
+       
+       code="QString "+m_prefix+"Interface::rightToString(Right r)\n{\n\tswitch(r){\n";
+       for(int i=0;i<r.size();i++)
+               code+="\t\tcase R"+r[i]+":return \""+r[i]+"\";\n";
+       for(int i=0;i<p.size();i++)
+               code+="\t\tcase P"+pp[i]+":return \""+p[i]+"\";\n";
+       code+="\t\tdefault:return \"\";\n\t}\n}\n";
+       code+="QString "+m_prefix+"Interface::rightToLocalString(Right r)\n{\n\tswitch(r){\n";
+       for(int i=0;i<r.size();i++)
+               code+="\t\tcase R"+r[i]+":return tr(\""+r[i]+"\");\n";
+       for(int i=0;i<p.size();i++)
+               code+="\t\tcase P"+pp[i]+":return tr(\""+p[i]+"\");\n";
+       code+="\t\tdefault:return \"\";\n\t}\n}\n";
+       code+=m_prefix+"Interface::Right "+m_prefix+"Interface::stringToRight(QString s)\n{\n";
+       for(int i=0;i<r.size();i++)
+               code+="\tif(s==\""+r[i]+"\")return R"+r[i]+";else\n";
+       for(int i=0;i<p.size();i++)
+               code+="\tif(s==\""+p[i]+"\")return P"+pp[i]+";else\n";
+       code+="\treturn NoRight;\n}\n";
+       code+="QList<"+m_prefix+"Interface::Right> "+m_prefix+"Interface::allKnownRights()\n{\n";
+       code+="\tQList<Right> ret;ret";
+       for(int i=0;i<r.size();i++)
+               code+="<<R"+r[i];
+       for(int i=0;i<p.size();i++)
+               code+="<<P"+pp[i];
+       code+=";\n\treturn ret;\n}\n";
+       code+="QStringList "+m_prefix+"Interface::allKnownRightsString()\n{\n";
+       code+="\tQStringList ret;ret";
+       for(int i=0;i<r.size();i++)
+               code+="<<\""+r[i]+"\"";
+       for(int i=0;i<p.size();i++)
+               code+="<<\""+p[i]+"\"";
+       code+=";\n\treturn ret;\n}\n";
+       m_ifacecpp.write(code.toAscii());
+}
+
+QString WocQtClientOut::qttype(const WocTransaction&trn,QString v,InOut io)
+{
+       QString tp=io==In?trn.inputType(v):trn.outputType(v);
+       QString r,e;
+       if(tp.startsWith("List:")){
+               r="QList<";e=">";
+               tp=tp.mid(5);
+       }else
+               if(io==Out){
+                       r="Nullable<";
+                       e=">";
+               }
+       if(tp=="astring" || tp=="string")r+="QString";else
+       if(tp=="int"||tp=="int32"||tp=="int64")r+="qint64";else
+       if(tp=="blob")r+="QByteArray";else
+       if(tp=="bool")r+="bool";else
+       if(tp==""){
+               qDebug("Warning: the final type of property %s is empty!",v.toAscii().data());
+               r+="void";
+       }else r+=m_prefix+"O"+tp.split("/",QString::SkipEmptyParts).at(0);
+       r+=e;r+=" ";
+       return r;
+}
+
+QString WocQtClientOut::qtobjtype(const WocTransaction&trn,QString v,InOut io)
+{
+       QString tp=io==In?trn.inputType(v):trn.outputType(v);
+       if(tp.startsWith("List:"))
+               tp=tp.mid(5);
+       if(tp=="astring" || tp=="string" || tp=="int" || tp=="int32" || tp=="int64" || tp=="bool" || tp=="blob")
+               return "";
+       else
+               return m_prefix+"O"+tp;
+}
+
+void WocQtClientOut::addFile(QString bn)
+{
+       QString code="HEADERS+="+m_subdir+"/"+bn+".h\nSOURCES+="+m_subdir+"/"+bn+".cpp\n";
+       m_pri.write(code.toAscii());
+       m_hdr.write(QByteArray("#include \"%.h\"\n").replace('%',bn.toAscii()));
+}
\ No newline at end of file
diff --git a/woc/qtout.h b/woc/qtout.h
new file mode 100644 (file)
index 0000000..281fce6
--- /dev/null
@@ -0,0 +1,64 @@
+//
+// C++ Interface: qtout
+//
+// Description: 
+//
+//
+// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2009
+//
+// Copyright: See README/COPYING files that come with this distribution
+//
+//
+
+#ifndef WOC_QTOUT_H
+#define WOC_QTOUT_H
+
+#include "processor.h"
+
+#include "mfile.h"
+
+class QDomElement;
+
+class WocQtClientOut:public WocOutput
+{
+       public:
+               WocQtClientOut(QDomElement&);
+               ~WocQtClientOut();
+       protected:
+               virtual void finalize();
+               virtual void newClass(const WocClass&);
+               virtual void newTable(const WocTable&);
+               virtual void newTransaction(const WocTransaction&);
+       private:
+               QString m_basedir,m_subdir,m_prefix;
+               MFile m_pri,m_iface,m_ifacecpp,m_hdr;
+               bool m_clean;
+               
+               /**helper: adds a file to the project file*/
+               void addFile(QString basename);
+               /**helper: generate enums for classes*/
+               void classEnums(const WocClass&,MFile&,MFile&,QString);
+               /**helper: generate properties*/
+               void classProperties(const WocClass&,MFile&,MFile&);
+               /**helper: generate constructors/deserializer/copiers*/
+               void classDeserializer(const WocClass&,MFile&,MFile&,QString);
+               /**helper: generate serializers*/
+               void classSerializers(const WocClass&,MFile&,MFile&,QString);
+               /**helper: generate a proper Qt type for a property*/
+               QString qttype(const WocClass&,QString,bool dolist=true);
+               
+               enum InOut{In,Out};
+               /**helper: generate a proper QT type for a transaction variable*/
+               QString qttype(const WocTransaction&,QString,InOut);
+               /**helper: generate a proper QT type for a transaction variable, WO* only */
+               QString qtobjtype(const WocTransaction&,QString,InOut);
+               
+               /**helper generates the transaction input encoding*/
+               QString trnInput(const WocTransaction&);
+               /**helper generates the transaction output decoding*/
+               QString trnOutput(const WocTransaction&);
+               /**helper generates enums and strings for all transactions*/
+               void trnList();
+};
+
+#endif
diff --git a/woc/woc.cpp b/woc/woc.cpp
new file mode 100644 (file)
index 0000000..05b4dd8
--- /dev/null
@@ -0,0 +1,36 @@
+//
+// C++ Implementation: woc
+//
+// Description: 
+//
+//
+// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2009
+//
+// Copyright: See README/COPYING files that come with this distribution
+//
+//
+
+#include <QCoreApplication>
+#include <QStringList>
+
+#include "processor.h"
+
+int main(int argc,char**argv)
+{
+       QCoreApplication app(argc,argv);
+       //get arguments
+       QStringList args=app.arguments();
+       args.removeFirst();
+       //process files
+       WocProcessor proc;
+       for(int i=0;i<args.size();i++)
+               if(!proc.processFile(args[i])){
+                       qDebug("aborting scan.");
+                       return 1;
+               }
+       //call finalizer
+       proc.finalize();
+       qDebug("done.");
+       //return success
+       return 0;
+}
\ No newline at end of file
diff --git a/woc/woc.pro b/woc/woc.pro
new file mode 100644 (file)
index 0000000..e04bdbd
--- /dev/null
@@ -0,0 +1,30 @@
+TEMPLATE=app
+TARGET=../woc/woc
+QT-=gui
+QT+=xml
+CONFIG+=console
+
+#compilation output:
+DESTDIR = ../woc
+OBJECTS_DIR = .ctmp
+MOC_DIR = .ctmp
+RCC_DIR = .ctmp
+
+
+SOURCES+= \
+       processor.cpp \
+       woc.cpp \
+       qtout.cpp \
+       phpout.cpp \
+       htmlout.cpp \
+       mfile.cpp \
+       ../src/misc/domquery.cpp
+HEADERS+= \
+       processor.h \
+       phpout.h \
+       qtout.h \
+       htmlout.h \
+       mfile.h \
+       ../src/misc/domquery.h
+
+INCLUDEPATH += ../src/misc
\ No newline at end of file