renamed folders and files, preparing the code structure to be updated to the latest version of QtWebApp

This commit is contained in:
Luis Ángel San Martín
2016-06-16 19:46:58 +02:00
parent edbb642cee
commit e6d229680f
42 changed files with 85 additions and 3 deletions

View File

@ -0,0 +1,188 @@
/**
@file
@author Stefan Frings
*/
#include "template.h"
#include <QFileInfo>
Template::Template(QString source, QString sourceName)
: QString(source) {
this->sourceName=sourceName;
this->warnings=false;
}
Template::Template(QFile& file, QTextCodec* textCodec) {
this->warnings=false;
sourceName=QFileInfo(file.fileName()).baseName();
if (!file.isOpen()) {
file.open(QFile::ReadOnly | QFile::Text);
}
QByteArray data=file.readAll();
file.close();
if (data.size()==0 || file.error()) {
qCritical("Template: cannot read from %s, %s",qPrintable(sourceName),qPrintable(file.errorString()));
append(textCodec->toUnicode(data));
}
}
int Template::setVariable(QString name, QString value) {
int count=0;
QString variable="{"+name+"}";
int start=indexOf(variable);
while (start>=0) {
replace(start, variable.length(), value);
count++;
start=indexOf(variable,start+value.length());
}
if (count==0 && warnings) {
qWarning("Template: missing variable %s in %s",qPrintable(variable),qPrintable(sourceName));
}
return count;
}
int Template::setCondition(QString name, bool value) {
int count=0;
QString startTag=QString("{if %1}").arg(name);
QString elseTag=QString("{else %1}").arg(name);
QString endTag=QString("{end %1}").arg(name);
// search for if-else-end
int start=indexOf(startTag);
while (start>=0) {
int end=indexOf(endTag,start+startTag.length());
if (end>=0) {
count++;
int ellse=indexOf(elseTag,start+startTag.length());
if (ellse>start && ellse<end) { // there is an else part
if (value==true) {
QString truePart=mid(start+startTag.length(), ellse-start-startTag.length());
replace(start, end-start+endTag.length(), truePart);
}
else { // value==false
QString falsePart=mid(ellse+elseTag.length(), end-ellse-elseTag.length());
replace(start, end-start+endTag.length(), falsePart);
}
}
else if (value==true) { // and no else part
QString truePart=mid(start+startTag.length(), end-start-startTag.length());
replace(start, end-start+endTag.length(), truePart);
}
else { // value==false and no else part
replace(start, end-start+endTag.length(), "");
}
start=indexOf(startTag,start);
}
else {
qWarning("Template: missing condition end %s in %s",qPrintable(endTag),qPrintable(sourceName));
}
}
// search for ifnot-else-end
QString startTag2="{ifnot "+name+"}";
start=indexOf(startTag2);
while (start>=0) {
int end=indexOf(endTag,start+startTag2.length());
if (end>=0) {
count++;
int ellse=indexOf(elseTag,start+startTag2.length());
if (ellse>start && ellse<end) { // there is an else part
if (value==false) {
QString falsePart=mid(start+startTag2.length(), ellse-start-startTag2.length());
replace(start, end-start+endTag.length(), falsePart);
}
else { // value==true
QString truePart=mid(ellse+elseTag.length(), end-ellse-elseTag.length());
replace(start, end-start+endTag.length(), truePart);
}
}
else if (value==false) { // and no else part
QString falsePart=mid(start+startTag2.length(), end-start-startTag2.length());
replace(start, end-start+endTag.length(), falsePart);
}
else { // value==true and no else part
replace(start, end-start+endTag.length(), "");
}
start=indexOf(startTag2,start);
}
else {
qWarning("Template: missing condition end %s in %s",qPrintable(endTag),qPrintable(sourceName));
}
}
if (count==0 && warnings) {
qWarning("Template: missing condition %s or %s in %s",qPrintable(startTag),qPrintable(startTag2),qPrintable(sourceName));
}
return count;
}
int Template::loop(QString name, int repetitions) {
//Q_ASSERT(repetitions>=0);
int count=0;
QString startTag="{loop "+name+"}";
QString elseTag="{else "+name+"}";
QString endTag="{end "+name+"}";
// search for loop-else-end
int start=indexOf(startTag);
while (start>=0) {
int end=indexOf(endTag,start+startTag.length());
if (end>=0) {
count++;
int ellse=indexOf(elseTag,start+startTag.length());
if (ellse>start && ellse<end) { // there is an else part
if (repetitions>0) {
QString loopPart=mid(start+startTag.length(), ellse-start-startTag.length());
QString insertMe;
for (int i=0; i<repetitions; ++i) {
// number variables, conditions and sub-loop within the loop
QString nameNum=name+QString::number(i);
QString s=loopPart;
s.replace(QString("{%1.").arg(name), QString("{%1.").arg(nameNum));
s.replace(QString("{if %1.").arg(name), QString("{if %1.").arg(nameNum));
s.replace(QString("{ifnot %1.").arg(name), QString("{ifnot %1.").arg(nameNum));
s.replace(QString("{else %1.").arg(name), QString("{else %1.").arg(nameNum));
s.replace(QString("{end %1.").arg(name), QString("{end %1.").arg(nameNum));
s.replace(QString("{loop %1.").arg(name), QString("{loop %1.").arg(nameNum));
insertMe.append(s);
}
replace(start, end-start+endTag.length(), insertMe);
}
else { // repetitions==0
QString elsePart=mid(ellse+elseTag.length(), end-ellse-elseTag.length());
replace(start, end-start+endTag.length(), elsePart);
}
}
else if (repetitions>0) { // and no else part
QString loopPart=mid(start+startTag.length(), end-start-startTag.length());
QString insertMe;
for (int i=0; i<repetitions; ++i) {
// number variables, conditions and sub-loop within the loop
QString nameNum=name+QString::number(i);
QString s=loopPart;
s.replace(QString("{%1.").arg(name), QString("{%1.").arg(nameNum));
s.replace(QString("{if %1.").arg(name), QString("{if %1.").arg(nameNum));
s.replace(QString("{ifnot %1.").arg(name), QString("{ifnot %1.").arg(nameNum));
s.replace(QString("{else %1.").arg(name), QString("{else %1.").arg(nameNum));
s.replace(QString("{end %1.").arg(name), QString("{end %1.").arg(nameNum));
s.replace(QString("{loop %1.").arg(name), QString("{loop %1.").arg(nameNum));
insertMe.append(s);
}
replace(start, end-start+endTag.length(), insertMe);
}
else { // repetitions==0 and no else part
replace(start, end-start+endTag.length(), "");
}
start=indexOf(startTag,start);
}
else {
qWarning("Template: missing loop end %s in %s",qPrintable(endTag),qPrintable(sourceName));
}
}
if (count==0 && warnings) {
qWarning("Template: missing loop %s in %s",qPrintable(startTag),qPrintable(sourceName));
}
return count;
}
void Template::enableWarnings(bool enable) {
warnings=enable;
}

