From: Konrad Rosenbaum Date: Sun, 11 Feb 2018 22:53:21 +0000 (+0100) Subject: backend for generic output, X-Git-Url: http://git.silmor.de/gitweb/?a=commitdiff_plain;h=refs%2Fheads%2Fmaster;p=web%2Fkonrad%2Fpack.git backend for generic output, draft for qt and html patterns Change-Id: I8d73c6918987aaa4bb172bd8b80da31c18de98fe --- diff --git a/.gitignore b/.gitignore index 9ca99a4..c731e7f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,4 @@ *~ -woc/woc -woc/prewoc *.exe *.o *.obj @@ -12,12 +10,16 @@ lib*.so* vinfo .ctmp .ptmp +*.tag +object_script* +.qmake.stash qtbase/Makefile* +woc/woc +woc/prewoc woc/Makefile* +woc/pattern.qrc doc/*base doc/woc -*.tag -object_script* cgi2scgi/cgi2scgi cgi2scgi/cgi2scgi.exe examples/clock/html/ @@ -27,4 +29,9 @@ examples/clock/qtc/clock examples/clock/qts/clockserv examples/clock/qtc/clock.exe examples/clock/qts/clockserv.exe -.qmake.stash +woc/qrcgen/Makefile +woc/qrcgen/qrcgen +woc/test/woctest_* +woc/test/Makefile* +woc/test/*/Makefile* +woc/test/generic/testfiles.qrc diff --git a/Makefile b/Makefile index f4ab98c..2cf45be 100644 --- a/Makefile +++ b/Makefile @@ -23,12 +23,15 @@ all: prewoc woc qtbase doc .PHONY: prewoc woc qtbase doc clean install install-woc install-qtbase install-phpbase install-doc -vinfo/staticVersion.h prewoc: +qrcgen: + cd woc/qrcgen && $(QMAKE) && $(MAKE) + +vinfo/staticVersion.h prewoc: qrcgen cd woc && $(QMAKE) -o Makefile.prewoc CONFIG+=prewoc $(MAKE) -C woc -f Makefile.prewoc cd woc && $(abspath woc/prewoc) :/version.wolf -woc: vinfo/staticVersion.h +woc: vinfo/staticVersion.h qrcgen cd woc && $(QMAKE) CONFIG+=woc $(MAKE) -C woc diff --git a/README b/README index 4084211..4fe8c1a 100644 --- a/README +++ b/README @@ -2,9 +2,9 @@ README for PACK ================= PACK = Persistence And Communication Kit -version 0.7 +version 1.1 -(c) Konrad Rosenbaum, 2009-2013 +(c) Konrad Rosenbaum, 2009-2018 Directory woc: The C++-source files of woc (Web Object Compiler) are licensed under GPLv3 or @@ -12,13 +12,13 @@ at your option any newer version of the license. See COPYING.GPL for details. The SOAP target of woc uses some schema files that are under different copyright, see those files (woc/soap/*.xsd) for details. -Directories qtbase, phpbase: -The C++-source files of the Qt base library are licensed under LGPLv3 or at +Directories *base: +The C++-source files of the base libraries are licensed under LGPLv3 or at your option any newer version of the license. See COPYING.LGPL for details. AGPL option: Since you are free to link this code with code under the AGPL, I encourage its -use for server side code. See COPYING.AGPL for details. +use for server side code, but I do not require it. See COPYING.AGPL for details. Directory examples: The example code is in the public domain - you are free to use it however you diff --git a/doc/index.html b/doc/index.html index 67774ce..773b7f1 100644 --- a/doc/index.html +++ b/doc/index.html @@ -14,7 +14,7 @@ The following languages are currently supported:
Server -C++/Qt 4.x +C++/Qt 5.x yes partial (no tables) @@ -40,14 +40,15 @@ The following languages are currently supported:

Building PACK

-For compiling PACK you need a C++ compiler (eg. GCC) and Qt4.x. -The meta-compiler WOC and the Qt binding require at least Qt 4.7 or 5.x. +For compiling PACK you need a C++ compiler (eg. GCC) and Qt5.x. +The meta-compiler WOC and the Qt binding require at least Qt 5.6. For compiling and running generated code it all depends on your target:
- - + +
C++/QtA C++ compiler and at least Qt 4.7 or 5.x, modules: Core, Network, XML, optionally XMLpatterns
PHPPHP, at least version 5.2
C++/QtA C++-11 compiler and at least Qt 5.6 LTS(*), modules: Core, Network, XML, optionally XMLpatterns
PHPPHP, at least version 5.6(*)
SOAPany compatible toolkit that can handle WSDL files

+(*) earlier versions may work, but are untested

Compiling the meta-compiler is relatively easy: just enter the woc directory and call qmake and make (or open the woc.pro file in QtCreator and compile from there). The same approach works for PACK's Qt base library. The PHP library of PACK does not need to be compiled, just include it in your project. @@ -79,7 +80,8 @@ The Communication Interface generated by PACK contains two types of structures:

- \ No newline at end of file + diff --git a/doc/woc-pattern.html b/doc/woc-pattern.html new file mode 100644 index 0000000..9f898fc --- /dev/null +++ b/doc/woc-pattern.html @@ -0,0 +1,553 @@ + + + +Web Object Compiler - Patterns + + +

Extending WOC with New Patterns

+ +The Web Object Compiler (woc) can be extended with new patterns for new languages and targets.

+ +Each pattern description contains a meta.xml file that contains the main defintion of that pattern, it describes basic properties of the generated language and references pattern files that are used to build specific file types.

+ +Normally patterns are built-in, but they can be extended or overwritten by supplying one or multiple -pattern=/directory/to/patterns arguments to the WOC command line. +WOC will then search all subdirectories of the given directory for meta.xml files that contain pattern definitions.

+ +

Pattern Meta Data: meta.xml

+ +This is an example definition for a fictitious programming language "MyLang": +
+<?xml version="1.0" encoding="utf-8" ?>
+<Meta lang="mylang/client" tag="MyLangClientOutput">
+    
+    <Include file="../meta-mylang-common.xml" />
+    
+    <Syntax comment="##" section-start="#--" variable-marker="${ }$" />
+        
+    <Feature name="allClassPrefix" type="string" default="C" />
+
+    <File type="interface" tag="" name="src{interface.classname}.h" pattern="interface.my"/>
+    <File type="class" tag="definition" name="{class.classname}.my" pattern="class-definition.my"/>
+    <File type="project" tag="includeAll" name="includeAll.my" pattern="includeAll.my"/>
+
+    <Type config="string" map="MyString"/>
+    <Type config="astring" map="MyString"/>
+    <Type config="string:*" map="MyString"/>
+    <Type config="text" map="MyString"/>
+    <Type config="List:*" map="List&lt;*>"/>
+    <Type config="int" map="int"/>
+    <Type config="int32" map="int"/>
+    <Type config="int64" map="long"/>
+    
+    <Precalc trigger="interface.new" deleteOn="interface.close">
+        <Variable name="interface.classname" value="{allClassPrefix}Interface"/>
+    </Precalc>
+    <Precalc trigger="class.new" deleteOn="class.close">
+        <Variable name="class.classname" if="{class.abstract}" valueFalse="{allClassPrefix}{class.name}" valueTrue="{allClassPrefix}{class.name}Abstract"/>
+    </Precalc>
+    
+</Meta>
+
+ +This pattern could be instantiated in a WOLF file with this line: +
+  <MyLangClientOutput sourceDir="mysource" subDir="wob" allClassPrefix="MyPrf" clean="yes" />
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Tag/@AttributeDescription
Meta/...is the main element of a pattern definition; only files that have this document element are considered valid definitions
Meta/@langdefines the target language and feature separated by a slash; features are usually "client" or "server", but may be any token; this is matched against the lang attribute in WOLF files; this does not need to be unique among patterns
Meta/@tagthe XML tag used in WOLF files to instantiate and configure this pattern; if two or more pattern definitions contain the same tag name then the one loaded last overrides the earlier ones
Meta/Includeincludes another XML file as part of this meta file; it must follow the same syntax; since included files are executed before any other definition in the originating file, definitions in the including file override included definitions; the attributes of the Meta tag are completely ignored in included files
Meta/Include/@filespecifies the file name of the included file relative to this file's location
Meta/Syntax/...defines the syntax elements used in pattern files
Meta/Syntax/@commentComments that are discarded entirely from pattern files - lines started with this string do not end up in generated files, while comments following the target language syntax are generated. The default is "#WOC:". This could be an extension of the normal line comment marker or something different.
Meta/Syntax/@section-startLines starting with this string mark the start of a new section in a pattern file. The default is "#SECTION:". This could be an extension of the normal line comment marker or something different.
Meta/Syntax/@variable-markerSpace separated markers for the beginning and end of a variable reference, the default is { and }. In this example variables additionally are surrounded by $-signs as in ${variable}$. The strings should be chosen so that it is unlikely they occur naturally anywhere in the target language.
Meta/Feature/...Features are configuration options that can be overridden in WOLF files as attributes to the instantiation tag.
Meta/Feature/@nameThe name of the feature - this is also used as attribute name for the WOLF file and as variable name to reflect the actual value.
Meta/Feature/@typeEither "string" or "boolean".
Meta/Feature/@defaultThe default value for the feature if the attribute is not used.
Meta/File/...Defines a file pattern: what kind of file to generate, when to do it and which file describes the content.
Meta/File/@typeDefines when a file is created and closed. E.g. for type "class" the file is created on class.new and closed on class.close; valid types are any pairs of "*.new" and "*.close" triggers (e.g. class, interface, transaction.input).
Meta/File/@tagA distinguishing tag for the file definition. The combination of type and tag must be unique. Empty tags are allowed.
Meta/File/@nameAn expression to calculate the final file name of the generated file(s). Per default the name will be identical to that of the pattern file. If the trigger ("type".new) can occur multiple times then this pattern should contain variables to make the file names unique, otherwise later triggered files overwrite earlier files.
Meta/File/@patternThe pattern file describing the file content. The pattern file name is relative to the current XML file.
Meta/Type/...Maps data types from the generic WOLF types to language specific types.
Meta/Type/@configThe type used in a WOLF file for transactions or for database types.
Meta/Type/@mapThe language specific data type
Meta/Precalc/...Describes a set of pre-calculated variables. Pre-calculation can happen on any trigger.
Meta/Precalc/@triggerThe trigger that starts the pre-calculation. The calculation happens before any patterns are evaluated.
Meta/Precalc/@deleteOnOptional. If given: the variables are deleted again after this trigger.
Meta/Precalc/Variable/@nameThe variable to be calculated.
Meta/Precalc/Variable/@valueThe content of the variable, it may contain variable values/expressions itself. (Ignored if there is an "if" attribute.)
Meta/Precalc/Variable/@ifIf given: makes the value dependent on this boolean expression.
Meta/Precalc/Variable/@valueTrueContent of the variable if the expression in "if" evaluates to true. This may be an expression.
Meta/Precalc/Variable/@valueFalseContent of the variable if the expression in "if" evaluates to false. This may be an expression.
+ +

Value Expressions

+ +Whereever values are assigned (file names, feature defaults, value assignments to variables, boolean expressions or inside pattern files) those assignments may contain quoted variables and transformations on them. Inside the meta.xml file variables are always enclosed in { and }, which the configured syntax patterns are used in pattern files.

+ +For example if the variable "v1" contains the value "world" then the expression hello {v1}! evaluates to hello world!.

+ +A variable value can be transformed with any of the transformations listed in the reference below by simply using a "pipe expression". +Transformations can be concatenated to execute several of them consecutively. +
E.g. hello {v1|toUpper|toCString}! evaluates to hello "WORLD"!. + +

Boolean Expressions

+ +A single boolean value can be expressed as the following case insensitive values:

+ +Variables can be used to obtain boolean values simply by using the appropriate markers ({variable} in meta.xml, the configured marker in pattern files). +Complex transformations can be used on

+ +A value can be negated by prefixing it with an exclamation mark, e.g.: !{negatedVariable}

+ +Multiple boolean values can be strung together with && (boolean and) or || (boolean or) to form expressions. +Those expressions are always evaluated from left to right with no operator precedence. +Spaces are ignored in expressions. +
E.g.: true && {variable} || {othervariable}

+ +Grouping sub-expressions with parentheses is currently not supported. + +

Type Mappings

+ +The following types need to be mapped for each supported language: + + + + + + + + + + + + + + + + + + + + + + + + +
WOLF Type
config="..."
DB / ClassExample (Qt)
map="..."
Description
  simple class and transaction types
string DB / Class QString Strings stored in XML elements or medium long strings in DB: needs a type that can store several kB of character data.
astring Class QString Strings stored in XML attributes: needs a type that can store about 1kB of character data.
text DB / Class QString Very long text: needs a type that can store large amounts of character data.
int Class qint64 Generic integer: should be at least 32bits wide.
int32 DB / Class qint32 32bit integer
int64 DB / Class qint64 64bit integer
bool DB / Class bool Boolean value, needs a type that understands true/false.
  DB types
seq DB qint64 DB sequence, needs an integer type that is at least 32bits wide.
seq32 DB qint32 DB sequence, needs an integer type that is at least 32bits wide.
seq64 DB qint64 DB sequence, needs an integer type that is at least 64bits wide.
enum DB qint64 Enum value that is stored as integer, needs an integer type that is at least 32bits wide.
enum32 DB qint32 Enum value that is stored as integer, needs an integer type that is at least 32bits wide.
enum64 DB qint64 Enum value that is stored as integer, needs an integer type that is at least 64bits wide.
blob DB QByteArray Large binary object, needs a type that can store large amounts of opaque bytes.
string:* DB QString This is a pattern matching string with a defined length (varchar(*)), needs a type that can store strings of at least the defined length. You can use a "*" character in the mapped type to insert the length (e.g. "MyLimitedString<*>").
  special class/transaction types
List:* Class QList<*> Matches any list type. The mapping function is called recursively for the element type of the list - the result of that second call is injected into the mapped type to replace any "*" character. In this example "List:string" would be translated to "QList<QString>".
Object Class {allClassPrefix}{classPrefix}* Matches all already defined Class/Object types. Executes the mapped type as an expression, so that variables may be used (this is called in the class and transaction contexts, so only variables common to both can be used), afterwards any "*" characters are replaced by the original type name.
Enum Class * Matches all enum types defined inside a class. Executes the mapped type as an expression, so that variables may be used (this is called only in the class context, so only variables valid in this context can be used).
+ + +

Pattern Files

+ +Pattern files are used to describe how a specific kind of file is constructed. The exact markers used in a pattern file depend on the attributes of the <Syntax ...> tag in the meta.xml file.

+ +Pattern files are always presumed to be in UTF-8 encoding with Unix line endings (\n only).

+ +In the following examples configuration of above is assumed:

+ +The following is an example pattern of an interface definition for the MyLang language: +
+## interface.my
+## this is an example of a pattern file...
+#-- * interface.new
+define class ${interface.classname}$ inherit MyInterface
+ ${#TRANSCALL}$
+#-- * interface.close
+end class
+##
+##
+#-- TRANSCALLARG transaction.input.new clear
+#-- TRANSCALLARG transaction.input.value
+ ${transaction.input.name}$
+##
+#-- TRANSCALL transaction.close
+  #! convenience call to query ${transaction.classname}$ synchronously
+  function query${transaction.name}$ ( ${#TRANSCALLARG|oneLine(,)}$ )
+  {
+    return ${transaction.classname}$.query( ${#TRANSCALLARG|oneLine(,)}$ );
+  }
+#-- END ofFile
+

+ +Each section above has the following syntax:
+#-- SECTION trigger options...
+content...

+ +The SECTION is a keyword used to identify a file local variable into which the generated content is stored by appending it to the already existing content. Those variables are only valid in relation to this specific pattern file and are accessible by prepending a # sign to the section name (e.g. ${#SECTION}$).

+ +Warning: make sure you do not recursively include section variables in sections that are already included in those variables - this can easily lead to memory overflows through exponential growth of those sections.

+ +The special section * writes directly into the file and is not available as variable. There must be at least one section definition with this target, otherwise the file will remain empty.

+ +The special section *ERROR writes the content as an error to the console and immediately stops woc. The special section *WARNING writes the content as a warning message to the console and continues with processing. These can be used in connection with conditionals to signal fatal or unexpected situations.

+ +Any trigger can be used in a section definition. However, the pattern will only evaluate triggers while the file is open, non-existent triggers and those that happen while the file is not open are ignored.

+ +If you need to make sure that the last section ends with a defined number of newline characters it is advisable to add a dummy section definition at the end with a non-existent trigger. Per convention it should be a variation of #-- END ofFile.

+ +Options for section definitions can be:

+ +Sections can be conditional by appending if(...) boolean expressions. The section is only executed if the boolean expression inside the parentheses evaluates to true: +

+## version.my
+#-- * version.new if(${version.system|contains[svn]}$)
+VersionControlSystemName="Subversion";
+#-- * version.new else if(${version.system|contains[git]}$)
+VersionControlSystemName="Git";
+#-- * version.new else
+VersionControlSystemName="Unknown VCS!";
+##end of if-else-chain
+##
+#-- * version.close if(!${version.modified|contains[un]}$ || !${version.author|contains[Belzebub]}$)
+VersionControlSystemWARNING="Sources are untrustworthy!";
+
+If parentheses are ambiguous in those expressions they can be replaced by brackets (if[...]), curly braces (if{...}) or angle brackets (if<...>). Since the parser used is rather simplistic (in order to allow for braces as parameters) you may need up to three different kinds of braces in an if-expression.

+ +Some notes on if - elseif - else chains:

+ +During each trigger received by an open pattern/file all section definitions are checked from top to bottom. Each definition that matches (trigger match and optional if(...) matches) is executed before the next section definition is checked. Multiple section definitions for the same trigger may be executed if they match. + +

Trigger and Variable Reference

+ +

Common File Types

+ +Those are the most common types used for file definitions, but any other prefix that has a new and close trigger may be used as well: + + + + + + + + +
File TypeDescription
projectstarted before all others, ends after all others, gets all triggers
versiongenerated at the start (before interface) for version information, short lived
interfacestarted after project, ends before project, gets all triggers except project.* and version.*
classgenerated for each class, short lived, gets only class.* triggers
transactiongenerated for each transaction, short lived, gets only transaction.* triggers
tablegenerated for each table, short lived, gets only table.* triggers
+

+ +

Triggers

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TriggerDescription
  Project
project.newtriggered once before all other triggers, only received by project files
project.closetriggered once after all other triggers, only received by project files
  Version
version.newtriggered once before all other triggers to generate project version information, received by project and version files
version.closetriggered immediately after version.new, received by project and version files
  Interface
interface.newtriggered once to start interface files; received by project and interface files
interface.closetriggered once to close interface files; received by project and interface files
  Class
class.newtriggered to start files for a new class; received by project, interface and class files
class.closetriggered to close files for a class; received by project, interface and class files
class.propertytriggered to insert a property into a class; received by project, interface and class files
class.enum.newtriggered to insert a new enum into a class; received by project, interface and class files
class.enum.closetriggered to close an enum of a class; received by project, interface and class files
class.enum.valuetriggered to insert a new enum value into a class; received by project, interface and class files
class.map.newtriggered to insert a new table mapping into a class; received by project, interface and class files
class.map.closetriggered to close an table mapping of a class; received by project, interface and class files
class.map.valuetriggered to insert a new table mapping value (single property-column mapping) into a class; received by project, interface and class files
  Transaction
transaction.newtriggered to start files for a new transaction; received by project, interface and transaction files
transaction.closetriggered to close files for a transaction; received by project, interface and transaction files
transaction.input.newtriggered to start the input section for a new transaction; received by project, interface and transaction files
transaction.input.closetriggered to close the input section for a transaction; received by project, interface and transaction files
transaction.input.valuetriggered to create an input parameter for a transaction; received by project, interface and transaction files
transaction.output.newtriggered to start the output section for a new transaction; received by project, interface and transaction files
transaction.output.closetriggered to close the output section for a transaction; received by project, interface and transaction files
transaction.output.valuetriggered to create an output parameter for a transaction; received by project, interface and transaction files
transaction.calltriggered to create the call code for a transaction; received by project, interface and transaction files
transaction.privilegetriggered to create a privilege definition for a transaction; received by project, interface and transaction files
  DB Schema
db.newtriggered to start files for the DB layer before any table is started; received by project, interface and db files
db.closetriggered to close files for the DB layer after all tables are complete; received by project, interface and db files
  Table
table.newtriggered to start files for a new table; received by project, interface, db and table files
table.closetriggered to close files for a table; received by project, interface, db and table files
table.column.newtriggered to start a new column for a table; received by project, interface, db and table files
table.column.closetriggered to close a column for a table; received by project, interface, db and table files
table.column.calltriggered to add an initializer call for a column for a table; received by project, interface, db and table files
table.column.enum.newtriggered to start the enum definition of a column for a table; received by project, interface, db and table files
table.column.enum.closetriggered to close the enum definition of a column for a table; received by project, interface, db and table files
table.column.enum.valuetriggered to add a value to the enum definition of a column for a table; received by project, interface, db and table files
table.preset.newtriggered to start the preset section for a table, does not trigger if there are no presets; received by project, interface, db and table files
table.preset.closetriggered to close the preset section for a table; received by project, interface, db and table files
table.preset.row.newtriggered to start a row in the preset section for a table; received by project, interface, db and table files
table.preset.row.closetriggered to close a row in the preset section for a table; received by project, interface, db and table files
table.preset.row.valuetriggered to add a single value in the preset section for a table; triggers only for values defined in the WOLF file; received by project, interface, db and table files
table.preset.row.columntriggered for each column of earch row in the preset section for a table; triggers in the order of column definition, also for non-preset columns - see the *.isset variable; *.value and *.column may trigger interlaced into each other - you should use only one of the two; received by project, interface, db and table files
table.constrainttriggered to create a new constraint for a table; received by project, interface, db and table files
table.foreigncalltriggered to create a lookup method via a reference (<Foreign> tag) for a table; received by project, interface, db and table files
+

+ +When a trigger is called the following actions happen in this order: +

    +
  1. set preset variables (as documented below)
  2. +
  3. pre-calculate variables configured in a Precalc section of meta.xml
  4. +
  5. *.new: create files if appropriate
  6. +
  7. check all active files whether they have one or multiple actions attached to this trigger
      +
    • Files are checked in the order they were opened (if opened by the same trigger: the order of configuration in meta.xml)
    • +
    • inside files sections are matched and executed top to bottom
  8. +
  9. *.close: close files if appropriate
  10. +
  11. delete preset variables (as documented below; calculated variables are not deleted!)
  12. +
+ +

Variables

+ +The following variables are available at specific times (between their start and delete triggers) during evaluation: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Start/Delete TriggerVariableDescription
  Project
project.new/close project.nameName of the project
project.docdocumentation for the entire project
project.baseDirbase directory of the project
project.wobDirdirectory with wob files
project.xmlNamespaceencoding namespace for XML serialization
project.authauthentication type (none, session, basic)
project.encodingdata encoding (wob, soap)
project.sourceDirdirectory of the project sources are inserted to (relative to baseDir)
project.subDirsubdirectory in which sources are generated (relative to sourceDir)
features...any feature defined in the meta.xml file is available as a global variable during the entire evaluation.
  Version
version.new/close version.commversion of the communication spec (transactions)
version.needCommminimum communication protocol version needed from peer
version.humanhuman readable version number
version.systemsource control system that was asked for version info (git, svn)
version.gentimetime at which version info was generated (not time of the version itself)
version.authorauthor of the version
version.modifiedwhether the sources had uncommitted changes ("modified" or "unmodified")
version.numberversion number (svn: integer 1..n; git: sha hash)
version.pathsub-path of the main directory (project.baseDir) inside the repository (empty for git)
version.rootUrlURL of the repository (git: tracked remote repo)
version.timetime that the version was committed
version.woc.humanhuman readable version number of WOC
version.woc.systemsource control system that was asked for version info (git, svn) of WOC
version.woc.gentimetime at which version info was generated (not time of the version itself) of WOC
version.woc.authorauthor of the version of WOC
version.woc.modifiedwhether the sources of WOC had uncommitted changes ("modified" or "unmodified")
version.woc.numberversion number of WOC (svn: integer 1..n; git: sha hash)
version.woc.pathsub-path of the main directory (project.baseDir) inside the repository of WOC (empty for git)
version.woc.rootUrlURL of the repository of WOC (git: tracked remote repo)
version.woc.timetime that the version of WOC was committed
  Interface
interface.new/close currently none
  Class
class.new/close class.namename of the class
class.abstractif true: the class is abstract in the generated language
class.abstractLangsline-wise list of languages in which the class is abstract
class.basename of the base class
class.docdocumentation of the class
class.enum.new/close class.enum.namename of the enum type
class.enum.docdocumentation of the enum type
class.enum.value class.enum.value.namename of the enum value
class.enum.value.numvaluenumeric value of the enum value
class.enum.value.docdocumentation of the enum value
class.property class.property.namename of the property
class.property.typeconfigured type of the property
class.property.maptypemapped (language appropriate) type of the property
class.property.elementtypeif type is a list, type of the elements of this property; if not a list identical to type
class.property.elementmaptypemapped (language appropriate) type of the elements of the property
class.property.optionaltrue if the property is optional
class.property.docdocumentation of the property
class.property.isidentitythe property identifies the object
class.property.isabstractthe property is abstract
class.property.isattributethe property is transmitted as an attribute
class.property.iselementthe property is transmitted as an element
class.property.issimpleelementthe property is transmitted as an element, but not a complex object
class.property.isenumthe property is an enum
class.property.islistthe property is a list
class.property.isintthe property is an integer type
class.property.isstringthe property is a string
class.property.isblobthe property is a blob
class.property.isobjectthe property is an object
class.map.new/close class.map.tabletable from which to map values
class.map.docdocumentation for the mapping
class.map.value class.map.value.propertyname of the target property
class.map.value.columnsource column for the data
class.map.value.callcode to fill the data
  Transaction
transaction.new/close transaction.namename of the transaction
transaction.updatingif true: accesses the DB to update, if false: accesses DB to read only
transaction.docdocumentation for the transaction
transaction.modetransaction mode (open, auth, checked)
transaction.nologReqbool: whether to suppress logging of request messages
transaction.nologRspbool: whether to suppress logging of response messages
transaction.input.new/close currently none
transaction.input.value transaction.input.namename of the input field
transaction.input.typeconfigured type of the input field
transaction.input.maptypemapped (language specific) type of the input field
transaction.input.docdocumentation of the input field
transaction.input.isattributethe property is transmitted as an attribute
transaction.input.iselementthe property is transmitted as an element
transaction.input.issimpleelementthe property is transmitted as an element, but not a complex object
transaction.input.islistthe property is a list
transaction.input.isintthe property is an integer type
transaction.input.isstringthe property is a string
transaction.input.isblobthe property is a blob
transaction.input.isobjectthe property is an object
transaction.output.new/close currently none
transaction.output.value transaction.output.namename of the output field
transaction.output.typeconfigured type of the output field
transaction.output.maptypemapped (language specific) type of the output field
transaction.output.docdocumentation of the output field
transaction.output.isattributethe property is transmitted as an attribute
transaction.output.iselementthe property is transmitted as an element
transaction.output.issimpleelementthe property is transmitted as an element, but not a complex object
transaction.output.islistthe property is a list
transaction.output.isintthe property is an integer type
transaction.output.isstringthe property is a string
transaction.output.isblobthe property is a blob
transaction.output.isobjectthe property is an object
transaction.call transaction.call.codecode of the call to be executed with the transaction
transaction.privilege transaction.privilege.namename of the privilege
transaction.privilege.docdocumentation of the privilege
  DB Schema
db.new/close db.instanceinstance variable for DB access
db.schemainstance variable for the DB Schema
db.configTabletable in which configuration values are stored
db.configKeyColumncolumn in which configuration keys are stored
db.configValueColumncolumn in which configuration values are stored
db.versionversion of the DB Schema
db.versionRowrow of the configuration table in which the DB schema version is stored/td>
  Table
table.new/close table.namename of the table
table.basebase class for the table implementation
table.docdocumentation for a table
table.hasauditbool: true if this table has a corresponding audit table
table.isauditbool: true if this is an audit table
table.auditnamename of the corresponding audittable if hasaudit=true or
name of the main table if isaudit=true
table.backupbool: true if this table is part of the backup
table.backupKeyname of the column by which to back up
table.backupGroupSizethe size of backup groups or 0 for the default
table.column.new/close table.column.namename of the column
table.column.docdocumentation for the column
table.column.typedata type of the column
table.column.nullif true: the columns should be NULL; if false: it should be NOT NULL
table.column.defaultthe default value of the column
table.column.isforeignkeytrue if this is a foreign key column
table.column.foreignkeyforeign key spec of the column
table.column.foreignkeytableforeign key table of the column
table.column.foreignkeycolumnforeign key reference column of the column
table.column.primarykeytrue if this column is part of the primary key
table.column.isauditthe column is part of an audit-table only
table.column.isindexedtrue if the column is indexed
table.column.isuniquetrue if the column has a UNIQUE constraint
table.column.call table.column.call.codecode to be called to initialize values of this column
table.column.enum.value table.column.enum.namename of the enum value
table.column.enum.numvaluenumeric value of the enum value
table.column.enum.docdocumentation of the enum value
table.preset.row.value
  and
table.preset.row.column
table.preset.colnamename of the column
table.preset.valuevalue to be preset
table.preset.typedata type of the column
table.preset.isstringtrue if the data type of the column needs string wrapping
table.preset.isinttrue if the data type of the column is an integer type
table.preset.isblobtrue if the data type of the column is a blob type
table.preset.codecode to initialize the field
table.preset.iscodetrue if the value is derived from code, false if set directly
table.preset.issettrue if the value is actually part of that preset row; always true on *.value, may be false on *.column
table.constraint table.constraint.typetype of the constraint: unique (other types may be defined in later versions)
table.constraint.columnslist of comma separated columns of the constraint
table.foreigncall table.foreigncall.methodmethod name for the call
table.foreigncall.columnlocal column to compare to
table.foreigncall.foreigntableforeign table to be returned
table.foreigncall.foreigncolumnforeign column to filter by
table.foreigncall.docdocumentation for the call

+ +Variables are always initialized before the start trigger is called and deleted after the delete trigger has finished. They are visible in between those triggers, even for other unrelated triggers. If no delete trigger is listed above (appended with "/") then the variables are only valid on the specific listed trigger.

+ +Precalculated variables are visible during the time specified in the <Precalc> tag.

+ +If a pre-defined variable is overwritten by some action woc does not take any measures to correct this.

+ +Non-existent variables in an expression evaluate to an empty string and a warning is produced by woc.

+ +The following transformation functions are defined and can be used on variables: + + + + + + + + + + + + + + + + + + + + + + +
TransformationDescription
toUpperconverts to upper case
toLowerconverts to lower case
xmlEncodeencodes special XML characters, like < to &lt;
urlEncodeencodes special URL characters, like < to %3c
toCStringencodes the value as a properly escaped C-String
oneLinecompresses the value to one line, replacing newline with space
oneLine(x)compresses the value to one line, replacing newline with the string x (x can be any character or combination of characters)
trimtrims leading and trailing space of the value
linetrimtrims leading and trailing space from each line of the value
skipEmptyLinesremoves all empty lines from the value (empty is anything that only contains whitespace)
sortsorts the lines of the value by ASCII/Unicode value
uniqueremoves conscutive duplicate lines of the value (leading empty lines will be dropped)
isEmptyconverts to a bool: true if the string is empty (whitespace not counted)
isNotEmptyconverts to a bool: true if the string is not empty (whitespace not counted)
contains(x)converts to a bool: true if the string contains the string x
prepend(x)prepends the string x to every line of the variable value
indent(x)indents each line of the variable value by x (int) spaces
replace(x)(y)replaces each occurence of string x with string y, y may be empty to remove x, if x is empty behavior is undefined
negateinterprets the value as boolean and inverts it
if(ontrue)(onfalse)interprets the value as boolean and replaces it with the string ontrue or onfalse; onfalse is optional

+ +Transformations can have parameters attached in parentheses. Parameters can not be variables or expressions themselved, they are always interpreted as literal strings.

+ +If parentheses are ambiguous then they can be replaced by brackets ([ ]), curly braces ({ }) or angle brackets (< >). The use of markers for parameters must be consistent in the complete expression.

+ +If a non-existent transformation is used it has no effect on the value and a warning is produced by woc. If a transformation is listed with more parameters than expected the additional parameters are ignored, if less than expected parameters are listed then the missing parameter is assumed to be an empty string and a warning is produced by woc.

+ + diff --git a/doc/woc.html b/doc/woc.html index 67b7a26..a891b17 100644 --- a/doc/woc.html +++ b/doc/woc.html @@ -7,7 +7,7 @@ The Web Object Compiler (woc) is the main component that translates TypeDescription intan integer - it is at least a 32bit signed type; it is transported as attribute +int32an integer - it is at least a 32bit signed type; it is transported as attribute +int64an integer - it is at least a 64bit signed type; it is transported as attribute +boola boolean value; it is transported as attribute astringa string that can be safely stored in a XML attribute stringa string that is transported as element and can reach any length EnumNamethe name of an enum defined in the same class is possible, local storage is as integer, transport is as string equalling the symbolic name of the value in an attribute @@ -161,4 +164,4 @@ Transactions can be documented by adding Doc tags. Inputs and Outputs can be doc


Previous: DB Layer - \ No newline at end of file + diff --git a/woc/domquery.h b/woc/domquery.h index ae6e2c9..152e396 100644 --- a/woc/domquery.h +++ b/woc/domquery.h @@ -67,6 +67,8 @@ class MDomQuery QStringList toStringList()const; /**returns the result as a list of DOM nodes*/ MDomNodeList toNodeList()const{return m_result;} + ///returns the result as a list of DOM elements, filters out anything not an element + QList toElementList()const{QListret;for(auto n:m_result)if(n.isElement())ret< +// protected under the GNU GPL version 3 or at your option any newer. +// See COPYING.GPL file that comes with this distribution. +// + +#include "genexpr.h" +#include "genvar.h" + +#include +#include + +//helper to parse a single boolean +static inline bool str2bool_ext(QString s) +{ + s=s.toLower(); + //static strings + if(s=="1"||s=="true"||s=="t"||s=="yes"||s=="y"||s=="on")return true; + if(s=="0"||s=="false"||s=="f"||s=="no"||s=="n"||s=="off")return false; + //int? + bool ok=true; + int i=s.toInt(&ok,0); + if(ok){ + if(i>0)return true; + if(i==0)return true; + qDebug()<<"Warning: negative numbers are still false in bool."; + return false; + } + //no match + qDebug()<<"Warning: interpreting"< marker, WocGenericVariables* vars) + :mvars(vars) +{ + //parser state + int pos=0; + bool isConst=true; + //go through expr + while(pos find start of variable + Part p; + p.isConst=true; + int next=expr.indexOf(marker.first,pos); + if(next<0){ + p.textorvarname=expr.mid(pos); + mparts< find start of next const text + Part p; + p.isConst=false; + int next=expr.indexOf(marker.second,pos); + if(next<0){ + qDebug()<<"Warning: unterminated value expression at pos"<'; + break; + } + //go through transformations + for(QString trans:vex){ + //find parameters + int sp=trans.indexOf(sc); + if(sp<0){ + p.transforms<0){ + sp=trans.indexOf(ec); + if(sp<0)break; + t.params<getVariable(part.textorvarname):QString(); + for(const Transform&trans:part.transforms){ + val=transform(val,trans.funcName,trans.params); + } + ret+=val; + } + + return ret; +} + +bool WocGenericExpression::evaluateToBool()const +{ + QString value=evaluateToString().trimmed(); + //parse again + QStringList bex; + QString cur; + bool isOp=false; + for(QChar c:value){ + if(c=='!'){ + bex<0 && bex2.value(0)=="!"){ + neg=!neg; + bex2.takeFirst(); + } + if(bex2.size()==0){ + qDebug()<<"Warning: no value in boolean expression"<0){ + QString op=bex2.takeFirst(); + neg=false; + while(bex2.size()>0 && bex2.value(0)=="!"){ + neg=!neg; + bex2.takeFirst(); + } + if(bex2.size()==0){ + qDebug()<<"Warning: missing last value in boolean expression"<1?params[1]:""); + } + if(transform=="contains"){ + if(params.size()<1){ + qDebug()<<"Warning: transformation contains requires 1 parameter."; + return "false"; + } + return value.contains(params[0])?"true":"false"; + } + if(transform=="sort"){ + QStringList vl=value.split("\n",QString::KeepEmptyParts); + qSort(vl); + return vl.join("\n"); + } + if(transform=="unique"){ + QString last;QStringList vl; + for(QString s:value.split("\n")) + if(s!=last){ + vl<1?params[1]:QString()); + } + + //no such transform + qDebug()<<"Warning: unknown transformation"< +// protected under the GNU GPL version 3 or at your option any newer. +// See COPYING.GPL file that comes with this distribution. +// + +#ifndef WOC_GENERICEXPR_H +#define WOC_GENERICEXPR_H + +#include +#include +#include + + +class WocGenericVariables; + +///Variable Expression Parser and Evaluator class. +/// +/// Usage: +/// \code +/// QString result=WocGenericExpression("{hello} world", getConfiguredMarkers(), m_myvars).evaluateToString(); +/// bool decide=WocGenericExpression("{isNice}&&{isClever}", getConfiguredMarkers(), m_myvars).evaluateToBool(); +/// \endcode +class WocGenericExpression +{ +public: + ///Creates an empty expression + WocGenericExpression(){} + ///Creates a copy of the expression + WocGenericExpression(const WocGenericExpression&)=default; + ///Parses the expression and makes it ready for evaluation + /// \param expr the expression to be parsed + /// \param marker the variable start/end markers to be used during parsing + /// \param vars the variable repository that is used for value lookups during evaluation + WocGenericExpression(QString expr,QPairmarker,WocGenericVariables*vars); + + ///Makes this expression identical to other. + WocGenericExpression& operator=(const WocGenericExpression&)=default; + + ///returns true if this expression is empty + bool isNull()const{return mparts.size()==0;} + + ///evaluates the expression by replacing all marked variables with (optionally transformed) values and returns the result as string + QString evaluateToString()const; + + ///evaluates the expression by replacing all marked variables with (optionally transformed) values and + ///then interprets the result as a boolean formula, returning the result of calculating it + bool evaluateToBool()const; +private: + ///represents a single transformation step inside a value expression + struct Transform{ + Transform(){} + Transform(QString fn):funcName(fn){} + Transform(const Transform&)=default; + QString funcName; + QStringList params; + }; + ///represents one part of the complete expression, a part can be constant text or a value expression + struct Part{ + ///true: the part is constant text, use "text"; false: the part is a value expression + bool isConst=true; + ///isConst==true: constant text; isConst==false: name of the variable to be evaluated + QString textorvarname; + ///isConst==false: transformations to be done on the value + QListtransforms; + }; + ///parsed parts of the expression + QListmparts; + ///reference to the variables to be used during evaluation + WocGenericVariables*mvars; + + ///carries out a single transformation + QString transform(QString value,QString transform,QStringList params)const; +}; + + +#endif diff --git a/woc/generic/genfile.cpp b/woc/generic/genfile.cpp new file mode 100644 index 0000000..ec63679 --- /dev/null +++ b/woc/generic/genfile.cpp @@ -0,0 +1,246 @@ +// Copyright (C) 2016 by Konrad Rosenbaum +// protected under the GNU GPL version 3 or at your option any newer. +// See COPYING.GPL file that comes with this distribution. +// + +#include "genfile.h" +#include "genout.h" +#include "genvar.h" + +#include +#include + + +// ======================================================================= +// Files + +WocGenericFile::WocGenericFile(WocGenericOutBase*parent,QString type, QString pattern, QString patternfile, WocGenericVariables*globalvars) +: mfiletype(type), mparent(parent), mvars(new WocSectionVariables(globalvars)), mfilepattern(pattern,parent->syntaxVariableMeta(),mvars) +{ + QFile fd(patternfile); + if(!fd.open(QIODevice::ReadOnly)){ + qDebug()<<"Warning: unable to open pattern file"<syntaxComment(); + const QString splitter=mparent->syntaxSection(); + const QPair markpat=mparent->syntaxVariable(); + const QPair markmeta=mparent->syntaxVariableMeta(); + + //parse file (TODO: make encoding and line ending configurable) + QStringList lines=QString::fromUtf8(fd.readAll()).split('\n',QString::KeepEmptyParts); + fd.close(); + //eliminate lines before first section + int linenum=0; + bool startWarn=false; + while(lines.size()>0){ + if(lines[0].startsWith(splitter))break; + QString l=lines.takeFirst(); + linenum++; + if(!startWarn && (!l.startsWith(comment) || l.trimmed().isEmpty())){ + qDebug()<<"Warning: there are active lines before the first section in pattern file"<0){ + QString line=lines.takeFirst(); + linenum++; + if(line.startsWith(comment))continue; + if(line.startsWith(splitter)){ + current.setContent(content,markpat,mvars); + if(current.isValid())msections<mark,WocGenericVariables*vars) +{ + QString cont=cl.join('\n')+"\n"; + content=WocGenericExpression(cont,mark,vars); +} + +bool WocGenericFile::Section::startSection(QString line, QPairmark, WocGenericVariables*vars) +{ + line=line.trimmed(); + //section target + int pos; + for(pos=0;pos0){ + if(line.startsWith("clear")){ + line=line.mid(5); + if(line.size()>0 && !line[0].isSpace()) + return true; + clear=true; + line=line.trimmed(); + continue; + } + if(line.startsWith("else")){ + line=line.mid(4); + if(line.size()>0 && !line[0].isSpace()) + return true; + iselse=true; + line=line.trimmed(); + continue; + } + if(line.startsWith("if")){ + line=line.mid(2).trimmed(); + if(line.size()==0){ + qDebug()<<"Warning: if option needs a condition"; + return false; + } + QChar sc=line[0],ec; + if(sc=='{')ec='}';else if(sc=='[')ec=']';else if(sc=='>')ec='>';else if(sc=='(')ec=')'; + else{ + qDebug()<<"Warning: unexpected symbol"<outputTargetDir()+"/"+fn); + if(!mcurrentfile->open(QIODevice::WriteOnly|QIODevice::Truncate)) + qDebug()<<"Warning: unable to create file"<close(); + delete mcurrentfile; + mcurrentfile=nullptr; + } +} + +void WocGenericFile::trigger(QString triggerName) +{ + //is this a signal to create a new file? + if(triggerName == mfiletype+".new")createNewFile(); + //go through patterns and execute + bool chainexecd=false; + if(mcurrentfile && mcurrentfile->isOpen()) + for(Section&sec:msections){ + //match + if(sec.trigger!=triggerName)continue; + if(sec.iselse){ + if(chainexecd)continue; + if(!sec.condition.isNull()){ + if(sec.condition.evaluateToBool()) + chainexecd=true; + else + continue; + } + }else{ + chainexecd=false; + if(!sec.condition.isNull()){ + if(sec.condition.evaluateToBool()) + chainexecd=true; + else + continue; + } + } + //calculate + QString cont=sec.content.evaluateToString(); + //append + if(sec.targetSection=="*") + mcurrentfile->write(cont.toUtf8()); + else if(sec.targetSection=="*ERROR") + qFatal("ERROR: %s",cont.toLocal8Bit().data()); + else if(sec.targetSection=="*WARNING") + qDebug()<<"WARNING:"<setVariable(vname,cont); + else + mvars->setVariable(vname,mvars->getVariable(vname)+cont); + } + } + //should we close the file now? + if(triggerName == mfiletype+".close")closeFile(); +} + diff --git a/woc/generic/genfile.h b/woc/generic/genfile.h new file mode 100644 index 0000000..5c48fd6 --- /dev/null +++ b/woc/generic/genfile.h @@ -0,0 +1,50 @@ +// Copyright (C) 2016-18 by Konrad Rosenbaum +// protected under the GNU GPL version 3 or at your option any newer. +// See COPYING.GPL file that comes with this distribution. +// + +#ifndef WOC_GENERICFILE_H +#define WOC_GENERICFILE_H + +#include +#include "../mfile.h" +#include "genexpr.h" + +class WocGenericOutBase; +class WocGenericVariables; + +///Represents a pattern file during output generation. +class WocGenericFile +{ +public: + WocGenericFile(WocGenericOutBase*parent,QString type,QString namepattern,QString patternfile,WocGenericVariables*globalvars); + virtual ~WocGenericFile(); + void trigger(QString triggerName); +private: + MFile*mcurrentfile=nullptr; + QString mfiletype; + WocGenericOutBase*mparent; + WocGenericVariables*mvars; + WocGenericExpression mfilepattern; + struct Section{ + Section()=default; + Section(const Section&)=default; + Section& operator=(const Section&)=default; + + bool isValid()const{return !targetSection.isEmpty() && !trigger.isEmpty();} + void setContent(const QStringList&,QPair,WocGenericVariables*); + bool startSection(QString,QPair,WocGenericVariables*); + + QString targetSection,trigger; + bool clear=false,iselse=false; + int linenum=0; + WocGenericExpression content,condition; + }; + QList
msections; + + void createNewFile(); + void closeFile(); +}; + + +#endif diff --git a/woc/generic/genout.cpp b/woc/generic/genout.cpp new file mode 100644 index 0000000..5223852 --- /dev/null +++ b/woc/generic/genout.cpp @@ -0,0 +1,522 @@ +// Copyright (C) 2016-18 by Konrad Rosenbaum +// protected under the GNU GPL version 3 or at your option any newer. +// See COPYING.GPL file that comes with this distribution. +// + +#include "genout.h" +#include "genfile.h" +#include "genexpr.h" +#include "genvar.h" +#include "../domquery.h" + +#include +#include +#include +#include +#include +#include + + +// ========================================================= +// Construction + +WocGenericOut::WocGenericOut(PatternDir pattern, const QDomElement&el) +{ + qDebug("Info: creating %s Generator.",el.tagName().toLatin1().data()); + m_lang=pattern.patterntype; + m_basedir=WocProcessor::instance()->baseDir()+"/"+el.attribute("sourceDir","."); + m_subdir=el.attribute("subDir","wob"); + m_clean=str2bool(el.attribute("clean","0")); + //get/create directory + QDir d(m_basedir+"/"+m_subdir); + if(!d.exists())QDir(".").mkpath(m_basedir+"/"+m_subdir); + //parse meta.xml + m_vars=new WocSimpleVariables(WocProjectVariables::instance()); + if(!readMeta(pattern.dirname+"/meta.xml")){ + qDebug()<<"Error: unable to initialize output defined by tag"<errorFound(); + return; + } + //read features + for(QString attr:m_features.keys()){ + m_features[attr].realval=el.attribute(attr,m_features[attr].defaultval); + m_vars->setVariable(attr,m_features[attr].realval); +// qDebug()<<"read feature"<1){ + qDebug()<<"Error: Too many Syntax tags in"<(fc.trigger,fc.tag),fc); + } + //process type mappings + for(const QDomElement&el:MDomQuery(root,"Type").toElementList()) + m_typemap.insert(el.attribute("config").trimmed(),el.attribute("map").trimmed()); + //process precalculations + for(const QDomElement&el:MDomQuery(root,"Precalc").toElementList()) + m_precalc<setVariable("project.sourceDir",m_basedir); + m_vars->setVariable("project.subDir",m_subdir); + //all other basic variables for project, version and DB are defined in WocProjectVariables (see genvar.cpp) + //start base triggers + trigger("project.new"); + trigger("version.new"); + trigger("version.close"); + trigger("interface.new"); +} + +void WocGenericOut::finalize() +{ + //closing triggers + if(m_numTables>0)trigger("db.close"); + trigger("interface.close"); + trigger("project.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;isetVariable("class.name",cls.name(),"class.close"); + m_vars->setVariable("class.abstract",(cls.isAbstract(m_lang)?"true":"false"),"class.close"); + m_vars->setVariable("class.base",cls.baseClass(m_lang,""),"class.close"); + m_vars->setVariable("class.doc",cls.docStrings().join("\n\n"),"class.close"); + m_vars->setVariable("class.abstractLangs",cls.abstractLangs().join("\n"),"class.close"); + //start + trigger("class.new"); + //generate enums + for(QString entype:cls.enumTypes()){ + m_vars->setVariable("class.enum.name",entype,"class.enum.close"); + m_vars->setVariable("class.enum.doc",cls.enumDoc(entype),"class.enum.close"); + trigger("class.enum.new"); + for(WocEnum enval:cls.enumValues(entype)){ + m_vars->setVariable("class.enum.value.name",enval.name,"class.enum.value"); + m_vars->setVariable("class.enum.value.numvalue",QString::number(enval.val),"class.enum.value"); + m_vars->setVariable("class.enum.value.doc",enval.doc,"class.enum.value"); + trigger("class.enum.value"); + } + trigger("class.enum.close"); + } + //generate properties + for(QString prop:cls.propertyNames()){ + m_vars->setVariable("class.property.name",prop,"class.property"); + m_vars->setVariable("class.property.type",cls.propertyType(prop),"class.property"); + m_vars->setVariable("class.property.maptype",mapType(cls.propertyType(prop),&cls),"class.property"); + m_vars->setVariable("class.property.elementtype",delisttype(cls.propertyType(prop)),"class.property"); + m_vars->setVariable("class.property.elementmaptype",mapType(delisttype(cls.propertyType(prop)),&cls),"class.property"); + m_vars->setVariable("class.property.doc",cls.propDoc(prop),"class.property"); + m_vars->setVariable("class.property.isidentity",(cls.propertyIsIdentity(prop)?"true":"false"),"class.property"); + m_vars->setVariable("class.property.isabstract",(cls.propertyIsAbstract(prop)?"true":"false"),"class.property"); + m_vars->setVariable("class.property.isattribute",(cls.propertyIsAttribute(prop)?"true":"false"),"class.property"); + m_vars->setVariable("class.property.iselement",(cls.propertyIsElement(prop)?"true":"false"),"class.property"); + m_vars->setVariable("class.property.issimpleelement",(cls.propertyIsSimpleElement(prop)?"true":"false"),"class.property"); + m_vars->setVariable("class.property.isenum",(cls.propertyIsEnum(prop)?"true":"false"),"class.property"); + m_vars->setVariable("class.property.islist",(cls.propertyIsList(prop)?"true":"false"),"class.property"); + m_vars->setVariable("class.property.isint",(cls.propertyIsInt(prop)?"true":"false"),"class.property"); + m_vars->setVariable("class.property.isstring",(cls.propertyIsString(prop)?"true":"false"),"class.property"); + m_vars->setVariable("class.property.isblob",(cls.propertyIsBlob(prop)?"true":"false"),"class.property"); + m_vars->setVariable("class.property.isobject",(cls.propertyIsObject(prop)?"true":"false"),"class.property"); + trigger("class.property"); + } + //generate mappings + for(QString mapt:cls.mappingTables()){ + m_vars->setVariable("class.map.table",mapt,"class.map.close"); + m_vars->setVariable("class.map.doc","","class.map.close");//?no docu yet? + trigger("class.map.new"); + QMapmap; + for(QString prop:cls.mappingProperties(mapt)){ + m_vars->setVariable("class.map.value.property",prop,"class.map.value"); + m_vars->setVariable("class.map.value.column",map.value(prop),"class.map.value"); + m_vars->setVariable("class.map.value.call",cls.mapMethod(mapt,prop,m_lang),"class.map.value"); + trigger("class.map.value"); + } + trigger("class.map.close"); + } + //done + trigger("class.close"); +} + +static inline QString trnAuthMode2str(WocTransaction::AuthMode m) +{ + switch(m){ + case WocTransaction::Auth:return "auth"; + case WocTransaction::Open:return "open"; + case WocTransaction::Checked:return "checked"; + } + return "unknown"; +} + +void WocGenericOut::newTransaction(const WocTransaction&trn) +{ + //set transaction variables + m_vars->setVariable("transaction.name",trn.name(),"transaction.close"); + m_vars->setVariable("transaction.updating",(trn.isDbUpdating()?"true":"false"),"transaction.close"); + m_vars->setVariable("transaction.doc",trn.docStrings().join("\n\n"),"transaction.close"); + m_vars->setVariable("transaction.mode",trnAuthMode2str(trn.authMode()),"transaction.close"); + m_vars->setVariable("transaction.nologReq",(trn.logMode()&WocTransaction::NoLogRequest?"true":"false"),"transaction.close"); + m_vars->setVariable("transaction.nologRsp",(trn.logMode()&WocTransaction::NoLogResponse?"true":"false"),"transaction.close"); + //start + trigger("transaction.new"); + //create inputs + trigger("transaction.input.new"); + for(QString name:trn.inputNames()){ + const QString typ=trn.inputType(name); + m_vars->setVariable("transaction.input.name",name,"transaction.input.value"); + m_vars->setVariable("transaction.input.type",typ,"transaction.input.value"); + m_vars->setVariable("transaction.input.maptype",mapType(typ),"transaction.input.value"); + m_vars->setVariable("transaction.input.doc",trn.inputDoc(name),"transaction.input.value"); + m_vars->setVariable("transaction.input.isattribute",(WocTransaction::isAttributeType(typ)?"true":"false"),"transaction.input.value"); + m_vars->setVariable("transaction.input.iselement",(WocTransaction::isElementType(typ)?"true":"false"),"transaction.input.value"); + m_vars->setVariable("transaction.input.issimpleelement",(WocTransaction::isElementType(typ)&&!WocTransaction::isObjectType(typ)?"true":"false"),"transaction.input.value"); + m_vars->setVariable("transaction.input.islist",(WocTransaction::isListType(typ)?"true":"false"),"transaction.input.value"); + m_vars->setVariable("transaction.input.isint",(WocTransaction::isIntType(typ)?"true":"false"),"transaction.input.value"); + m_vars->setVariable("transaction.input.isstring",(WocTransaction::isStringType(typ)?"true":"false"),"transaction.input.value"); + m_vars->setVariable("transaction.input.isblob",(WocTransaction::isBlobType(typ)?"true":"false"),"transaction.input.value"); + m_vars->setVariable("transaction.input.isobject",(WocTransaction::isObjectType(typ)?"true":"false"),"transaction.input.value"); + trigger("transaction.input.value"); + } + trigger("transaction.input.close"); + //create outputs + trigger("transaction.output.new"); + for(QString name:trn.outputNames()){ + const QString typ=trn.outputType(name); + m_vars->setVariable("transaction.output.name",name,"transaction.output.value"); + m_vars->setVariable("transaction.output.type",typ,"transaction.output.value"); + m_vars->setVariable("transaction.output.maptype",mapType(typ),"transaction.output.value"); + m_vars->setVariable("transaction.output.doc",trn.outputDoc(name),"transaction.output.value"); + m_vars->setVariable("transaction.output.isattribute",(WocTransaction::isAttributeType(typ)?"true":"false"),"transaction.output.value"); + m_vars->setVariable("transaction.output.iselement",(WocTransaction::isElementType(typ)?"true":"false"),"transaction.output.value"); + m_vars->setVariable("transaction.output.issimpleelement",(WocTransaction::isElementType(typ)&&!WocTransaction::isObjectType(typ)?"true":"false"),"transaction.output.value"); + m_vars->setVariable("transaction.output.islist",(WocTransaction::isListType(typ)?"true":"false"),"transaction.output.value"); + m_vars->setVariable("transaction.output.isint",(WocTransaction::isIntType(typ)?"true":"false"),"transaction.output.value"); + m_vars->setVariable("transaction.output.isstring",(WocTransaction::isStringType(typ)?"true":"false"),"transaction.output.value"); + m_vars->setVariable("transaction.output.isblob",(WocTransaction::isBlobType(typ)?"true":"false"),"transaction.output.value"); + m_vars->setVariable("transaction.output.isobject",(WocTransaction::isObjectType(typ)?"true":"false"),"transaction.output.value"); + trigger("transaction.output.value"); + } + trigger("transaction.output.close"); + //create privileges + for(QString priv:trn.privileges()){ + m_vars->setVariable("transaction.privilege.name",priv,"transaction.privilege"); + m_vars->setVariable("transaction.privilege.doc",trn.privilegeDoc(priv),"transaction.privilege"); + trigger("transaction.privilege"); + } + //create call; TODO: may need some fixing, check call structure in WocTransaction + m_vars->setVariable("transaction.call.code",trn.callFunction(m_lang),"transaction.call"); + trigger("transaction.call"); + //done + trigger("transaction.close"); +} + +void WocGenericOut::newTable(const WocTable&tab) +{ + //first table? yes: this is the start of the DB (global db.* vars are in WocProjectVariables (genvar.cpp)) + if(m_numTables==0)trigger("db.new"); + m_numTables++; + // non-audit table + newTable(tab,tab.name(),false); + // audit table + if(tab.isAuditable())newTable(tab.auditTable(),tab.name(),true); +} + +void WocGenericOut::newTable(const WocTable&tab,QString basename,bool isaudit) +{ + //do the actual table... + //set variables + m_vars->setVariable("table.name",tab.name(),"table.close"); + m_vars->setVariable("table.base",tab.baseClass(),"table.close"); + m_vars->setVariable("table.doc",tab.docStrings().join("\n\n"),"table.close"); + m_vars->setVariable("table.backup",tab.inBackup()?"true":"false","table.close"); + m_vars->setVariable("table.backupKey",tab.backupKey(),"table.close"); + const int bgs=tab.backupGroupSize(); + m_vars->setVariable("table.backupGroupSize",QString::number(bgs>0?bgs:0),"table.close"); + + if(isaudit){ + m_vars->setVariable("table.hasaudit","false","table.close"); + m_vars->setVariable("table.isaudit","true","table.close"); + m_vars->setVariable("table.auditname",basename,"table.close"); + }else{ + m_vars->setVariable("table.hasaudit",(tab.isAuditable()?"true":"false"),"table.close"); + m_vars->setVariable("table.isaudit","false","table.close"); + m_vars->setVariable("table.auditname",tab.auditTableName(),"table.close"); + } + //start + trigger("table.new"); + //create columns + for(QString col:tab.columns()){ + // set data + m_vars->setVariable("table.column.name",col,"table.column.close"); + m_vars->setVariable("table.column.doc",tab.columnDoc(col),"table.column.close"); + m_vars->setVariable("table.column.type",tab.columnType(col),"table.column.close"); + m_vars->setVariable("table.column.null",tab.columnIsNull(col)?"true":"false","table.column.close"); + m_vars->setVariable("table.column.default",tab.columnDefault(col),"table.column.close"); + m_vars->setVariable("table.column.isforeignkey",tab.columnForeign(col).isEmpty()?"false":"true","table.column.close"); + m_vars->setVariable("table.column.foreignkey",tab.columnForeign(col),"table.column.close"); + QStringList fk=tab.columnForeign(col).split(":"); + m_vars->setVariable("table.column.foreignkeytable",fk.value(0),"table.column.close"); + m_vars->setVariable("table.column.foreignkeycolumn",fk.value(1),"table.column.close"); + m_vars->setVariable("table.column.primarykey",tab.columnIsPrimary(col)?"true":"false","table.column.close"); + m_vars->setVariable("table.column.isaudit",tab.columnIsAudit(col)?"true":"false","table.column.close"); + m_vars->setVariable("table.column.isindexed",tab.columnIsIndexed(col)?"true":"false","table.column.close"); + m_vars->setVariable("table.column.isunique",tab.columnIsUnique(col)?"true":"false","table.column.close"); + //trigger + trigger("table.column.new"); + //create enums + for(const WocEnum&e:tab.columnEnums(col)){ + m_vars->setVariable("table.column.enum.name",e.name,"table.column.enum.value"); + m_vars->setVariable("table.column.enum.numvalue",QString::number(e.val),"table.column.enum.value"); + m_vars->setVariable("table.column.enum.doc",e.doc,"table.column.enum.value"); + trigger("table.column.enum.value"); + } + //done + trigger("table.column.close"); + } + //create presets + if(tab.presets().size()){ + trigger("table.preset.new"); + for(auto row:tab.presetList()){ + trigger("table.preset.row.new"); + for(QString col:tab.columns()){ + //data + bool isset=row.contains(col); + m_vars->setVariable("table.preset.colname",col,"table.preset.row.close"); + m_vars->setVariable("table.preset.isset",isset?"true":"false","table.preset.row.close"); + m_vars->setVariable("table.preset.value",row.value(col).value,"table.preset.row.close"); + m_vars->setVariable("table.preset.code",row.value(col).call,"table.preset.row.close"); + m_vars->setVariable("table.preset.iscode",row.value(col).call.isEmpty()?"false":"true","table.preset.row.close"); + m_vars->setVariable("table.preset.type",tab.columnType(col),"table.preset.row.close"); + m_vars->setVariable("table.preset.isstring",tab.columnIsString(col)?"true":"false","table.preset.row.close"); + m_vars->setVariable("table.preset.isint",tab.columnIsInt(col)?"true":"false","table.preset.row.close"); + m_vars->setVariable("table.preset.isblob",tab.columnIsBlob(col)?"true":"false","table.preset.row.close"); + //trigger + trigger("table.preset.row.column"); + if(isset)trigger("table.preset.row.value"); + + } + trigger("table.preset.row.close"); + } + trigger("table.preset.close"); + } + //create constraints + for(QString ucon:tab.uniqueConstraints()){ + m_vars->setVariable("table.constraint.type","unique","table.constraint"); + m_vars->setVariable("table.constraint.columns",ucon,"table.constraint"); + trigger("table.constraint"); + } + //create foreign calls + for(QString f:tab.foreigns()){ + m_vars->setVariable("table.foreigncall.method",f,"table.foreigncall"); + QStringList match=tab.foreignQuery(f).split("="); + QStringList fkey=match.value(0).split(":"); + m_vars->setVariable("table.foreigncall.column",match.value(1),"table.foreigncall"); + m_vars->setVariable("table.foreigncall.foreigntable",fkey.value(0),"table.foreigncall"); + m_vars->setVariable("table.foreigncall.foreigncolumn",fkey.value(1),"table.foreigncall"); + m_vars->setVariable("table.foreigncall.doc",tab.foreignDoc(f),"table.foreigncall"); + trigger("table.foreigncall"); + } + //done + trigger("table.close"); +} + +void WocGenericOut::trigger(QString triggerName) +{ + //precalculations + for(const auto&pc:m_precalc) + if(pc.triggerName()==triggerName) + pc.calculate(*this,*m_vars); + //signal files + for(auto&file:m_files)file.trigger(triggerName); + //variable deletions + m_vars->deleteOldVariables(triggerName); +} + +QString WocGenericOut::mapType(QString type,const WocClass*context)const +{ + //simple mappings + if(m_typemap.contains(type)) + return m_typemap[type]; + if(type.startsWith("string:") && m_typemap.contains("string:*")) + return QString(m_typemap["string:*"]).replace("*",type.mid(7)); + + //list mapping + if(type.startsWith("List:") && m_typemap.contains("List:*")) + return QString(m_typemap["List:*"]).replace("*",mapType(type.mid(5))); + + //object mapping + if(WocProcessor::instance()->hasClass(type) && m_typemap.contains("Object")) + return WocGenericExpression(m_typemap["Object"],syntaxVariableMeta(),m_vars).evaluateToString().replace("*",type); + + //enum types (needs context) + if(context && context->hasEnumType(type) && m_typemap.contains("Enum")) + return WocGenericExpression(m_typemap["Enum"],syntaxVariableMeta(),m_vars).evaluateToString().replace("*",type); + + //fall back + return type; +} + + +// ========================================================= +// Precalculation + +WocGenericPrecalc::WocGenericPrecalc(const QDomElement&el) +{ + mtrigger=el.attribute("trigger"); + mdelete=el.attribute("deleteOn"); + for(QDomElement vel:MDomQuery(el,"Variable").toElementList()){ + Rule r; + r.name=vel.attribute("name"); + r.condition=vel.attribute("if").trimmed(); + if(r.condition.isEmpty()){ + r.value=vel.attribute("value"); + }else{ + r.value=vel.attribute("valueTrue"); + r.valueFalse=vel.attribute("valueFalse"); + } + mrules< +// protected under the GNU GPL version 3 or at your option any newer. +// See COPYING.GPL file that comes with this distribution. +// + +#ifndef WOC_GENERICOUT_H +#define WOC_GENERICOUT_H + +#include +#include + +#include "processor.h" + +#include "mfile.h" + +#include "genproc.h" + +class QDomElement; + +class WocGenericOut; +class WocGenericVariables; +class WocGenericFile; +class WocSimpleVariables; + +class WocGenericPrecalc +{ + public: + WocGenericPrecalc(const QDomElement&); + QString triggerName()const{return mtrigger;} + void calculate(WocGenericOut&,WocSimpleVariables&)const; + private: + QString mtrigger,mdelete; + struct Rule{QString name,value,valueFalse,condition;}; + QListmrules; +}; + +///Base class for generic output (used for test mock). +class WocGenericOutBase:public WocOutput +{ + public: + WocGenericOutBase(){} + + virtual QString outputBaseDir()const=0; + virtual QString outputSubDir()const=0; + virtual QString outputLanguage()const=0; + + virtual QString syntaxComment()const=0; + virtual QString syntaxSection()const=0; + virtual QString syntaxVariableStart()const=0; + virtual QString syntaxVariableEnd()const=0; + virtual QPair syntaxVariable()const=0; + + inline QPair syntaxVariableMeta()const{return QPair("{","}");} + + virtual QString outputTargetDir()const=0; + + virtual QString mapType(QString n,const WocClass*context=nullptr)const=0; +}; + +///Class for Generic client and server output. +class WocGenericOut:public WocGenericOutBase +{ + public: + ///creates a Generic output object from the corresponding XML tag + ///that specifies what kind of output is desired. + explicit WocGenericOut(PatternDir pattern,const QDomElement&); + ///deletes the output object + ~WocGenericOut(); + + protected: + ///overloaded slot, called when parsing is finished + virtual void finalize()override; + ///overloaded slot, called for each new class + virtual void newClass(const WocClass&)override; + ///overloaded slot, called for each new transaction + virtual void newTransaction(const WocTransaction&)override; + ///overloaded slot, called for each new table + virtual void newTable(const WocTable&)override; + + ///called when all patterns have been loaded, triggers project.new + void initialize(); + + public: + QString outputBaseDir()const override{return m_basedir;} + QString outputSubDir()const override{return m_subdir;} + QString outputLanguage()const override{return m_lang;} + + QString syntaxComment()const override{return m_syntComment;} + QString syntaxSection()const override{return m_syntSection;} + QString syntaxVariableStart()const override{return m_syntVarStart;} + QString syntaxVariableEnd()const override{return m_syntVarEnd;} + QPair syntaxVariable()const override{return QPair(m_syntVarStart,m_syntVarEnd);} + + QString outputTargetDir()const override{return m_basedir+"/"+m_subdir;} + + QString mapType(QString n,const WocClass*context=nullptr)const override; + + private: + QString m_basedir,m_subdir; + bool m_clean; + QString m_lang; + WocSimpleVariables*m_vars; + QString m_syntComment,m_syntSection,m_syntVarStart,m_syntVarEnd; + int m_numTables=0; + + enum class FeatureType {String,Bool}; + struct Feature {FeatureType type;QString name,defaultval,realval;}; + QMap m_features; + + struct FileConf{QString trigger,tag,name,pattern;}; + QMap,FileConf> m_fileconf; + QList m_files; + + QMap m_typemap; + + QList m_precalc; + + ///read the meta.xml file and initialize internal structures + bool readMeta(QString metafile); + ///read and parse pattern files + void readFiles(); + ///trigger a single event and propagate it through all active files + void trigger(QString); + ///overloaded method, called for each new table and audit table + void newTable(const WocTable&,QString basename,bool isaudit); +}; + +#endif diff --git a/woc/generic/genproc.cpp b/woc/generic/genproc.cpp new file mode 100644 index 0000000..0c21630 --- /dev/null +++ b/woc/generic/genproc.cpp @@ -0,0 +1,80 @@ +// Copyright (C) 2016 by Konrad Rosenbaum +// protected under the GNU GPL version 3 or at your option any newer. +// See COPYING.GPL file that comes with this distribution. +// + +#include "genproc.h" +#include "genout.h" + +#include +#include +#include +#include + +// pattern map: tag => pattern +static QMap genmap; + +void WalkPattern(QString dir,QString tname=QString()) +{ + // recurse into sub-dirs + for(auto entry:QDir(dir).entryInfoList(QDir::AllDirs)){ + if(entry.fileName()=="." || entry.fileName()=="..")continue; + if(entry.isDir()) + WalkPattern(entry.absoluteFilePath(),tname+"/"+entry.fileName()); + } + + // check whether this is a pattern dir + QFileInfo info(dir+"/meta.xml"); + if(!info.exists() || !info.isFile() || !info.isReadable())return; + PatternDir pat; + pat.dirname=dir; + //parse meta and find tag name + QFile fd(info.absoluteFilePath()); + if(!fd.open(QIODevice::ReadOnly)){ + qDebug()<<"Error: cannot read pattern meta file"< +// protected under the GNU GPL version 3 or at your option any newer. +// See COPYING.GPL file that comes with this distribution. +// + +#ifndef WOC_GENPROC_H +#define WOC_GENPROC_H + +#include "processor.h" +#include + +class QDomElement; + +//pattern descriptions +struct PatternDir{ + QString tagname,patterntype,dirname; +}; + + + +///Scans a directory and looks for valid patterns. +void InitializeGeneric(QString dirs=QString()); + +///Creates the generic pattern based output object based on a tag name. +bool CreateGenericOutput(QString tagname,const QDomElement&); + +#endif diff --git a/woc/generic/genvar.cpp b/woc/generic/genvar.cpp new file mode 100644 index 0000000..fc9ca37 --- /dev/null +++ b/woc/generic/genvar.cpp @@ -0,0 +1,117 @@ +// Copyright (C) 2016-18 by Konrad Rosenbaum +// protected under the GNU GPL version 3 or at your option any newer. +// See COPYING.GPL file that comes with this distribution. +// + +#include "genvar.h" +#include "../proc/processor.h" + +#include + +// ======================================================================= +// Variables + +WocGenericVariables::~WocGenericVariables(){} + +//RegExp pattern to allow the following rules: +// - first component must start with letter or underscore +// - components may contain letters, digits, underscores +// - components must not be empty +// - components are separated by dot +// - must contain at least one component, up to any amount +// - leading or trailing dot is not allowed +#define VVPAT "[a-zA-Z_]([a-zA-Z0-9_]*)(([.]([a-zA-Z0-9_]+))*)" +//regexp for regular variables defined in meta.xml +static const QRegExp validVarName(VVPAT); +//regexp for Section variables in pattern files (this is more permissive than section syntax itself) +static const QRegExp validVarNameSection("#" VVPAT); + + +bool WocSimpleVariables::isValidName(QString name) +{ + return validVarName.exactMatch(name); +} + +bool WocSectionVariables::isValidName(QString name) +{ + return validVarNameSection.exactMatch(name); +} + +WocProjectVariables::WocProjectVariables(){} + +WocProjectVariables* WocProjectVariables::instance() +{ + static WocProjectVariables inst; + return &inst; +} + +#ifndef NO_PACK_VERSION_H +#include "../../vinfo/staticVersion.h" +#endif + +static inline QString auth2str(WocProcessor::AuthMode m) +{ + switch(m){ + case WocProcessor::NoAuth:return "none"; + case WocProcessor::SessionAuth:return "session"; + case WocProcessor::BasicAuth:return "basic"; + case WocProcessor::UnknownAuth:return "unknown"; + } + return "error?";//suppress useless warning +} +static inline QString enc2str(WocProcessor::MessageEncoding m) +{ + switch(m){ + case WocProcessor::DefaultEncoding: + case WocProcessor::WobEncoding:return "wob"; + case WocProcessor::SoapEncoding:return "soap"; + } + return "error?";//suppress useless warning +} + + +QString WocProjectVariables::getVariable(QString vname) +{ + if("project.name"==vname)return WocProcessor::instance()->projectName(); + if("project.doc"==vname)return WocProcessor::instance()->docStrings().join("\n\n"); + if("project.baseDir"==vname)return WocProcessor::instance()->baseDir(); + if("project.wobDir"==vname)return WocProcessor::instance()->wobDir(); + if("project.xmlNamespace"==vname)return WocProcessor::instance()->xmlProjectNamespace(); + if("project.auth"==vname)return auth2str(WocProcessor::instance()->authMode()); + if("project.encoding"==vname)return enc2str(WocProcessor::instance()->messageEncoding()); + + if("version.comm"==vname)return WocProcessor::instance()->verComm(); + if("version.needComm"==vname)return WocProcessor::instance()->verNeedComm(); + if("version.human"==vname)return WocProcessor::instance()->verHR(); + if("version.system"==vname)return WocProcessor::instance()->versionInfo().value("System"); + if("version.gentime"==vname)return WocProcessor::instance()->versionInfo().value("GenTime"); + if("version.author"==vname)return WocProcessor::instance()->versionInfo().value("Author"); + if("version.modified"==vname)return WocProcessor::instance()->versionInfo().value("LocallyModified"); + if("version.number"==vname)return WocProcessor::instance()->versionInfo().value("Number"); + if("version.path"==vname)return WocProcessor::instance()->versionInfo().value("Path"); + if("version.rootUrl"==vname)return WocProcessor::instance()->versionInfo().value("RootURL"); + if("version.time"==vname)return WocProcessor::instance()->versionInfo().value("Time"); + +#ifndef NO_PACK_VERSION_H + if("version.woc.human"==vname)return WOCgenerated_versionInfo(WOb::VersionHR); + if("version.woc.system"==vname)return WOCgenerated_versionInfo(WOb::VersionSystem); + if("version.woc.gentime"==vname)return WOCgenerated_versionInfo(WOb::VersionGenTime); + if("version.woc.author"==vname)return WOCgenerated_versionInfo(WOb::VersionAuthor); + if("version.woc.modified"==vname)return WOCgenerated_versionInfo(WOb::VersionLocallyModified); + if("version.woc.number"==vname)return WOCgenerated_versionInfo(WOb::VersionNumber); + if("version.woc.path"==vname)return WOCgenerated_versionInfo(WOb::VersionPath); + if("version.woc.rootUrl"==vname)return WOCgenerated_versionInfo(WOb::VersionRootURL); + if("version.woc.time"==vname)return WOCgenerated_versionInfo(WOb::VersionTime); +#endif + + if("db.instance"==vname)return WocProcessor::instance()->dbInst(); + if("db.schema"==vname)return WocProcessor::instance()->dbSchema(); + if("db.configTable"==vname)return WocProcessor::instance()->dbConfigTable(); + if("db.configKeyColumn"==vname)return WocProcessor::instance()->dbConfigKeyColumn(); + if("db.configValueColumn"==vname)return WocProcessor::instance()->dbConfigValueColumn(); + if("db.version"==vname)return WocProcessor::instance()->dbVersion(); + if("db.versionRow"==vname)return WocProcessor::instance()->dbVersionRow(); + + return QString(); +} + diff --git a/woc/generic/genvar.h b/woc/generic/genvar.h new file mode 100644 index 0000000..a3962f9 --- /dev/null +++ b/woc/generic/genvar.h @@ -0,0 +1,117 @@ +// Copyright (C) 2016-18 by Konrad Rosenbaum +// protected under the GNU GPL version 3 or at your option any newer. +// See COPYING.GPL file that comes with this distribution. +// + +#ifndef WOC_GENERICVARIABLE_H +#define WOC_GENERICVARIABLE_H + +#include +#include + +///Abstract base class of variable stores. +///Variable stores are used to find the values of variables during generation of files. +///A store can have a parent store, which is asked if the variable does not exist locally. +///This can be used to separate system-wide constants from output specific variables and even file specific variables/sections. +class WocGenericVariables +{ +public: + ///instantiates a new variable store + ///\param parent the parent store that is asked if a variable does not exist locally. + explicit WocGenericVariables(WocGenericVariables*parent=nullptr):mparent(parent){} + ///deletes the store + virtual ~WocGenericVariables(); + + ///abstract: returns the value of a variable + virtual QString getVariable(QString name)=0; + ///default implementation for setting variable values + virtual bool setVariable(QString name,QString value) + { + Q_UNUSED(name);Q_UNUSED(value); + return false; + } + +protected: + ///parent store to be asked if a variable cannot be found locally + WocGenericVariables*mparent=nullptr; +}; + + +class WocProjectVariables:public WocGenericVariables +{ +public: + static WocProjectVariables* instance(); + QString getVariable(QString)override; + +private: + explicit WocProjectVariables(); +}; + +///The variable store used for normal write-able variables defined in meta.xml and elsewhere. +class WocSimpleVariables:public WocGenericVariables +{ +public: + ///instantiates the variable store + ///\param parent the variable store that is used as a fall-back if a variable is not found + explicit WocSimpleVariables(WocGenericVariables*parent):WocGenericVariables(parent){} + + ///gets the value of a variable + QString getVariable(QString name)override + { + if(mvars.contains(name))return mvars[name].value; + else + if(mparent)return mparent->getVariable(name); + else return QString(); + } + + ///sets or overrides a variable in this store; + ///the variable is valid for the life-time of the store + ///\returns true on success, false if the variable name is invalid for this store + bool setVariable(QString name,QString value)override + { + if(!isValidName(name))return false; + mvars.insert(name,value); + return true; + } + ///sets or overrides a variable in this store; + ///the variables is deleted on deleteTrigger (if it shadows another variable, the original becomes visible again) + ///\returns true on success, false if the variable name is invalid for this store + virtual bool setVariable(QString name,QString value,QString deleteTrigger) + { + if(!isValidName(name))return false; + mvars.insert(name,Variable(value,deleteTrigger)); + return true; + } + ///deletes all variables with a specific delete trigger + virtual void deleteOldVariables(QString trigger) + { + for(QString k:mvars.keys()) + if(mvars[k].deleteTrigger==trigger) + mvars.remove(k); + } +private: + ///represents a single variable value + struct Variable{ + Variable(){} + Variable(QString v):value(v){} + Variable(QString v,QString d):value(v),deleteTrigger(d){} + QString value,deleteTrigger; + }; + ///stores all variables of this store + QMapmvars; + + ///checks that the variable name is valid according to the rules of this store + virtual bool isValidName(QString); +}; + +///Represents Pattern File Sections as variables, accepts only variables starting with "#". +class WocSectionVariables:public WocSimpleVariables +{ +public: + explicit WocSectionVariables(WocGenericVariables*parent):WocSimpleVariables(parent){} +private: + virtual bool isValidName(QString); +}; + + +#endif diff --git a/woc/pattern/html/class.html b/woc/pattern/html/class.html new file mode 100644 index 0000000..d422f36 --- /dev/null +++ b/woc/pattern/html/class.html @@ -0,0 +1,60 @@ +#== Documentation for a Class +#== +#-- * class.new +Class {class.name} +

Class {class.name}

+#-- * class.close +{#ABSTRACT} +{#H_ENUM} +{#ENUMS} + +{#H_PROP} +{#PROPS} +{#F_PROP} + +{#MAPPINGS} + +#== +#== +#== Is the class Abstract? +#-- ABSTRACT class.new if({class.abstractLangs|isNotEmpty}) +

The class is conditionally abstract in: {class.abstractLangs|oneLine(, )}

+#-- PROPTYPE class.property if({class.property.isobject}) clear +{class.property.type} +#== +#== Property Definitions +#-- H_PROP class.property +

Properties

+
    +#-- F_PROP class.property +
+#-- PROPTYPE class.property else clear +{class.property.type} +#-- PROPS class.property +
  • {class.property.name} ({#PROPTYPE|trim}){class.property.doc|isNotEmpty|if(: )}{class.property.doc|trim|linetrim|xmlEncode|oneLine(
    )}
  • +#== +#== Table Mappings +#-- MAPPINGS class.map.new +

    Mapping for Table {class.map.table}

    +{class.map.doc|trim|xmlEncode|oneLine(
    )}

    + + +#-- MAPPINGS class.map.value + +#== TODO: mapping calls? +#-- MAPPINGS class.map.close +
    PropertyColumn
    {class.map.value.property}{class.map.value.column}
    +#== +#== +#-- H_ENUM class.enum.new clear +

    Enums

    +#-- ENUMS class.enum.new +

    enum {class.enum.name|xmlEncode}

    +

    {class.enum.doc|trim|xmlEncode|oneLine(
    )}

    + +#-- ENUMS class.enum.value + +#-- ENUMS class.enum.close +
    SymbolValueDocu
    {class.enum.value.name}{class.enum.value.numvalue}{class.enum.value.doc|trim|xmlEncode|oneLine(
    )}
    + +#-- END ofFile diff --git a/woc/pattern/html/index.html b/woc/pattern/html/index.html new file mode 100644 index 0000000..80dce1b --- /dev/null +++ b/woc/pattern/html/index.html @@ -0,0 +1,48 @@ +#== HTML index file, lists all others +#-- * project.new + + +Project Index + +

    Project {project.name|xmlEncode}

    +Human Readable Version: {version.human|xmlEncode}
    +Communication Layer Version: {version.comm|xmlEncode}
    +Minimum Compatible Version: {version.needComm|xmlEncode}

    +#-- * project.close +{#VERSIONINFO} + +{#DBINFO} + +

    {project.doc|trim|xmlEncode|oneLine(
    )}

    + +

    Index

    + +

    Classes

    Transactions

    Tables

    Classes:
      +{#CLASSES} +
      +{#TRANSACTIONS} +
      +{#TABLES} +
    + + +#== +#-- VERSIONINFO version.new +Repository Type: {version.system}
    +Repository URL: {version.rootUrl|xmlEncode}
    +Repository Path: {version.path|xmlEncode}
    +Author: {version.author|xmlEncode}
    +Time Stamp: {version.time|xmlEncode}
    +Revision: {version.number}

    +#-- DBINFO db.new +Database Instance Object: db
    +Database Schema Object: dbScheme
    +Database default access mode: updating
    +Database Schema Version: 01.10

    +#-- CLASSES class.new +

  • {class.name}
  • +#-- TABLES table.new +
  • {table.name}
  • +#-- TRANSACTIONS transaction.new +
  • {transaction.name}
  • +#-- END ofFile diff --git a/woc/pattern/html/meta.xml b/woc/pattern/html/meta.xml new file mode 100644 index 0000000..d2423c3 --- /dev/null +++ b/woc/pattern/html/meta.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + diff --git a/woc/pattern/html/table.html b/woc/pattern/html/table.html new file mode 100644 index 0000000..879bc13 --- /dev/null +++ b/woc/pattern/html/table.html @@ -0,0 +1,73 @@ +#== Table Definitions +#-- * table.new +Table {table.name} +

    Table {table.name}

    +

    {table.doc|trim|xmlEncode|oneLine(
    )}

    + +#-- * table.close +{#COLUMNS} +
    Column NameTypePropertiesDocu
    +{#ENUMS} +{#H_FOREIGN}{#FOREIGN}{#F_FOREIGN} +{#PRESETS} + +#== +#== Column descriptions +#-- CDEFAULT table.column.new clear +#-- CDEFAULT table.column.new if({table.column.default|isNotEmpty}) clear +default="{table.column.default|xmlEncode}" +#-- CFOREIGN table.column.new clear +#-- CFOREIGN table.column.new if({table.column.isforeignkey}) clear +Foreign-Key={table.column.foreignkeytable}({table.column.foreignkeycolumn}) +#-- CPROPS table.column.new +{table.column.primarykey|if(Primary-Key )} +{table.column.null|if(NULL-able )} +{#CDEFAULT|trim} +{#CFOREIGN|trim} +{table.column.isindexed|if(Indexed )} +{table.column.isunique|if(Unique )} +#-- COLUMNS table.column.new +{table.column.name}{table.column.type}{#CPROPS|linetrim|skipEmptyLines|oneLine}{table.column.doc|trim|xmlEncode|oneLine(
    )} +#== +#== enums +#-- ENUMS table.column.enum.new +

    Enum for column {table.column.name}

    +
      +#-- ENUMDOC table.column.enum.value if({table.column.enum.doc|isNotEmpty) clear +
      {table.column.enum.doc|trim|xmlEncode|oneLine(
      )} +#-- ENUMDOC table.column.enum.value else clear +#-- ENUMS table.column.enum.value +
    • {table.column.enum.name}={table.column.enum.numvalue}{#ENUMDOC|trim}
    • +#-- ENUMS table.column.enum.close +
    +#== +#== foreign getters +#-- H_FOREIGN table.foreigncall clear +

    Foreign Getters

    +
      + +#-- F_FOREIGN table.foreigncall clear + +
    +#-- FOREIGN table.foreigncall +
  • Column {table.foreigncall.column} fetches {table.foreigncall.foreigntable}({table.foreigncall.foreigncolumn}){table.foreigncall.doc|isNotEmpty|if(: )}{table.foreigncall.doc|trim|xmlEncode}
  • +#== +#== Presets +#-- PCOLS table.column.new +{table.column.name} +#-- PRESETS table.preset.new +

    Presets

    + +{#PCOLS|trim|linetrim|oneLine()} +#-- PDATA table.preset.row.new clear +#-- PDATA table.preset.row.column if({table.preset.row.isset} && !{table.preset.row.iscode}) + +#-- PDATA table.preset.row.column else if({table.preset.row.isset} && {table.preset.row.iscode}) + +#-- PDATA table.preset.row.column else + +#-- PRESETS table.preset.row.close +{#PDATA|trim|linetrim|oneLine()} +#-- PRESETS table.preset.close +
    "{table.preset.row.value}"{table.preset.row.code}-
    +#-- END ofFile diff --git a/woc/pattern/html/transaction.html b/woc/pattern/html/transaction.html new file mode 100644 index 0000000..b635776 --- /dev/null +++ b/woc/pattern/html/transaction.html @@ -0,0 +1,52 @@ +#== Generate Docu for Transactions +#-- * transaction.new +Transaction {transaction.name} +

    Transaction {transaction.name}

    +

    Authentication mode: Checked (known user, must have the privilege)

    +

    Database access mode: reading

    +

    This transaction allows to make a complete database backup. The backup file is replayed into the database via the admin.php interface. + This is the old-style backup interface, it is obsolete as of 2016.

    +#-- * transaction.close +

    Inputs:

    +
      +{#INPUT} +
    +

    Outputs:

    +
      +{#OUTPUT} +
    +{#H_PRIVS}{#PRIVS}{#F_PRIVS} + +#== +#== Inputs +#-- ITYPE transaction.input.value if({transaction.input.isobject}) clear +{transaction.input.type} +#-- ITYPE transaction.input.value else clear +{transaction.input.type} +#-- IDOC transaction.input.value if({transaction.input.doc|isEmpty}) clear +#-- IDOC transaction.input.value else clear +
    {transaction.input.doc|trim|linetrim|xmlEncode|oneLine(
    )} +#-- INPUT transaction.input.value +
  • {transaction.input.name}: {#ITYPE|trim}{#IDOC|trim}
  • +#== +#== Outputs +#-- OTYPE transaction.output.value if({transaction.output.isobject}) clear +{transaction.output.type} +#-- OTYPE transaction.output.value else clear +{transaction.output.type} +#-- ODOC transaction.output.value if({transaction.output.doc|isEmpty}) clear +#-- ODOC transaction.output.value else clear +
    {transaction.output.doc|trim|linetrim|xmlEncode|oneLine(
    )} +#-- OUTPUT transaction.output.value +
  • {transaction.output.name}: {#OTYPE|trim}{#ODOC|trim}
  • +#== +#== Privileges +#-- H_PRIVS transaction.privilege clear +

    Privileges

    +
      +#-- F_PRIVS transaction.privilege clear +
    +#-- PRIVS transaction.privilege +
  • {transaction.privilege.name}{transaction.privilege.doc|isNotEmpty|if(
    )}{transaction.privilege.doc|trim|linetrim|xmlEncode|oneLine(
    )}
  • +#== +#-- END ofFile diff --git a/woc/pattern/qt5/client/class-include b/woc/pattern/qt5/client/class-include new file mode 100644 index 0000000..8cc3af5 --- /dev/null +++ b/woc/pattern/qt5/client/class-include @@ -0,0 +1,2 @@ +//-- * class.new +#include "src${class.classname}.h" diff --git a/woc/pattern/qt5/client/class.cpp b/woc/pattern/qt5/client/class.cpp new file mode 100644 index 0000000..9a63b64 --- /dev/null +++ b/woc/pattern/qt5/client/class.cpp @@ -0,0 +1,152 @@ +//== Source File for Class Implementations +//-- * class.new +//BEGIN OF AUTOMATICALLY GENERATED FILE +//DO NOT EDIT THIS FILE DIRECTLY, USE THE XML SOURCE! +#include "${class.classname}" +#include +#include +#include +QString ${class.classname}::toString() +{ + QDomDocument doc; + doc.appendChild(toXml(doc)); + return doc.toString(); +} +QDomElement ${class.classname}::toXml(QDomDocument&doc,QString name) +{ + QDomElement r=doc.createElement(name); + toXml(doc,r); + return r; +} +//-- * class.close +void ${class.classname}::toXml(QDomDocument&doc,QDomElement&r) +{ + ${class.baseclassname}::toXml(doc,r); +${#TOXML} +} +MOAddressAbstract::MOAddressAbstract(const QDomElement&root) + :WObject(root) +{ + QList nl; +${#FROMXML} +} +${class.classname} ${class.classname}::fromXml(const QDomElement&root){return MOAddressAbstract(root);} +${class.classname} ${class.classname}::fromString(const QString&txt) +{ + QDomDocument doc; + if(!doc.setContent(txt))return ${class.classname}(); + else return ${class.classname}(doc.documentElement()); +} +static int class_metatypeid= qRegisterMetaType<${class.classname}>()+ qRegisterMetaType >()+ qRegisterMetaType >(); +static bool class_converter=QMetaType::registerConverter,${class.classname}>([](const Nullable<${class.classname}>&n){return n.value();})| +QMetaType::registerConverter,QVariantList>([](const QList<${class.classname}>&n){QVariantList r;for(auto v:n)r<0){ + setname(nl.at(0).toElement().text()); + } + nl=elementsByTagName(root,"addr1"); + if(nl.size()>0){ + setaddr1(nl.at(0).toElement().text()); + } + nl=elementsByTagName(root,"addr2"); + if(nl.size()>0){ + setaddr2(nl.at(0).toElement().text()); + } + nl=elementsByTagName(root,"city"); + if(nl.size()>0){ + setcity(nl.at(0).toElement().text()); + } + nl=elementsByTagName(root,"state"); + if(nl.size()>0){ + setstate(nl.at(0).toElement().text()); + } + nl=elementsByTagName(root,"zipcode"); + if(nl.size()>0){ + setzipcode(nl.at(0).toElement().text()); + } + if(root.hasAttribute("countryid")){ + setcountryid(root.attribute("countryid")); + } + if(root.hasAttribute("isdeleted")){ + setisdeleted(str2bool(root.attribute("isdeleted"))); + } + nl=elementsByTagName(root,"country"); + if(nl.size()>0){ + setcountry(MOCountry(nl.at(0).toElement())); + } diff --git a/woc/pattern/qt5/client/class.h b/woc/pattern/qt5/client/class.h new file mode 100644 index 0000000..e19dc10 --- /dev/null +++ b/woc/pattern/qt5/client/class.h @@ -0,0 +1,112 @@ +//== Header File for class implementations +//-- * class.new +//BEGIN OF AUTOMATICALLY GENERATED FILE +//DO NOT EDIT THIS FILE DIRECTLY, USE THE XML SOURCE! +#ifndef ${includeGuard} +#define ${includeGuard} + +${defineExportMacro} + +#include "WObject" +#include + +//-- * class.close +${#INCLUDETYPES|sort|unique} +#include "MOCountry" + + +class ${exportmacro} ${class.classname} : public ${class.baseclassname} +{ + Q_GADGET + public: +${#ENUMDEF} +${#PROPDEF} + protected: +${#PROPVAR} + public: +${#PROPGET} +${#PROPSET} + public: + /// Serializes the object to XML and returns the string representation of that XML. + QString toString(); + /// Transforms the object into its XML representation, the element node returned is not appended to the document - you have to do that yourself. + /// \param doc the DOM document node for which to generate the element + /// \param name the name to give the generated element, per default "${class.name}" is used + QDomElement toXml(QDomDocument&doc,QString name="${class.name}"); + /// Serializes the object into the given element. + void toXml(QDomDocument&,QDomElement&); + public: + /// default constructor: constructs an invalid instance of ${class.classname} + ${class.classname}():${class.baseclassname}(){} + /// copy constructor: creates a (deep) copy of the object + ${class.classname}(const ${class.classname}&)=default; + /// move constructor: moves the object + ${class.classname}(${class.classname}&&)=default; + /// copy assignment: creates a (deep) copy of the object + ${class.classname}& operator=(const ${class.classname}&)=default; + /// move operator: moves the object data + ${class.classname}& operator=(${class.classname}&&)=default; + /// special constructor: create from the XML representation, deserializing the object + explicit ${class.classname}(const QDomElement&); + ///create ${class.classname} from XML (inverse of toXml) + static ${class.classname} fromXml(const QDomElement&); + ///create ${class.classname} from XML formatted string (inverse of toString) + static ${class.classname} fromString(const QString&); + /// destructor: deletes this copy of the object + virtual ~${class.classname}(){} + +}; +Q_DECLARE_METATYPE(${class.classname}) +Q_DECLARE_METATYPE(QList<${class.classname}>) +Q_DECLARE_METATYPE(Nullable<${class.classname}>) + +//END OF AUTOMATICALLY GENERATED FILE +#endif + +//== ======================================================== +//-- PROPDEF class.property if(${class.property.islist|negate}) +${class.property.doc|linetrim|prepend(/// )|indent(8)} + Q_PROPERTY(Nullable<${class.property.maptype}> ${class.property.name} READ ${class.property.name} WRITE set${class.property.name}) +//-- PROPDEF class.property if(${class.property.islist}) +${class.property.doc|linetrim|prepend(/// )|indent(8)} + Q_PROPERTY(${class.property.maptype} ${class.property.name} READ ${class.property.name} WRITE set${class.property.name}) +//-- PROPVAR class.property if(${class.property.islist|negate}) + Nullable<${class.property.maptype}> mp_${class.property.name}; +//-- PROPVAR class.property if(${class.property.islist}) + ${class.property.maptype} mp_${class.property.name}; +//-- PROPGET class.property if(${class.property.islist|negate}) +${class.property.doc|linetrim|prepend(/// )|indent(8)} + virtual Nullable<${class.property.maptype}> ${class.property.name}()const{return mp_${class.property.name};} +//-- PROPGET class.property if(${class.property.islist}) +${class.property.doc|linetrim|prepend(/// )|indent(8)} + virtual ${class.property.maptype} ${class.property.name}()const{return mp_${class.property.name};} +//-- PROPSET class.property if(${class.property.islist|negate}) +${class.property.doc|linetrim|prepend(/// )|indent(8)} + virtual void set${class.property.name}(Nullable<${class.property.maptype}> s){mp_${class.property.name}=s;} +//-- PROPSET class.property if(${class.property.islist}) +${class.property.doc|linetrim|prepend(/// )|indent(8)} + virtual void set${class.property.name}(${class.property.maptype} s){mp_${class.property.name}=s;} + virtual void clear${class.property.name}(){mp_${class.property.name}.clear();} + virtual void add${class.property.name}(Nullable<${class.property.elementmaptype}> a){mp_${class.property.name}.append(a);} +//== TODO: what to do about abstract and ID props? +//== ======================================================== +//-- ENUMDEF class.enum.new +Q_ENUMS(OrderState); +${class.enum.doc|linetrim|prepend(/// )|indent(8)} + enum ${class.enum.name}{ +//-- ENUMDEF class.enum.value +${class.enum.doc|linetrim|prepend(/// )|indent(12)} + ${class.enum.value.name}=${class.enum.numvalue}, +//-- ENUMDEF class.enum.close + }; + /// Converts string into the corresponding enum ${class.enum.name} value. + static ${class.enum.name} str2${class.enum.name}(QString,bool*ok=0); + /// Converts enum ${class.enum.name} value into the corresponding string. + static QString ${class.enum.name}2str(${class.enum.name}); + /// Converts a localized string into the corresponding enum ${class.enum.name} value. + static ${class.enum.name} locstr2${class.enum.name}(QString,bool*ok=0); + /// Converts enum ${class.enum.name} value into the corresponding localized string. + static QString ${class.enum.name}2locstr(${class.enum.name}); +//== ======================================================== +//== TODO mappings +//-- END ofFile diff --git a/woc/pattern/qt5/client/includeAll.h b/woc/pattern/qt5/client/includeAll.h new file mode 100644 index 0000000..721ba96 --- /dev/null +++ b/woc/pattern/qt5/client/includeAll.h @@ -0,0 +1,21 @@ +//=============================== +//== general include file +//=============================== +//-- * project.new +//BEGIN OF AUTOMATICALLY GENERATED FILE +//DO NOT EDIT THIS FILE DIRECTLY, USE THE XML SOURCE! +#ifndef WOBGEN_${allClassPrefix}INCLUDE_H +#define WOBGEN_${allClassPrefix}INCLUDE_H + +//=============================== +//-- * project.close + +//END OF AUTOMATICALLY GENERATED FILE +#endif +//=============================== +//-- * class.new +#include "src${class.classname}.h" +//=============================== +//-- * transaction.new +#include "src${transaction.classname}.h" +//-- END project.end diff --git a/woc/pattern/qt5/client/interface-include b/woc/pattern/qt5/client/interface-include new file mode 100644 index 0000000..fbfafd6 --- /dev/null +++ b/woc/pattern/qt5/client/interface-include @@ -0,0 +1,2 @@ +//-- * interface.new +#include "src${interface.classname}.h" diff --git a/woc/pattern/qt5/client/interface.h b/woc/pattern/qt5/client/interface.h new file mode 100644 index 0000000..a78fbe1 --- /dev/null +++ b/woc/pattern/qt5/client/interface.h @@ -0,0 +1,85 @@ +//=============================== +//== Interface Header File definition +//=============================== +//-- * interface.new +//BEGIN OF AUTOMATICALLY GENERATED FILE +//DO NOT EDIT THIS FILE DIRECTLY, USE THE XML SOURCE! +#ifndef ${includeGuard} +#define ${includeGuard} + +#include "WInterface" +#include + +${defineExportMacro} + +//=============================== +//-- * interface.close +${#PREDEFS} + +/// ${project.name} interface class. +class ${exportmacro} ${interface.classname} : public WInterface +{ + Q_OBJECT + public: + ${interface.classname}(QString name=${project.name|toCString}):WInterface(name){} + /// convenience override: returns a pointer to an instance with the given name if it is of the correct type, otherwise nullptr + static ${interface.classname}*instance(QString name=${project.name|toCString}) + {return qobject_cast(WInterface::instance(name));} + + /// returns version information of this interface + Q_INVOKABLE static QString staticVersionInfo(WOb::VersionInfo); + /// returns version information about the WOC that created the interface class + Q_INVOKABLE static QString staticWocVersionInfo(WOb::VersionInfo); + /// returns version information of this interface + Q_INVOKABLE QString versionInfo(WOb::VersionInfo)const; + /// returns version information about the WOC that created the interface class + Q_INVOKABLE QString wocVersionInfo(WOb::VersionInfo)const; +${#TRANSCALL} + Q_ENUMS(Right) + /// This enum represents transactions and the right to use them. + enum Right { + /// dummy as a fall back for no transaction + NoRight, +${#RIGHTS} +${#PRIVILEGES} + }; + typedef QList RightList; + ///converts a right enum to its string representation + Q_INVOKABLE static QString rightToString(Right); + ///converts a right enum to its localized string representation + Q_INVOKABLE static QString rightToLocalString(Right); + ///converts a string to a matching right enum, returns NoRight if the string does not match + Q_INVOKABLE static Right stringToRight(QString); + ///converts a localized string to a matching right enum, returns NoRight if the string does not match + Q_INVOKABLE static QStringList allKnownRightsString(); + ///returns a list of all known rights/transactions + Q_INVOKABLE static ${interface.classname}::RightList allKnownRights(); +}; +Q_DECLARE_METATYPE(${interface.classname}::RightList) +Q_DECLARE_METATYPE(QList<${interface.classname}::RightList>) + +//END OF AUTOMATICALLY GENERATED FILE +#endif +//=============================== +//-- PREDEFS class.new +class ${class.classname}; +//=============================== +//-- PREDEFS class.new if(${class.abstract}) +class ${class.classname}Abstract; +//=============================== +//-- PREDEFS transaction.new +class ${transaction.classname}; +//=============================== +//-- TRANSCALLARG transaction.input.new clear +//-- TRANSCALLARG transaction.input.value +const ${transaction.input.maptype} &a${transaction.input.name} +//-- TRANSCALL transaction.close + /// convenience call to query ${transaction.classname} synchronously + ${transaction.classname} query${transaction.name}(${#TRANSCALLARG|trim|oneLine(,)}); +//-- RIGHTS transaction.new +${transaction.doc|linetrim|prepend(/// )|indent(4)} + R${transaction.name}, +//-- PRIVILEGES transaction.privilege +${transaction.privilege.doc|linetrim|prepend(/// )|indent(4)} + P${transaction.name}_${transaction.privilege.name}, +//-- END project.end diff --git a/woc/pattern/qt5/client/meta.xml b/woc/pattern/qt5/client/meta.xml new file mode 100644 index 0000000..c90a3ec --- /dev/null +++ b/woc/pattern/qt5/client/meta.xml @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/woc/pattern/qt5/client/staticVersion.h b/woc/pattern/qt5/client/staticVersion.h new file mode 100644 index 0000000..2f19c81 --- /dev/null +++ b/woc/pattern/qt5/client/staticVersion.h @@ -0,0 +1,72 @@ +//-- * version.new +//BEGIN OF AUTOMATICALLY GENERATED FILE +//DO NOT EDIT THIS FILE DIRECTLY, USE THE XML SOURCE! +#ifndef WOBGEN_MSTATIC_VERSION_H +#define WOBGEN_MSTATIC_VERSION_H + +#include +#include +#include + +static inline QString WOCgenerated_versionInfo(WOb::VersionInfo vi){ +switch(vi){ + case WOb::VersionAuthor:return ${version.author|toCString}; + case WOb::VersionComm:return ${version.comm|toCString}; + case WOb::VersionGenTime:return ${version.gentime|toCString}; + case WOb::VersionHR:return ${version.human|toCString}; + case WOb::VersionLocallyModified:return ${version.modified|toCString}; + case WOb::VersionNeedComm:return ${version.needComm|toCString}; + case WOb::VersionNumber:return ${version.number|toCString}; + case WOb::VersionPath:return ${version.path|toCString}; + case WOb::VersionRootURL:return ${version.rootUrl|toCString}; + case WOb::VersionSystem:return ${version.system|toCString}; + case WOb::VersionTime:return ${version.time|toCString}; + default:return QString(); +}} +static inline void WOCgenerated_versionInfo(QMap&vi){ +vi.clear(); + vi.insert("Author",${version.author|toCString}); + vi.insert("Comm",${version.comm|toCString}); + vi.insert("GenTime",${version.gentime|toCString}); + vi.insert("HR",${version.human|toCString}); + vi.insert("LocallyModified",${version.modified|toCString}); + vi.insert("NeedComm",${version.needComm|toCString}); + vi.insert("Number",${version.number|toCString}); + vi.insert("Path",${version.path|toCString}); + vi.insert("RootURL",${version.rootUrl|toCString}); + vi.insert("System",${version.system|toCString}); + vi.insert("Time",${version.time|toCString}); +} +static inline QString WOCcopied_versionInfo(WOb::VersionInfo vi){ +switch(vi){ + case WOb::VersionAuthor:return ${version.woc.author|toCString}; + case WOb::VersionComm:return ""; + case WOb::VersionGenTime:return ${version.woc.gentime|toCString}; + case WOb::VersionHR:return ${version.woc.human|toCString}; + case WOb::VersionLocallyModified:return ${version.woc.modified|toCString}; + case WOb::VersionNeedComm:return ""; + case WOb::VersionNumber:return ${version.woc.number|toCString}; + case WOb::VersionPath:return ${version.woc.path|toCString}; + case WOb::VersionRootURL:return ${version.woc.rootUrl|toCString}; + case WOb::VersionSystem:return ${version.woc.system|toCString}; + case WOb::VersionTime:return ${version.woc.time|toCString}; + default:return QString(); +}} +static inline void WOCcopied_versionInfo(QMap&vi){ + vi.clear(); + vi.insert("Author",${version.woc.author|toCString}); + vi.insert("Comm",""); + vi.insert("GenTime",${version.woc.gentime|toCString}); + vi.insert("HR",${version.woc.human|toCString}); + vi.insert("LocallyModified",${version.woc.modified|toCString}); + vi.insert("NeedComm",""); + vi.insert("Number",${version.woc.number|toCString}); + vi.insert("Path",${version.woc.path|toCString}); + vi.insert("RootURL",${version.woc.rootUrl|toCString}); + vi.insert("System",${version.woc.system|toCString}); + vi.insert("Time",${version.woc.time|toCString}); +} + +//END OF AUTOMATICALLY GENERATED FILE +#endif +//-- END ofFile diff --git a/woc/pattern/qt5/client/transaction-include b/woc/pattern/qt5/client/transaction-include new file mode 100644 index 0000000..33f9b4c --- /dev/null +++ b/woc/pattern/qt5/client/transaction-include @@ -0,0 +1,2 @@ +//-- * transaction.new +#include "src${transaction.classname}.h" diff --git a/woc/pattern/qt5/client/wob.pri b/woc/pattern/qt5/client/wob.pri new file mode 100644 index 0000000..5cf3f25 --- /dev/null +++ b/woc/pattern/qt5/client/wob.pri @@ -0,0 +1,20 @@ +//-- * project.new +#AUTOMATICALLY GENERATED FILE - DONT CHANGE! +INCLUDEPATH += $$PWD +DEFINES += ${exportmacro}=Q_DECL_EXPORT +//-- * project.close +${#CLASS} +${#TRANSACTION} +${#INTERFACE} + +#END OF AUTOGENERATED PRI FILE +//-- CLASS class.new +HEADERS+=$$PWD/src${class.classname}.h +SOURCES+=$$PWD/src${class.classname}.cpp +//-- TRANSACTION transaction.new +HEADERS+=$$PWD/src${transaction.classname}.h +SOURCES+=$$PWD/src${transaction.classname}.cpp +//-- INTERFACE interface.new +HEADERS+=$$PWD/src${interface.classname}.h +SOURCES+=$$PWD/src${interface.classname}.cpp +//-- END ofFile diff --git a/woc/php/php.pri b/woc/php/php.pri index 93f22a7..b2c5ef5 100644 --- a/woc/php/php.pri +++ b/woc/php/php.pri @@ -1,16 +1,16 @@ SOURCES+= \ - php/phpout.cpp \ - php/phpclass.cpp \ - php/phptrans.cpp \ - php/phpctrans.cpp \ - php/phpstrans.cpp \ - php/phpdb.cpp + $$PWD/phpout.cpp \ + $$PWD/phpclass.cpp \ + $$PWD/phptrans.cpp \ + $$PWD/phpctrans.cpp \ + $$PWD/phpstrans.cpp \ + $$PWD/phpdb.cpp HEADERS+= \ - php/phpout.h \ - php/phpdb.h \ - php/phpclass.h \ - php/phptrans.h \ - php/phpctrans.h \ - php/phpstrans.h + $$PWD/phpout.h \ + $$PWD/phpdb.h \ + $$PWD/phpclass.h \ + $$PWD/phptrans.h \ + $$PWD/phpctrans.h \ + $$PWD/phpstrans.h -INCLUDEPATH += php +INCLUDEPATH += $$PWD diff --git a/woc/proc/proc.pri b/woc/proc/proc.pri index e936da0..e9dcd88 100644 --- a/woc/proc/proc.pri +++ b/woc/proc/proc.pri @@ -1,16 +1,16 @@ SOURCES+= \ - proc/processor.cpp \ - proc/procclass.cpp \ - proc/proctrans.cpp \ - proc/proctable.cpp + $$PWD/processor.cpp \ + $$PWD/procclass.cpp \ + $$PWD/proctrans.cpp \ + $$PWD/proctable.cpp HEADERS+= \ - proc/processor.h \ - proc/procclass.h \ - proc/proctrans.h \ - proc/proctable.h + $$PWD/processor.h \ + $$PWD/procclass.h \ + $$PWD/proctrans.h \ + $$PWD/proctable.h CONFIG(prewoc, prewoc|woc){ - RESOURCES += proc/version.qrc + RESOURCES += $$PWD/version.qrc } -INCLUDEPATH += proc \ No newline at end of file +INCLUDEPATH += $$PWD diff --git a/woc/proc/procclass.cpp b/woc/proc/procclass.cpp index 22a65e9..60bf515 100644 --- a/woc/proc/procclass.cpp +++ b/woc/proc/procclass.cpp @@ -5,9 +5,11 @@ #include "processor.h" -#include "phpout.h" #include "qtout.h" +#ifndef COMPILE_PREWOC +#include "phpout.h" #include "htmlout.h" +#endif #include "domquery.h" diff --git a/woc/proc/processor.cpp b/woc/proc/processor.cpp index f7c6b23..9db04c5 100644 --- a/woc/proc/processor.cpp +++ b/woc/proc/processor.cpp @@ -5,11 +5,14 @@ #include "processor.h" -#include "phpout.h" #include "qtout.h" +#ifndef COMPILE_PREWOC +#include "phpout.h" #include "htmlout.h" #include "schemaout.h" #include "soapout.h" +#endif +#include "genproc.h" #include "domquery.h" @@ -178,6 +181,7 @@ bool WocProcessor::processFile(QString fn) new WocQtClientOut(el); if(m_error)return false; }else +#ifndef COMPILE_PREWOC if(tn=="QtServerOutput"){ new WocQtServerOut(el); if(m_error)return false; @@ -202,6 +206,7 @@ bool WocProcessor::processFile(QString fn) new WocSoapOut(el); if(m_error)return false; }else +#endif if(tn=="Version"){ if(el.hasAttribute("system")) m_verSys=el.attribute("system").split(" ",QString::SkipEmptyParts); @@ -239,9 +244,14 @@ bool WocProcessor::processFile(QString fn) if(tn=="Doc"){ QString s=el.text().trimmed(); if(s!="")m_docstrings<ps; - QList nl2=elementsByTagName(el,"V"); + QMapps; + QMapps2; + QList nl2=elementsByTagName(el,"V"); for(int j=0;j0)m_presets.append(ps); + if(ps2.size()>0)m_presets2.append(ps2); } //Docu @@ -435,7 +440,7 @@ WocTable WocTable::auditTable()const 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_name=auditTableName();//enhance the name adt.m_base="WobTable";//revert to default adt.m_foreign=m_foreign; adt.m_fordocs=m_fordocs; diff --git a/woc/proc/proctable.h b/woc/proc/proctable.h index 2ca846f..f8ddcb2 100644 --- a/woc/proc/proctable.h +++ b/woc/proc/proctable.h @@ -1,4 +1,4 @@ -// Copyright (C) 2009-2011 by Konrad Rosenbaum +// Copyright (C) 2009-2018 by Konrad Rosenbaum // protected under the GNU GPL version 3 or at your option any newer. // See COPYING.GPL file that comes with this distribution. // @@ -67,7 +67,15 @@ class WocTable QList 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 true if the column name is for an audit column + bool columnIsAudit(QString col)const{return col=="auditid" || auditColumns().contains(col);} + ///returns true if the column has a string type + bool columnIsString(QString col)const{const QString t=columnType(col);return t=="string"||t=="text"||t.startsWith("string:");} + ///returns true if the column has an int type + bool columnIsInt(QString col)const{const QString t=columnType(col);return t.startsWith("int")||t.startsWith("seq")||t.startsWith("enum");} + ///returns true if the column has a blob type + bool columnIsBlob(QString col)const{return columnType(col)=="blob";} + /**returns all enum definitions of the table; see also columnEnums */ QList getEnums()const; @@ -79,8 +87,22 @@ class WocTable 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 > presets()const{return m_presets;} + 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 + \obsolete This method is obsolete, use presetList() */ + QT_DEPRECATED QList > presets()const{return m_presets;} + ///Preset value + struct Preset { + QString column,value,call; + Preset(){} + Preset(QString col,QString val,QString ca):column(col),value(val),call(ca){} + Preset(const Preset&)=default; + Preset(Preset&&)=default; + Preset& operator=(const Preset&)=default; + Preset& operator=(Preset&&)=default; + }; + /**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> presetList()const{return m_presets2;} /**parses the static part of auditing*/ static void parseAuditStatic(const QDomElement&); @@ -90,6 +112,8 @@ class WocTable WocTable auditTable()const; /**returns the names of audit columns (except auditid)*/ QStringList auditColumns()const; + ///returns the name of the corresponding audit table + QString auditTableName()const{if(m_audit)return m_name+"_audit";else return QString();} ///returns all complex Unique constraints (those not defined for a single column) QStringList uniqueConstraints()const{return m_uniquecols;} @@ -116,6 +140,7 @@ class WocTable static QListm_staticauditcolumns; QList >m_foreign; QList >m_presets; + QList> m_presets2; int m_backupsize=-1; QStringList m_docstrings,m_uniquecols; diff --git a/woc/proc/proctrans.cpp b/woc/proc/proctrans.cpp index 0a83897..b80f2dd 100644 --- a/woc/proc/proctrans.cpp +++ b/woc/proc/proctrans.cpp @@ -5,9 +5,11 @@ #include "processor.h" -#include "phpout.h" #include "qtout.h" +#ifndef COMPILE_PREWOC +#include "phpout.h" #include "htmlout.h" +#endif #include "domquery.h" diff --git a/woc/proc/version.wolf b/woc/proc/version.wolf index cdc169d..e20b359 100644 --- a/woc/proc/version.wolf +++ b/woc/proc/version.wolf @@ -2,8 +2,8 @@ - + - \ No newline at end of file + diff --git a/woc/qrcgen/qrcgen.cpp b/woc/qrcgen/qrcgen.cpp new file mode 100644 index 0000000..80d3e30 --- /dev/null +++ b/woc/qrcgen/qrcgen.cpp @@ -0,0 +1,47 @@ +#include +#include +#include + +#define PROLOG "\n\ +\n\ +\n\n" + +#define EPILOG "\n" + +void writeOut(QFile&out,QString source,QString target) +{ + QDir dir(source); + qDebug()<<"Scanning directory"<%2/%1\n").arg(fn).arg(source).arg(target).toLatin1()); + } +} + +int main(int ac,char**av) +{ + QCoreApplication app(ac,av); + qDebug()<<"Generatin QRCs from"< ",app.arguments().at(0).toLocal8Bit().data()); + return 1; + } + const QStringList pathes=app.arguments().mid(1); + if(!pathes[2].endsWith(".qrc")){ + qFatal("Resource File Name %s is invalid - resource file name must end in .qrc", pathes[2].toLatin1().data()); + return 1; + } + QFile out(pathes[2]); + if(!out.open(QIODevice::WriteOnly|QIODevice::Truncate)){ + qFatal("Unable to create %s - giving up.", pathes[2].toLatin1().data()); + return 1; + } + out.write(PROLOG); + writeOut(out,pathes[0],pathes[1]); + out.write(EPILOG); + out.close(); + return 0; +} diff --git a/woc/qrcgen/qrcgen.pri b/woc/qrcgen/qrcgen.pri new file mode 100644 index 0000000..0abca15 --- /dev/null +++ b/woc/qrcgen/qrcgen.pri @@ -0,0 +1,13 @@ +#Include this file to be able to automatically generate QRC files from directories +# +# Setting this variable: +# GRESOURCES += directory +# will auto-generate directory.qrc and include it into the final binary. + + +qrcgen.output = ${QMAKE_FILE_NAME}.qrc +qrcgen.variable_out = RESOURCES +qrcgen.commands = $$PWD/qrcgen ${QMAKE_FILE_NAME} ${QMAKE_FILE_NAME} ${QMAKE_FILE_NAME}.qrc +qrcgen.depend_command = find ${QMAKE_FILE_NAME} -type f +qrcgen.input = GRESOURCES +QMAKE_EXTRA_COMPILERS += qrcgen diff --git a/woc/qrcgen/qrcgen.pro b/woc/qrcgen/qrcgen.pro new file mode 100644 index 0000000..80f57fd --- /dev/null +++ b/woc/qrcgen/qrcgen.pro @@ -0,0 +1,7 @@ +TEMPLATE = app +TARGET = qrcgen +SOURCES += qrcgen.cpp +DESTDIR = ../qrcgen +QT -= gui +CONFIG += console +CONFIG -= app_bundle diff --git a/woc/qt/qt.pri b/woc/qt/qt.pri index b64e5cf..2e6fd5b 100644 --- a/woc/qt/qt.pri +++ b/woc/qt/qt.pri @@ -1,15 +1,15 @@ SOURCES+= \ - qt/qtout.cpp \ - qt/qtclass.cpp \ - qt/qtdb.cpp \ - qt/qtctrans.cpp \ - qt/qtstrans.cpp + $$PWD/qtout.cpp \ + $$PWD/qtclass.cpp \ + $$PWD/qtdb.cpp \ + $$PWD/qtctrans.cpp \ + $$PWD/qtstrans.cpp HEADERS+= \ - qt/qtout.h \ - qt/qtclass.h \ - qt/qtdb.h \ - qt/qtctrans.h \ - qt/qtstrans.cpp + $$PWD/qtout.h \ + $$PWD/qtclass.h \ + $$PWD/qtdb.h \ + $$PWD/qtctrans.h \ + $$PWD/qtstrans.cpp -INCLUDEPATH += qt +INCLUDEPATH += $$PWD diff --git a/woc/soap/soap.pri b/woc/soap/soap.pri index 080b0a2..aa20664 100644 --- a/woc/soap/soap.pri +++ b/woc/soap/soap.pri @@ -1,11 +1,11 @@ -RESOURCES += soap/schemafiles.qrc +RESOURCES += $$PWD/schemafiles.qrc SOURCES += \ - soap/schemaout.cpp \ - soap/soapout.cpp + $$PWD/schemaout.cpp \ + $$PWD/soapout.cpp HEADERS += \ - soap/schemaout.h \ - soap/soapout.h + $$PWD/schemaout.h \ + $$PWD/soapout.h -INCLUDEPATH += soap \ No newline at end of file +INCLUDEPATH += $$PWD diff --git a/woc/test/generic/generic.pro b/woc/test/generic/generic.pro new file mode 100644 index 0000000..d591182 --- /dev/null +++ b/woc/test/generic/generic.pro @@ -0,0 +1,48 @@ +# Copyright (C) 2009-2018 by Konrad Rosenbaum +# protected under the GNU GPL version 3 or at your option any newer. +# See COPYING.GPL file that comes with this distribution. + +TEMPLATE=app +QT-=gui +QT+=xml testlib +CONFIG+=console debug +CONFIG-=app_bundle + +DEFINES += COMPILE_WOC +TARGET = ../woctest_generic +MYTMPDIR = .ctmp + +#compilation output: +OBJECTS_DIR = $$MYTMPDIR +MOC_DIR = $$MYTMPDIR +RCC_DIR = $$MYTMPDIR + + +SOURCES+= \ + ../../mfile.cpp \ + ../../domquery.cpp +HEADERS+= \ + ../../mfile.h \ + ../../domquery.h + +SOURCES+= woctest.cpp +HEADERS+= woctest.h + +include(../../proc/proc.pri) +include(../../generic/generic.pri) +GRESOURCES += testfiles +# TODO: remove +include(../../qt/qt.pri) +include(../../php/php.pri) +include(../../soap/soap.pri) +SOURCES+=../../htmlout.cpp +#END TODO + +include(../../qrcgen/qrcgen.pri) + +CONFIG += c++11 + +INCLUDEPATH += . ../../../vinfo ../../../qtbase/include ../.. + +#make sure dependencies are found +DEPENDPATH += $$INCLUDEPATH diff --git a/woc/test/generic/testfiles/conditional.pat b/woc/test/generic/testfiles/conditional.pat new file mode 100644 index 0000000..79bef11 --- /dev/null +++ b/woc/test/generic/testfiles/conditional.pat @@ -0,0 +1,63 @@ +//== this pattern tests chains of conditional sections +//-- * test.close +results: +{#CHAIN1|trim} +{#CHAIN2|trim} +{#CHAIN3|trim} +{#CHAIN4|trim} +{#CHAIN5|trim} +{#CHAIN6A|trim}-{#CHAIN6B|trim}-{#CHAIN6C|trim} +{#CHAIN7A|trim}-{#CHAIN7B|trim}-{#CHAIN7C|trim} +{#UNCO|trim} +//== +//== Chain 1: simple if, one section only +//-- CHAIN1 run1 if({run|contains[1]}) +C1 +//== +//== Chain 2: two sections, first triggers +//-- CHAIN2 run2 if({run|isNotEmpty}) +C2 +//-- CHAIN2 run2 else +C2E +//== Chain 3: same trigger different chain +//-- CHAIN3 run2 if(true) +C3 +//== Chain 3b: same trigger, same target, different chain +//-- CHAIN3 run2 if({run|isNotEmpty}) +C3B +//== unconditional addition +//-- UNCO run2 +U1 +//== +//== Chain 4: 3 sections, last else triggers +//-- CHAIN4 run3 if({run|isEmpty}) +C4E1 +//-- CHAIN4 run3 else if({run|contains[55]}) +C4E2 +//-- CHAIN4 run3 else +C4 +//== +//== Chain5: 3 sections, middle one triggers +//-- CHAIN5 run5 if({run|isEmpty}) +C5E1 +//-- CHAIN5 run5 else if({hello|toLower|contains[world]}) +C5 +//-- CHAIN5 run5 else +C5E2 +//== +//== Chain 6: different targets for same chain +//-- CHAIN6A run6 if({run|isEmpty}) +C6E1 +//-- CHAIN6B run6 else if({hello|toLower|contains[world]}) +C6 +//-- CHAIN6C run6 else +C6E2 +//== +//== Chain 6: different targets for same chain, all expressions true +//-- CHAIN7A run7 if({run|isNotEmpty}) +C7 +//-- CHAIN7B run7 else if({hello|toLower|contains[world]}) +C7E1 +//-- CHAIN7C run7 else if(true) +C7E2 +//-- END ofFile diff --git a/woc/test/generic/testfiles/conditional.res b/woc/test/generic/testfiles/conditional.res new file mode 100644 index 0000000..4f494e7 --- /dev/null +++ b/woc/test/generic/testfiles/conditional.res @@ -0,0 +1,10 @@ +results: +C1 +C2 +C3 +C3B +C4 +C5 +-C6- +C7-- +U1 diff --git a/woc/test/generic/testfiles/empty.pat b/woc/test/generic/testfiles/empty.pat new file mode 100644 index 0000000..e69de29 diff --git a/woc/test/generic/testfiles/empty.res b/woc/test/generic/testfiles/empty.res new file mode 100644 index 0000000..e69de29 diff --git a/woc/test/generic/testfiles/simple.pat b/woc/test/generic/testfiles/simple.pat new file mode 100644 index 0000000..affaf6b --- /dev/null +++ b/woc/test/generic/testfiles/simple.pat @@ -0,0 +1,16 @@ +//== this pattern tests some basics +//== invisible line +//-- * test.new +{file} +//-- * test.close +{#OTHER|trim} +//== skipped line +{#OTHER|trim|oneLine(,)} +//-- OTHER run1 +{run} +//-- OTHER run3 +{run} +three +//-- * after +this line should NOT appear in the result: the file should be closed by now +//-- END ofFile diff --git a/woc/test/generic/testfiles/simple.res b/woc/test/generic/testfiles/simple.res new file mode 100644 index 0000000..27a852f --- /dev/null +++ b/woc/test/generic/testfiles/simple.res @@ -0,0 +1,5 @@ +simple.pat +1 +3 +three +1,3,three diff --git a/woc/test/generic/testfiles/transform.pat b/woc/test/generic/testfiles/transform.pat new file mode 100644 index 0000000..0a3a2aa --- /dev/null +++ b/woc/test/generic/testfiles/transform.pat @@ -0,0 +1,51 @@ +//== this pattern tests all defined transformations +//-- * test.close +{var1|toUpper} +{hello|toLower} +{#XML|trim|xmlEncode} +{#URL|trim|urlEncode} +{#STR|trim|toCString} +{#MULTI|trim|oneLine} +{#MULTI|trim|oneLine(->)} +{#MULTI|trim|sort|oneLine} +{#MULTI|trim|unique|oneLine} +{#MULTI|trim|sort|unique|oneLine} + +{#NONE|isEmpty|if(e)(n)} +{#NONE|isNotEmpty|if(e)(n)} +{#STR|isEmpty|if(e)(n)} +{#STR|isNotEmpty|if(e)(n)} +{#MULTI|contains(aaa)|if(ya)(na)} +{#MULTI|contains(aaa)|negate|if(ya)(na)} + +{var2|replace(2)(two)} + +{#TWOS|trim|prepend(//)} + +{#TWOS|linetrim|indent(3)} +//-- XML run2 +<{run} /> +//-- URL run3 +x://y@z +//-- STR run4 +"{run}'x +//-- MULTI run1 +aaa +{run} +//-- MULTI run2 +{run} +//-- MULTI run3 +{run} +//-- MULTI run4 +{run} +//-- MULTI run5 +{run} +//-- MULTI run7 +aaa +aaa +//-- NONE run8 + +//-- TWOS run9 + one + two +//-- END ofFile diff --git a/woc/test/generic/testfiles/transform.res b/woc/test/generic/testfiles/transform.res new file mode 100644 index 0000000..b84d95a --- /dev/null +++ b/woc/test/generic/testfiles/transform.res @@ -0,0 +1,25 @@ +VALUE_1 +hello world! +<2 /> +x%3A%2F%2Fy%40z +"\"4\'x" +aaa 1 2 3 4 5 aaa aaa +aaa->1->2->3->4->5->aaa->aaa +1 2 3 4 5 aaa aaa aaa +aaa 1 2 3 4 5 aaa +1 2 3 4 5 aaa + +e +n +n +e +ya +na + +value_two + +//one +// two + + one + two diff --git a/woc/test/generic/woctest.cpp b/woc/test/generic/woctest.cpp new file mode 100644 index 0000000..7e19e8a --- /dev/null +++ b/woc/test/generic/woctest.cpp @@ -0,0 +1,168 @@ +// Copyright (C) 2018 by Konrad Rosenbaum +// protected under the GNU GPL version 3 or at your option any newer. +// See COPYING.GPL file that comes with this distribution. +// + +#include +#include "woctest.h" + +#include "generic/genvar.h" +#include "generic/genexpr.h" +#include "generic/genfile.h" + +void WocTest::genVarTest() +{ + WocSimpleVariables sv1(nullptr); + + QVERIFY(sv1.setVariable("v1","val1")); + QCOMPARE(sv1.getVariable("v1"),"val1"); + QCOMPARE(sv1.getVariable("xy"),""); + + QVERIFY(!sv1.setVariable("###","s")); + QCOMPARE(sv1.getVariable("###"),""); + QVERIFY(!sv1.setVariable("#v","s")); + QCOMPARE(sv1.getVariable("#v"),""); + + WocSimpleVariables sv2(&sv1); + QVERIFY(sv1.setVariable("v2","val2")); + QVERIFY(sv2.setVariable("v2","valB")); + QCOMPARE(sv1.getVariable("v2"),"val2"); + QCOMPARE(sv2.getVariable("v2"),"valB"); + QCOMPARE(sv2.getVariable("v1"),"val1"); + + WocSectionVariables sec(&sv1); + QVERIFY(sec.setVariable("#section","content")); + QVERIFY(!sec.setVariable("normal","blah")); + QCOMPARE(sec.getVariable("#section"),"content"); + QCOMPARE(sec.getVariable("v1"),"val1"); +} + +void WocTest::genExprStringTest() +{ + WocSimpleVariables sv(nullptr); + QPairmark1("{","}"); + sv.setVariable("mix","Hello World!"); + QCOMPARE(WocGenericExpression("{mix}",mark1,&sv).evaluateToString(),"Hello World!"); + QCOMPARE(WocGenericExpression("{mix|toLower}",mark1,&sv).evaluateToString(),"hello world!"); + QCOMPARE(WocGenericExpression("{mix|toUpper}",mark1,&sv).evaluateToString(),"HELLO WORLD!"); + QCOMPARE(WocGenericExpression("{mix|replace(l)(XX)}",mark1,&sv).evaluateToString(),"HeXXXXo WorXXd!"); + QCOMPARE(WocGenericExpression("{mix|replace(l)}",mark1,&sv).evaluateToString(),"Heo Word!"); + QCOMPARE(WocGenericExpression("{mix|replace[l][<((><]}",mark1,&sv).evaluateToString(),"He<((><<((>"); + QCOMPARE(WocGenericExpression("**{enc|xmlEncode}**",mark1,&sv).evaluateToString(),"**<jo at="tr"/>**"); + QCOMPARE(WocGenericExpression("**{enc|urlEncode}**",mark1,&sv).evaluateToString(),"**%3Cjo%20at%3D%22tr%22%2F%3E**"); + QCOMPARE(WocGenericExpression("**{enc|toCString}**",mark1,&sv).evaluateToString(),"**\"\"**"); + + sv.setVariable("ml","hello\nworld"); + QCOMPARE(WocGenericExpression("{ml}",mark1,&sv).evaluateToString(),"hello\nworld"); + QCOMPARE(WocGenericExpression("{ml|oneLine}",mark1,&sv).evaluateToString(),"hello world"); + QCOMPARE(WocGenericExpression("{ml|oneLine(,)}",mark1,&sv).evaluateToString(),"hello,world"); + QCOMPARE(WocGenericExpression("{ml|prepend(//)}",mark1,&sv).evaluateToString(),"//hello\n//world"); + QCOMPARE(WocGenericExpression("{ml|indent(3)}",mark1,&sv).evaluateToString()," hello\n world"); + + sv.setVariable("spc"," \t\n "); + sv.setVariable("wspc"," space\t"); + QCOMPARE(WocGenericExpression("{mix|isEmpty}",mark1,&sv).evaluateToString(),"false"); + QCOMPARE(WocGenericExpression("{mix|isNotEmpty}",mark1,&sv).evaluateToString(),"true"); + QCOMPARE(WocGenericExpression("{spc|isEmpty}",mark1,&sv).evaluateToString(),"true"); + QCOMPARE(WocGenericExpression("{spc|isNotEmpty}",mark1,&sv).evaluateToString(),"false"); + QCOMPARE(WocGenericExpression("{wspc|isEmpty}",mark1,&sv).evaluateToString(),"false"); + QCOMPARE(WocGenericExpression("{wspc|isNotEmpty}",mark1,&sv).evaluateToString(),"true"); + QCOMPARE(WocGenericExpression("{wspc|trim}",mark1,&sv).evaluateToString(),"space"); + QCOMPARE(WocGenericExpression("{wspc}",mark1,&sv).evaluateToString()," space\t"); + + sv.setVariable("t","on"); + sv.setVariable("f","no"); + QCOMPARE(WocGenericExpression("{t|negate}",mark1,&sv).evaluateToString(),"false"); + QCOMPARE(WocGenericExpression("{f|negate}",mark1,&sv).evaluateToString(),"true"); + QCOMPARE(WocGenericExpression("{t|if(yo)(nah)}",mark1,&sv).evaluateToString(),"yo"); + QCOMPARE(WocGenericExpression("{f|if(yo)(nah)}",mark1,&sv).evaluateToString(),"nah"); + QCOMPARE(WocGenericExpression("{t|if(yo)}",mark1,&sv).evaluateToString(),"yo"); + QCOMPARE(WocGenericExpression("{f|if(yo)}",mark1,&sv).evaluateToString(),""); + + QPairmark2("$[",")>"); + QCOMPARE(WocGenericExpression("{wspc}$[mix)>{mix}",mark2,&sv).evaluateToString(),"{wspc}Hello World!{mix}"); + + QCOMPARE(WocGenericExpression().evaluateToString(),""); + QCOMPARE(WocGenericExpression("{mix}",mark1,nullptr).evaluateToString(),""); +} + +void WocTest::genExprBoolTest() +{ + WocSimpleVariables sv(nullptr); + QPairmark1("{","}"); + sv.setVariable("mix","Hello World!"); + sv.setVariable("t","on"); + sv.setVariable("f","no"); + QCOMPARE(WocGenericExpression("{t}",mark1,&sv).evaluateToBool(),true); + QCOMPARE(WocGenericExpression("{f}",mark1,&sv).evaluateToBool(),false); + QCOMPARE(WocGenericExpression("yes",mark1,&sv).evaluateToBool(),true); + QCOMPARE(WocGenericExpression("no",mark1,&sv).evaluateToBool(),false); + QCOMPARE(WocGenericExpression("{t}||{f}",mark1,&sv).evaluateToBool(),true); + QCOMPARE(WocGenericExpression("{t}&&{f}",mark1,&sv).evaluateToBool(),false); + QCOMPARE(WocGenericExpression("{t}|| no",mark1,&sv).evaluateToBool(),true); + QCOMPARE(WocGenericExpression("{t}&& yes",mark1,&sv).evaluateToBool(),true); + QCOMPARE(WocGenericExpression("{t} && yes && {f|negate} || {f}",mark1,&sv).evaluateToBool(),true); + + QCOMPARE(WocGenericExpression("!{t}",mark1,&sv).evaluateToBool(),false); + QCOMPARE(WocGenericExpression("! {t}",mark1,&sv).evaluateToBool(),false); + QCOMPARE(WocGenericExpression("!{f}",mark1,&sv).evaluateToBool(),true); + QCOMPARE(WocGenericExpression("! {f}",mark1,&sv).evaluateToBool(),true); + QCOMPARE(WocGenericExpression("!!{f}",mark1,&sv).evaluateToBool(),false); + QCOMPARE(WocGenericExpression("!!{t}",mark1,&sv).evaluateToBool(),true); + + QCOMPARE(WocGenericExpression("!{f} && {t} && !no",mark1,&sv).evaluateToBool(),true); +} + +static inline QString getFile(QString fn) +{ + QFile fd(fn); + if(!fd.open(QIODevice::ReadOnly))return QString(); + QString r=QString::fromUtf8(fd.readAll()); + fd.close(); + return r.trimmed(); +} + +void WocTest::genFileTest() +{ + //environment + WocSimpleVariables sv(nullptr); + for(int i=0;i<10;i++)sv.setVariable(QString("var%1").arg(i),QString("value_%1").arg(i)); + sv.setVariable("hello","Hello World!"); + OutMock om; + //go through test files + QDir d(":/testfiles"); + for(QString fn:d.entryList(QStringList()<<"*.pat")){ + qDebug()<<"Testing pattern file"< +// protected under the GNU GPL version 3 or at your option any newer. +// See COPYING.GPL file that comes with this distribution. +// + +#include +#include "generic/genfile.h" +#include "generic/genout.h" + +class WocTest:public QObject +{ + Q_OBJECT +private slots: + void genVarTest(); + void genExprStringTest(); + void genExprBoolTest(); + void genFileTest(); +}; + +class OutMock:public WocGenericOutBase +{ +public: + OutMock(){} + QString outputBaseDir()const override{return ".";} + QString outputSubDir()const override{return ".";} + QString outputLanguage()const override{return "fake/client";} + + QString syntaxComment()const override{return "//==";} + QString syntaxSection()const override{return "//--";} + QString syntaxVariableStart()const override{return "{";} + QString syntaxVariableEnd()const override{return "}";} + QPair syntaxVariable()const override{return QPair("{","}");} + + QString outputTargetDir()const override{return ".";} + + QString mapType(QString n,const WocClass*context=nullptr)const override; +protected: + virtual void finalize()override{} + virtual void newClass(const WocClass&)override{} + virtual void newTransaction(const WocTransaction&)override{} + virtual void newTable(const WocTable&)override{} +}; diff --git a/woc/test/test.pro b/woc/test/test.pro new file mode 100644 index 0000000..878af0a --- /dev/null +++ b/woc/test/test.pro @@ -0,0 +1,2 @@ +TEMPLATE = subdirs +SUBDIRS = generic diff --git a/woc/woc.cpp b/woc/woc.cpp index 5194b7d..88fdab1 100644 --- a/woc/woc.cpp +++ b/woc/woc.cpp @@ -17,24 +17,32 @@ The Qt back-end is in the "qt" directory with WocQtOut, WocQtClientOut, and WocQ The PHP back-end is in the "php" directory with WocPHPOut, WocPHPClientOut, and WocPHPServerOut as the main output classes. +The generic pattern based back-end is in the "generic" directory with default patterns in the "pattern directory. */ #include #include +#include #include "processor.h" +#include "genproc.h" int main(int argc,char**argv) { QCoreApplication app(argc,argv); + //Initializations + InitializeGeneric(); //get arguments QStringList args=app.arguments(); args.removeFirst(); //process files WocProcessor proc; - for(int i=0;i +# Copyright (C) 2009-2018 by Konrad Rosenbaum # protected under the GNU GPL version 3 or at your option any newer. # See COPYING.GPL file that comes with this distribution. @@ -9,10 +9,11 @@ CONFIG+=console CONFIG-=app_bundle CONFIG(prewoc, prewoc|woc){ - DEFINES += NO_PACK_VERSION_H + DEFINES += NO_PACK_VERSION_H COMPILE_PREWOC TARGET = ../woc/prewoc MYTMPDIR = .ptmp }else{ + DEFINES += COMPILE_WOC TARGET = ../woc/woc MYTMPDIR = .ctmp } @@ -26,20 +27,27 @@ RCC_DIR = $$MYTMPDIR SOURCES+= \ woc.cpp \ - htmlout.cpp \ mfile.cpp \ domquery.cpp HEADERS+= \ - htmlout.h \ mfile.h \ domquery.h include(proc/proc.pri) -include(php/php.pri) include(qt/qt.pri) -include(soap/soap.pri) +include(generic/generic.pri) +CONFIG(prewoc, prewoc|woc){ +}else{ + SOURCES += htmlout.cpp + HEADERS += htmlout.h + include(php/php.pri) + include(soap/soap.pri) +} + +GRESOURCES+=pattern +include (qrcgen/qrcgen.pri) -gcc { QMAKE_CXXFLAGS += -std=c++11 } +CONFIG += c++11 INCLUDEPATH += . ../vinfo ../qtbase/include