View File

@ -0,0 +1,167 @@
/**
@file
@author Stefan Frings
*/
#ifndef TEMPLATE_H
#define TEMPLATE_H
#include <QString>
#include <QRegExp>
#include <QIODevice>
#include <QTextCodec>
#include <QFile>
#include <QString>
/**
Enhanced version of QString for template processing. Templates
are usually loaded from files, but may also be loaded from
prepared Strings.
Example template file:
<p><code><pre>
Hello {username}, how are you?
{if locked}
Your account is locked.
{else locked}
Welcome on our system.
{end locked}
The following users are on-line:
Username Time
{loop user}
{user.name} {user.time}
{end user}
</pre></code></p>
<p>
Example code to fill this template:
<p><code><pre>
Template t(QFile("test.tpl"),QTextCode::codecForName("UTF-8"));
t.setVariable("user", "Stefan");
t.setCondition("locked",false);
t.loop("user",2);
t.setVariable("user0.name,"Markus");
t.setVariable("user0.time,"8:30");
t.setVariable("user1.name,"Roland");
t.setVariable("user1.time,"8:45");
</pre></code></p>
<p>
The code example above shows how variable within loops are numbered.
Counting starts with 0. Loops can be nested, for example:
<p><code><pre>
&lt;table&gt;
{loop row}
&lt;tr&gt;
{loop row.column}
&lt;td&gt;{row.column.value}&lt;/td&gt;
{end row.column}
&lt;/tr&gt;
{end row}
&lt;/table&gt;
</pre></code></p>
<p>
Example code to fill this nested loop with 3 rows and 4 columns:
<p><code><pre>
t.loop("row",3);
t.loop("row0.column",4);
t.setVariable("row0.column0.value","a");
t.setVariable("row0.column1.value","b");
t.setVariable("row0.column2.value","c");
t.setVariable("row0.column3.value","d");
t.loop("row1.column",4);
t.setVariable("row1.column0.value","e");
t.setVariable("row1.column1.value","f");
t.setVariable("row1.column2.value","g");
t.setVariable("row1.column3.value","h");
t.loop("row2.column",4);
t.setVariable("row2.column0.value","i");
t.setVariable("row2.column1.value","j");
t.setVariable("row2.column2.value","k");
t.setVariable("row2.column3.value","l");
</pre></code></p>
@see TemplateLoader
@see TemplateCache
*/
class Template : public QString {
public:
/**
Constructor that reads the template from a string.
@param source The template source text
@param sourceName Name of the source file, used for logging
*/
Template(QString source, QString sourceName);
/**
Constructor that reads the template from a file. Note that this class does not
cache template files by itself, so using this constructor is only recommended
to be used on local filesystem.
@param file File that provides the source text
@param textCodec Encoding of the source
@see TemplateLoader
@see TemplateCache
*/
Template(QFile& file, QTextCodec* textCodec);
/**
Replace a variable by the given value.
Affects tags with the syntax
- {name}
After settings the
value of a variable, the variable does not exist anymore,
it it cannot be changed multiple times.
@param name name of the variable
@param value new value
@return The count of variables that have been processed
*/
int setVariable(QString name, QString value);
/**
Set a condition. This affects tags with the syntax
- {if name}...{end name}
- {if name}...{else name}...{end name}
- {ifnot name}...{end name}
- {ifnot name}...{else name}...{end name}
@param name Name of the condition
@param value Value of the condition
@return The count of conditions that have been processed
*/
int setCondition(QString name, bool value);
/**
Set number of repetitions of a loop.
This affects tags with the syntax
- {loop name}...{end name}
- {loop name}...{else name}...{end name}
@param name Name of the loop
@param repetitions The number of repetitions
@return The number of loops that have been processed
*/
int loop(QString name, int repetitions);
/**
Enable warnings for missing tags
@param enable Warnings are enabled, if true
*/
void enableWarnings(bool enable=true);
private:
/** Name of the source file */
QString sourceName;
/** Enables warnings, if true */
bool warnings;
};
#endif // TEMPLATE_H

View File

@ -0,0 +1,30 @@
#include "templatecache.h"
#include <QDateTime>
#include <QStringList>
#include <QSet>
TemplateCache::TemplateCache(QSettings* settings, QObject* parent)
:TemplateLoader(settings,parent)
{
cache.setMaxCost(settings->value("cacheSize","160000").toInt());//este tama<6D>o antes era 1000000
cacheTimeout=settings->value("cacheTime","60000").toInt();
qDebug("TemplateCache: timeout=%i, size=%i",cacheTimeout,cache.maxCost());
}
QString TemplateCache::tryFile(QString localizedName) {
qint64 now=QDateTime::currentMSecsSinceEpoch();
// search in cache
qDebug("TemplateCache: trying cached %s",qPrintable(localizedName));
CacheEntry* entry=cache.object(localizedName);
if (entry && (cacheTimeout==0 || entry->created>now-cacheTimeout)) {
return entry->document;
}
// search on filesystem
entry=new CacheEntry();
entry->created=now;
entry->document=TemplateLoader::tryFile(localizedName);
// Store in cache even when the file did not exist, to remember that there is no such file
cache.insert(localizedName,entry,entry->document.size());
return entry->document;
}

View File

@ -0,0 +1,77 @@
#ifndef TEMPLATECACHE_H
#define TEMPLATECACHE_H
#include "templateloader.h"
#include <QCache>
/**
Caching template loader, reduces the amount of I/O and improves performance
on remote file systems. The cache has a limited size, it prefers to keep
the last recently used files. Optionally, the maximum time of cached entries
can be defined to enforce a reload of the template file after a while.
<p>
In case of local file system, the use of this cache is optionally, since
the operating system caches files already.
<p>
Loads localized versions of template files. If the caller requests a file with the
name "index" and the suffix is ".tpl" and the requested locale is "de_DE, de, en-US",
then files are searched in the following order:
- index-de_DE.tpl
- index-de.tpl
- index-en_US.tpl
- index-en.tpl
- index.tpl
<p>
The following settings are required:
<code><pre>
path=.
suffix=.tpl
encoding=UTF-8
cacheSize=1000000
cacheTime=60000
</pre></code>
The path is relative to the directory of the config file. In case of windows, if the
settings are in the registry, the path is relative to the current working directory.
<p>
Files are cached as long as possible, when cacheTime=0.
@see TemplateLoader
*/
class TemplateCache : public TemplateLoader {
Q_OBJECT
Q_DISABLE_COPY(TemplateCache);
public:
/**
Constructor.
@param settings configurations settings
@param parent Parent object
*/
TemplateCache(QSettings* settings, QObject* parent=0);
protected:
/**
Try to get a file from cache or filesystem.
@param localizedName Name of the template with locale to find
@return The template document, or empty string if not found
*/
virtual QString tryFile(QString localizedName);
private:
struct CacheEntry {
QString document;
qint64 created;
};
/** Timeout for each cached file */
int cacheTimeout;
/** Cache storage */
QCache<QString,CacheEntry> cache;
};
#endif // TEMPLATECACHE_H

View File

@ -0,0 +1,7 @@
INCLUDEPATH += $$PWD
DEPENDPATH += $$PWD
HEADERS += $$PWD/template.h $$PWD/templateloader.h $$PWD/templatecache.h
SOURCES += $$PWD/template.cpp $$PWD/templateloader.cpp $$PWD/templatecache.cpp
OTHER_FILES += $$PWD/../doc/readme.txt

View File

@ -0,0 +1,24 @@
/**
@file
@author Stefan Frings
*/
#ifndef TEMPLATEGLOBAL_H
#define TEMPLATEGLOBAL_H
#include <QtGlobal>
// This is specific to Windows dll's
#if defined(Q_OS_WIN)
#if defined(QTWEBAPPLIB_EXPORT)
#define DECLSPEC Q_DECL_EXPORT
#elif defined(QTWEBAPPLIB_IMPORT)
#define DECLSPEC Q_DECL_IMPORT
#endif
#endif
#if !defined(DECLSPEC)
#define DECLSPEC
#endif
#endif // TEMPLATEGLOBAL_H

View File

@ -0,0 +1,109 @@
/**
@file
@author Stefan Frings
*/
#include "templateloader.h"
#include <QFile>
#include <QFileInfo>
#include <QStringList>
#include <QDir>
#include <QSet>
#include <QCoreApplication>
TemplateLoader::TemplateLoader(QSettings* settings, QObject* parent)
: QObject(parent)
{
templatePath=settings->value("path","./server/templates").toString();
// Convert relative path to absolute, based on the directory of the config file.
#ifdef Q_OS_WIN32
if (QDir::isRelativePath(templatePath) && settings->format()!=QSettings::NativeFormat)
#else
if (QDir::isRelativePath(templatePath))
#endif
{
#if defined Q_OS_UNIX && !defined Q_OS_MAC
QFileInfo configFile(QString(DATADIR)+"/yacreader");
templatePath=QFileInfo(QString(DATADIR)+"/yacreader",templatePath).absoluteFilePath();
#else
QFileInfo configFile(QCoreApplication::applicationDirPath());
templatePath=QFileInfo(QCoreApplication::applicationDirPath(),templatePath).absoluteFilePath();
#endif
}
fileNameSuffix=settings->value("suffix",".tpl").toString();
QString encoding=settings->value("encoding").toString();
if (encoding.isEmpty()) {
textCodec=QTextCodec::codecForLocale();
}
else {
textCodec=QTextCodec::codecForName(encoding.toLocal8Bit());
}
qDebug("TemplateLoader: path=%s, codec=%s",qPrintable(templatePath),textCodec->name().data());
}
TemplateLoader::~TemplateLoader() {}
QString TemplateLoader::tryFile(QString localizedName) {
QString fileName=templatePath+"/"+localizedName+fileNameSuffix;
qDebug("TemplateCache: trying file %s",qPrintable(fileName));
QFile file(fileName);
if (file.exists()) {
file.open(QIODevice::ReadOnly);
QString document=textCodec->toUnicode(file.readAll());
file.close();
if (file.error()) {
qCritical("TemplateLoader: cannot load file %s, %s",qPrintable(fileName),qPrintable(file.errorString()));
return "";
}
else {
return document;
}
}
return "";
}
Template TemplateLoader::getTemplate(QString templateName, QString locales) {
mutex.lock();
QSet<QString> tried; // used to suppress duplicate attempts
QStringList locs=locales.split(',',QString::SkipEmptyParts);
// Search for exact match
foreach (QString loc,locs) {
loc.replace(QRegExp(";.*"),"");
loc.replace('-','_');
QString localizedName=templateName+"-"+loc.trimmed();
if (!tried.contains(localizedName)) {
QString document=tryFile(localizedName);
if (!document.isEmpty()) {
mutex.unlock();
return Template(document,localizedName);
}
tried.insert(localizedName);
}
}
// Search for correct language but any country
foreach (QString loc,locs) {
loc.replace(QRegExp("[;_-].*"),"");
QString localizedName=templateName+"-"+loc.trimmed();
if (!tried.contains(localizedName)) {
QString document=tryFile(localizedName);
if (!document.isEmpty()) {
mutex.unlock();
return Template(document,localizedName);
}
tried.insert(localizedName);
}
}
// Search for default file
QString document=tryFile(templateName);
if (!document.isEmpty()) {
mutex.unlock();
return Template(document,templateName);
}
qCritical("TemplateCache: cannot find template %s",qPrintable(templateName));
mutex.unlock();
return Template("",templateName);
}

View File

@ -0,0 +1,85 @@
/**
@file
@author Stefan Frings
*/
#ifndef TEMPLATELOADER_H
#define TEMPLATELOADER_H
#include <QString>
#include <QSettings>
#include <QTextCodec>
#include "template.h"
#include <QMutex>
/**
Loads localized versions of template files. If the caller requests a file with the
name "index" and the suffix is ".tpl" and the requested locale is "de_DE, de, en-US",
then files are searched in the following order:
- index-de_DE.tpl
- index-de.tpl
- index-en_US.tpl
- index-en.tpl
- index.tpl
The following settings are required:
<code><pre>
path=.
suffix=.tpl
encoding=UTF-8
</pre></code>
The path is relative to the directory of the config file. In case of windows, if the
settings are in the registry, the path is relative to the current working directory.
@see TemplateCache
*/
class TemplateLoader : public QObject {
Q_OBJECT
Q_DISABLE_COPY(TemplateLoader);
public:
/**
Constructor.
@param settings configurations settings
@param parent parent object
*/
TemplateLoader(QSettings* settings, QObject* parent=0);
/** Destructor */
virtual ~TemplateLoader();
/**
Get a template for a given locale.
This method is thread safe.
@param templateName base name of the template file, without suffix and without locale
@param locales Requested locale(s), e.g. "de_DE, en_EN". Strings in the format of
the HTTP header Accept-Locale may be used. Badly formatted parts in the string are silently
ignored.
@return If the template cannot be loaded, an error message is logged and an empty template is returned.
*/
Template getTemplate(QString templateName, QString locales=QString());
protected:
/**
Try to get a file from cache or filesystem.
@param localizedName Name of the template with locale to find
@return The template document, or empty string if not found
*/
virtual QString tryFile(QString localizedName);
/** Directory where the templates are searched */
QString templatePath;
/** Suffix to the filenames */
QString fileNameSuffix;
/** Codec for decoding the files */
QTextCodec* textCodec;
/** Used to synchronize threads */
QMutex mutex;
};
#endif // TEMPLATELOADER_H