Files
qqq/docs/index.html
2023-03-03 11:24:30 -06:00

1396 lines
77 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="generator" content="Asciidoctor 2.0.18">
<title>QQQ</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700">
<style>
/*! Asciidoctor default stylesheet | MIT License | https://asciidoctor.org */
/* Uncomment the following line when using as a custom stylesheet */
/* @import "https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700"; */
html{font-family:sans-serif;-webkit-text-size-adjust:100%}
a{background:none}
a:focus{outline:thin dotted}
a:active,a:hover{outline:0}
h1{font-size:2em;margin:.67em 0}
b,strong{font-weight:bold}
abbr{font-size:.9em}
abbr[title]{cursor:help;border-bottom:1px dotted #dddddf;text-decoration:none}
dfn{font-style:italic}
hr{height:0}
mark{background:#ff0;color:#000}
code,kbd,pre,samp{font-family:monospace;font-size:1em}
pre{white-space:pre-wrap}
q{quotes:"\201C" "\201D" "\2018" "\2019"}
small{font-size:80%}
sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}
sup{top:-.5em}
sub{bottom:-.25em}
img{border:0}
svg:not(:root){overflow:hidden}
figure{margin:0}
audio,video{display:inline-block}
audio:not([controls]){display:none;height:0}
fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}
legend{border:0;padding:0}
button,input,select,textarea{font-family:inherit;font-size:100%;margin:0}
button,input{line-height:normal}
button,select{text-transform:none}
button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}
button[disabled],html input[disabled]{cursor:default}
input[type=checkbox],input[type=radio]{padding:0}
button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}
textarea{overflow:auto;vertical-align:top}
table{border-collapse:collapse;border-spacing:0}
*,::before,::after{box-sizing:border-box}
html,body{font-size:100%}
body{background:#fff;color:rgba(0,0,0,.8);padding:0;margin:0;font-family:"Noto Serif","DejaVu Serif",serif;line-height:1;position:relative;cursor:auto;-moz-tab-size:4;-o-tab-size:4;tab-size:4;word-wrap:anywhere;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased}
a:hover{cursor:pointer}
img,object,embed{max-width:100%;height:auto}
object,embed{height:100%}
img{-ms-interpolation-mode:bicubic}
.left{float:left!important}
.right{float:right!important}
.text-left{text-align:left!important}
.text-right{text-align:right!important}
.text-center{text-align:center!important}
.text-justify{text-align:justify!important}
.hide{display:none}
img,object,svg{display:inline-block;vertical-align:middle}
textarea{height:auto;min-height:50px}
select{width:100%}
.subheader,.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{line-height:1.45;color:#7a2518;font-weight:400;margin-top:0;margin-bottom:.25em}
div,dl,dt,dd,ul,ol,li,h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6,pre,form,p,blockquote,th,td{margin:0;padding:0}
a{color:#2156a5;text-decoration:underline;line-height:inherit}
a:hover,a:focus{color:#1d4b8f}
a img{border:0}
p{line-height:1.6;margin-bottom:1.25em;text-rendering:optimizeLegibility}
p aside{font-size:.875em;line-height:1.35;font-style:italic}
h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{font-family:"Open Sans","DejaVu Sans",sans-serif;font-weight:300;font-style:normal;color:#ba3925;text-rendering:optimizeLegibility;margin-top:1em;margin-bottom:.5em;line-height:1.0125em}
h1 small,h2 small,h3 small,#toctitle small,.sidebarblock>.content>.title small,h4 small,h5 small,h6 small{font-size:60%;color:#e99b8f;line-height:0}
h1{font-size:2.125em}
h2{font-size:1.6875em}
h3,#toctitle,.sidebarblock>.content>.title{font-size:1.375em}
h4,h5{font-size:1.125em}
h6{font-size:1em}
hr{border:solid #dddddf;border-width:1px 0 0;clear:both;margin:1.25em 0 1.1875em}
em,i{font-style:italic;line-height:inherit}
strong,b{font-weight:bold;line-height:inherit}
small{font-size:60%;line-height:inherit}
code{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;color:rgba(0,0,0,.9)}
ul,ol,dl{line-height:1.6;margin-bottom:1.25em;list-style-position:outside;font-family:inherit}
ul,ol{margin-left:1.5em}
ul li ul,ul li ol{margin-left:1.25em;margin-bottom:0}
ul.circle{list-style-type:circle}
ul.disc{list-style-type:disc}
ul.square{list-style-type:square}
ul.circle ul:not([class]),ul.disc ul:not([class]),ul.square ul:not([class]){list-style:inherit}
ol li ul,ol li ol{margin-left:1.25em;margin-bottom:0}
dl dt{margin-bottom:.3125em;font-weight:bold}
dl dd{margin-bottom:1.25em}
blockquote{margin:0 0 1.25em;padding:.5625em 1.25em 0 1.1875em;border-left:1px solid #ddd}
blockquote,blockquote p{line-height:1.6;color:rgba(0,0,0,.85)}
@media screen and (min-width:768px){h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2}
h1{font-size:2.75em}
h2{font-size:2.3125em}
h3,#toctitle,.sidebarblock>.content>.title{font-size:1.6875em}
h4{font-size:1.4375em}}
table{background:#fff;margin-bottom:1.25em;border:1px solid #dedede;word-wrap:normal}
table thead,table tfoot{background:#f7f8f7}
table thead tr th,table thead tr td,table tfoot tr th,table tfoot tr td{padding:.5em .625em .625em;font-size:inherit;color:rgba(0,0,0,.8);text-align:left}
table tr th,table tr td{padding:.5625em .625em;font-size:inherit;color:rgba(0,0,0,.8)}
table tr.even,table tr.alt{background:#f8f8f7}
table thead tr th,table tfoot tr th,table tbody tr td,table tr td,table tfoot tr td{line-height:1.6}
h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2;word-spacing:-.05em}
h1 strong,h2 strong,h3 strong,#toctitle strong,.sidebarblock>.content>.title strong,h4 strong,h5 strong,h6 strong{font-weight:400}
.center{margin-left:auto;margin-right:auto}
.stretch{width:100%}
.clearfix::before,.clearfix::after,.float-group::before,.float-group::after{content:" ";display:table}
.clearfix::after,.float-group::after{clear:both}
:not(pre).nobreak{word-wrap:normal}
:not(pre).nowrap{white-space:nowrap}
:not(pre).pre-wrap{white-space:pre-wrap}
:not(pre):not([class^=L])>code{font-size:.9375em;font-style:normal!important;letter-spacing:0;padding:.1em .5ex;word-spacing:-.15em;background:#f7f7f8;border-radius:4px;line-height:1.45;text-rendering:optimizeSpeed}
pre{color:rgba(0,0,0,.9);font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;line-height:1.45;text-rendering:optimizeSpeed}
pre code,pre pre{color:inherit;font-size:inherit;line-height:inherit}
pre>code{display:block}
pre.nowrap,pre.nowrap pre{white-space:pre;word-wrap:normal}
em em{font-style:normal}
strong strong{font-weight:400}
.keyseq{color:rgba(51,51,51,.8)}
kbd{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;display:inline-block;color:rgba(0,0,0,.8);font-size:.65em;line-height:1.45;background:#f7f7f7;border:1px solid #ccc;border-radius:3px;box-shadow:0 1px 0 rgba(0,0,0,.2),inset 0 0 0 .1em #fff;margin:0 .15em;padding:.2em .5em;vertical-align:middle;position:relative;top:-.1em;white-space:nowrap}
.keyseq kbd:first-child{margin-left:0}
.keyseq kbd:last-child{margin-right:0}
.menuseq,.menuref{color:#000}
.menuseq b:not(.caret),.menuref{font-weight:inherit}
.menuseq{word-spacing:-.02em}
.menuseq b.caret{font-size:1.25em;line-height:.8}
.menuseq i.caret{font-weight:bold;text-align:center;width:.45em}
b.button::before,b.button::after{position:relative;top:-1px;font-weight:400}
b.button::before{content:"[";padding:0 3px 0 2px}
b.button::after{content:"]";padding:0 2px 0 3px}
p a>code:hover{color:rgba(0,0,0,.9)}
#header,#content,#footnotes,#footer{width:100%;margin:0 auto;max-width:62.5em;*zoom:1;position:relative;padding-left:.9375em;padding-right:.9375em}
#header::before,#header::after,#content::before,#content::after,#footnotes::before,#footnotes::after,#footer::before,#footer::after{content:" ";display:table}
#header::after,#content::after,#footnotes::after,#footer::after{clear:both}
#content{margin-top:1.25em}
#content::before{content:none}
#header>h1:first-child{color:rgba(0,0,0,.85);margin-top:2.25rem;margin-bottom:0}
#header>h1:first-child+#toc{margin-top:8px;border-top:1px solid #dddddf}
#header>h1:only-child,body.toc2 #header>h1:nth-last-child(2){border-bottom:1px solid #dddddf;padding-bottom:8px}
#header .details{border-bottom:1px solid #dddddf;line-height:1.45;padding-top:.25em;padding-bottom:.25em;padding-left:.25em;color:rgba(0,0,0,.6);display:flex;flex-flow:row wrap}
#header .details span:first-child{margin-left:-.125em}
#header .details span.email a{color:rgba(0,0,0,.85)}
#header .details br{display:none}
#header .details br+span::before{content:"\00a0\2013\00a0"}
#header .details br+span.author::before{content:"\00a0\22c5\00a0";color:rgba(0,0,0,.85)}
#header .details br+span#revremark::before{content:"\00a0|\00a0"}
#header #revnumber{text-transform:capitalize}
#header #revnumber::after{content:"\00a0"}
#content>h1:first-child:not([class]){color:rgba(0,0,0,.85);border-bottom:1px solid #dddddf;padding-bottom:8px;margin-top:0;padding-top:1rem;margin-bottom:1.25rem}
#toc{border-bottom:1px solid #e7e7e9;padding-bottom:.5em}
#toc>ul{margin-left:.125em}
#toc ul.sectlevel0>li>a{font-style:italic}
#toc ul.sectlevel0 ul.sectlevel1{margin:.5em 0}
#toc ul{font-family:"Open Sans","DejaVu Sans",sans-serif;list-style-type:none}
#toc li{line-height:1.3334;margin-top:.3334em}
#toc a{text-decoration:none}
#toc a:active{text-decoration:underline}
#toctitle{color:#7a2518;font-size:1.2em}
@media screen and (min-width:768px){#toctitle{font-size:1.375em}
body.toc2{padding-left:15em;padding-right:0}
#toc.toc2{margin-top:0!important;background:#f8f8f7;position:fixed;width:15em;left:0;top:0;border-right:1px solid #e7e7e9;border-top-width:0!important;border-bottom-width:0!important;z-index:1000;padding:1.25em 1em;height:100%;overflow:auto}
#toc.toc2 #toctitle{margin-top:0;margin-bottom:.8rem;font-size:1.2em}
#toc.toc2>ul{font-size:.9em;margin-bottom:0}
#toc.toc2 ul ul{margin-left:0;padding-left:1em}
#toc.toc2 ul.sectlevel0 ul.sectlevel1{padding-left:0;margin-top:.5em;margin-bottom:.5em}
body.toc2.toc-right{padding-left:0;padding-right:15em}
body.toc2.toc-right #toc.toc2{border-right-width:0;border-left:1px solid #e7e7e9;left:auto;right:0}}
@media screen and (min-width:1280px){body.toc2{padding-left:20em;padding-right:0}
#toc.toc2{width:20em}
#toc.toc2 #toctitle{font-size:1.375em}
#toc.toc2>ul{font-size:.95em}
#toc.toc2 ul ul{padding-left:1.25em}
body.toc2.toc-right{padding-left:0;padding-right:20em}}
#content #toc{border:1px solid #e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;border-radius:4px}
#content #toc>:first-child{margin-top:0}
#content #toc>:last-child{margin-bottom:0}
#footer{max-width:none;background:rgba(0,0,0,.8);padding:1.25em}
#footer-text{color:hsla(0,0%,100%,.8);line-height:1.44}
#content{margin-bottom:.625em}
.sect1{padding-bottom:.625em}
@media screen and (min-width:768px){#content{margin-bottom:1.25em}
.sect1{padding-bottom:1.25em}}
.sect1:last-child{padding-bottom:0}
.sect1+.sect1{border-top:1px solid #e7e7e9}
#content h1>a.anchor,h2>a.anchor,h3>a.anchor,#toctitle>a.anchor,.sidebarblock>.content>.title>a.anchor,h4>a.anchor,h5>a.anchor,h6>a.anchor{position:absolute;z-index:1001;width:1.5ex;margin-left:-1.5ex;display:block;text-decoration:none!important;visibility:hidden;text-align:center;font-weight:400}
#content h1>a.anchor::before,h2>a.anchor::before,h3>a.anchor::before,#toctitle>a.anchor::before,.sidebarblock>.content>.title>a.anchor::before,h4>a.anchor::before,h5>a.anchor::before,h6>a.anchor::before{content:"\00A7";font-size:.85em;display:block;padding-top:.1em}
#content h1:hover>a.anchor,#content h1>a.anchor:hover,h2:hover>a.anchor,h2>a.anchor:hover,h3:hover>a.anchor,#toctitle:hover>a.anchor,.sidebarblock>.content>.title:hover>a.anchor,h3>a.anchor:hover,#toctitle>a.anchor:hover,.sidebarblock>.content>.title>a.anchor:hover,h4:hover>a.anchor,h4>a.anchor:hover,h5:hover>a.anchor,h5>a.anchor:hover,h6:hover>a.anchor,h6>a.anchor:hover{visibility:visible}
#content h1>a.link,h2>a.link,h3>a.link,#toctitle>a.link,.sidebarblock>.content>.title>a.link,h4>a.link,h5>a.link,h6>a.link{color:#ba3925;text-decoration:none}
#content h1>a.link:hover,h2>a.link:hover,h3>a.link:hover,#toctitle>a.link:hover,.sidebarblock>.content>.title>a.link:hover,h4>a.link:hover,h5>a.link:hover,h6>a.link:hover{color:#a53221}
details,.audioblock,.imageblock,.literalblock,.listingblock,.stemblock,.videoblock{margin-bottom:1.25em}
details{margin-left:1.25rem}
details>summary{cursor:pointer;display:block;position:relative;line-height:1.6;margin-bottom:.625rem;outline:none;-webkit-tap-highlight-color:transparent}
details>summary::-webkit-details-marker{display:none}
details>summary::before{content:"";border:solid transparent;border-left:solid;border-width:.3em 0 .3em .5em;position:absolute;top:.5em;left:-1.25rem;transform:translateX(15%)}
details[open]>summary::before{border:solid transparent;border-top:solid;border-width:.5em .3em 0;transform:translateY(15%)}
details>summary::after{content:"";width:1.25rem;height:1em;position:absolute;top:.3em;left:-1.25rem}
.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{text-rendering:optimizeLegibility;text-align:left;font-family:"Noto Serif","DejaVu Serif",serif;font-size:1rem;font-style:italic}
table.tableblock.fit-content>caption.title{white-space:nowrap;width:0}
.paragraph.lead>p,#preamble>.sectionbody>[class=paragraph]:first-of-type p{font-size:1.21875em;line-height:1.6;color:rgba(0,0,0,.85)}
.admonitionblock>table{border-collapse:separate;border:0;background:none;width:100%}
.admonitionblock>table td.icon{text-align:center;width:80px}
.admonitionblock>table td.icon img{max-width:none}
.admonitionblock>table td.icon .title{font-weight:bold;font-family:"Open Sans","DejaVu Sans",sans-serif;text-transform:uppercase}
.admonitionblock>table td.content{padding-left:1.125em;padding-right:1.25em;border-left:1px solid #dddddf;color:rgba(0,0,0,.6);word-wrap:anywhere}
.admonitionblock>table td.content>:last-child>:last-child{margin-bottom:0}
.exampleblock>.content{border:1px solid #e6e6e6;margin-bottom:1.25em;padding:1.25em;background:#fff;border-radius:4px}
.exampleblock>.content>:first-child{margin-top:0}
.exampleblock>.content>:last-child{margin-bottom:0}
.sidebarblock{border:1px solid #dbdbd6;margin-bottom:1.25em;padding:1.25em;background:#f3f3f2;border-radius:4px}
.sidebarblock>:first-child{margin-top:0}
.sidebarblock>:last-child{margin-bottom:0}
.sidebarblock>.content>.title{color:#7a2518;margin-top:0;text-align:center}
.exampleblock>.content>:last-child>:last-child,.exampleblock>.content .olist>ol>li:last-child>:last-child,.exampleblock>.content .ulist>ul>li:last-child>:last-child,.exampleblock>.content .qlist>ol>li:last-child>:last-child,.sidebarblock>.content>:last-child>:last-child,.sidebarblock>.content .olist>ol>li:last-child>:last-child,.sidebarblock>.content .ulist>ul>li:last-child>:last-child,.sidebarblock>.content .qlist>ol>li:last-child>:last-child{margin-bottom:0}
.literalblock pre,.listingblock>.content>pre{border-radius:4px;overflow-x:auto;padding:1em;font-size:.8125em}
@media screen and (min-width:768px){.literalblock pre,.listingblock>.content>pre{font-size:.90625em}}
@media screen and (min-width:1280px){.literalblock pre,.listingblock>.content>pre{font-size:1em}}
.literalblock pre,.listingblock>.content>pre:not(.highlight),.listingblock>.content>pre[class=highlight],.listingblock>.content>pre[class^="highlight "]{background:#f7f7f8}
.literalblock.output pre{color:#f7f7f8;background:rgba(0,0,0,.9)}
.listingblock>.content{position:relative}
.listingblock code[data-lang]::before{display:none;content:attr(data-lang);position:absolute;font-size:.75em;top:.425rem;right:.5rem;line-height:1;text-transform:uppercase;color:inherit;opacity:.5}
.listingblock:hover code[data-lang]::before{display:block}
.listingblock.terminal pre .command::before{content:attr(data-prompt);padding-right:.5em;color:inherit;opacity:.5}
.listingblock.terminal pre .command:not([data-prompt])::before{content:"$"}
.listingblock pre.highlightjs{padding:0}
.listingblock pre.highlightjs>code{padding:1em;border-radius:4px}
.listingblock pre.prettyprint{border-width:0}
.prettyprint{background:#f7f7f8}
pre.prettyprint .linenums{line-height:1.45;margin-left:2em}
pre.prettyprint li{background:none;list-style-type:inherit;padding-left:0}
pre.prettyprint li code[data-lang]::before{opacity:1}
pre.prettyprint li:not(:first-child) code[data-lang]::before{display:none}
table.linenotable{border-collapse:separate;border:0;margin-bottom:0;background:none}
table.linenotable td[class]{color:inherit;vertical-align:top;padding:0;line-height:inherit;white-space:normal}
table.linenotable td.code{padding-left:.75em}
table.linenotable td.linenos,pre.pygments .linenos{border-right:1px solid;opacity:.35;padding-right:.5em;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}
pre.pygments span.linenos{display:inline-block;margin-right:.75em}
.quoteblock{margin:0 1em 1.25em 1.5em;display:table}
.quoteblock:not(.excerpt)>.title{margin-left:-1.5em;margin-bottom:.75em}
.quoteblock blockquote,.quoteblock p{color:rgba(0,0,0,.85);font-size:1.15rem;line-height:1.75;word-spacing:.1em;letter-spacing:0;font-style:italic;text-align:justify}
.quoteblock blockquote{margin:0;padding:0;border:0}
.quoteblock blockquote::before{content:"\201c";float:left;font-size:2.75em;font-weight:bold;line-height:.6em;margin-left:-.6em;color:#7a2518;text-shadow:0 1px 2px rgba(0,0,0,.1)}
.quoteblock blockquote>.paragraph:last-child p{margin-bottom:0}
.quoteblock .attribution{margin-top:.75em;margin-right:.5ex;text-align:right}
.verseblock{margin:0 1em 1.25em}
.verseblock pre{font-family:"Open Sans","DejaVu Sans",sans-serif;font-size:1.15rem;color:rgba(0,0,0,.85);font-weight:300;text-rendering:optimizeLegibility}
.verseblock pre strong{font-weight:400}
.verseblock .attribution{margin-top:1.25rem;margin-left:.5ex}
.quoteblock .attribution,.verseblock .attribution{font-size:.9375em;line-height:1.45;font-style:italic}
.quoteblock .attribution br,.verseblock .attribution br{display:none}
.quoteblock .attribution cite,.verseblock .attribution cite{display:block;letter-spacing:-.025em;color:rgba(0,0,0,.6)}
.quoteblock.abstract blockquote::before,.quoteblock.excerpt blockquote::before,.quoteblock .quoteblock blockquote::before{display:none}
.quoteblock.abstract blockquote,.quoteblock.abstract p,.quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{line-height:1.6;word-spacing:0}
.quoteblock.abstract{margin:0 1em 1.25em;display:block}
.quoteblock.abstract>.title{margin:0 0 .375em;font-size:1.15em;text-align:center}
.quoteblock.excerpt>blockquote,.quoteblock .quoteblock{padding:0 0 .25em 1em;border-left:.25em solid #dddddf}
.quoteblock.excerpt,.quoteblock .quoteblock{margin-left:0}
.quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{color:inherit;font-size:1.0625rem}
.quoteblock.excerpt .attribution,.quoteblock .quoteblock .attribution{color:inherit;font-size:.85rem;text-align:left;margin-right:0}
p.tableblock:last-child{margin-bottom:0}
td.tableblock>.content{margin-bottom:1.25em;word-wrap:anywhere}
td.tableblock>.content>:last-child{margin-bottom:-1.25em}
table.tableblock,th.tableblock,td.tableblock{border:0 solid #dedede}
table.grid-all>*>tr>*{border-width:1px}
table.grid-cols>*>tr>*{border-width:0 1px}
table.grid-rows>*>tr>*{border-width:1px 0}
table.frame-all{border-width:1px}
table.frame-ends{border-width:1px 0}
table.frame-sides{border-width:0 1px}
table.frame-none>colgroup+*>:first-child>*,table.frame-sides>colgroup+*>:first-child>*{border-top-width:0}
table.frame-none>:last-child>:last-child>*,table.frame-sides>:last-child>:last-child>*{border-bottom-width:0}
table.frame-none>*>tr>:first-child,table.frame-ends>*>tr>:first-child{border-left-width:0}
table.frame-none>*>tr>:last-child,table.frame-ends>*>tr>:last-child{border-right-width:0}
table.stripes-all>*>tr,table.stripes-odd>*>tr:nth-of-type(odd),table.stripes-even>*>tr:nth-of-type(even),table.stripes-hover>*>tr:hover{background:#f8f8f7}
th.halign-left,td.halign-left{text-align:left}
th.halign-right,td.halign-right{text-align:right}
th.halign-center,td.halign-center{text-align:center}
th.valign-top,td.valign-top{vertical-align:top}
th.valign-bottom,td.valign-bottom{vertical-align:bottom}
th.valign-middle,td.valign-middle{vertical-align:middle}
table thead th,table tfoot th{font-weight:bold}
tbody tr th{background:#f7f8f7}
tbody tr th,tbody tr th p,tfoot tr th,tfoot tr th p{color:rgba(0,0,0,.8);font-weight:bold}
p.tableblock>code:only-child{background:none;padding:0}
p.tableblock{font-size:1em}
ol{margin-left:1.75em}
ul li ol{margin-left:1.5em}
dl dd{margin-left:1.125em}
dl dd:last-child,dl dd:last-child>:last-child{margin-bottom:0}
li p,ul dd,ol dd,.olist .olist,.ulist .ulist,.ulist .olist,.olist .ulist{margin-bottom:.625em}
ul.checklist,ul.none,ol.none,ul.no-bullet,ol.no-bullet,ol.unnumbered,ul.unstyled,ol.unstyled{list-style-type:none}
ul.no-bullet,ol.no-bullet,ol.unnumbered{margin-left:.625em}
ul.unstyled,ol.unstyled{margin-left:0}
li>p:empty:only-child::before{content:"";display:inline-block}
ul.checklist>li>p:first-child{margin-left:-1em}
ul.checklist>li>p:first-child>.fa-square-o:first-child,ul.checklist>li>p:first-child>.fa-check-square-o:first-child{width:1.25em;font-size:.8em;position:relative;bottom:.125em}
ul.checklist>li>p:first-child>input[type=checkbox]:first-child{margin-right:.25em}
ul.inline{display:flex;flex-flow:row wrap;list-style:none;margin:0 0 .625em -1.25em}
ul.inline>li{margin-left:1.25em}
.unstyled dl dt{font-weight:400;font-style:normal}
ol.arabic{list-style-type:decimal}
ol.decimal{list-style-type:decimal-leading-zero}
ol.loweralpha{list-style-type:lower-alpha}
ol.upperalpha{list-style-type:upper-alpha}
ol.lowerroman{list-style-type:lower-roman}
ol.upperroman{list-style-type:upper-roman}
ol.lowergreek{list-style-type:lower-greek}
.hdlist>table,.colist>table{border:0;background:none}
.hdlist>table>tbody>tr,.colist>table>tbody>tr{background:none}
td.hdlist1,td.hdlist2{vertical-align:top;padding:0 .625em}
td.hdlist1{font-weight:bold;padding-bottom:1.25em}
td.hdlist2{word-wrap:anywhere}
.literalblock+.colist,.listingblock+.colist{margin-top:-.5em}
.colist td:not([class]):first-child{padding:.4em .75em 0;line-height:1;vertical-align:top}
.colist td:not([class]):first-child img{max-width:none}
.colist td:not([class]):last-child{padding:.25em 0}
.thumb,.th{line-height:0;display:inline-block;border:4px solid #fff;box-shadow:0 0 0 1px #ddd}
.imageblock.left{margin:.25em .625em 1.25em 0}
.imageblock.right{margin:.25em 0 1.25em .625em}
.imageblock>.title{margin-bottom:0}
.imageblock.thumb,.imageblock.th{border-width:6px}
.imageblock.thumb>.title,.imageblock.th>.title{padding:0 .125em}
.image.left,.image.right{margin-top:.25em;margin-bottom:.25em;display:inline-block;line-height:0}
.image.left{margin-right:.625em}
.image.right{margin-left:.625em}
a.image{text-decoration:none;display:inline-block}
a.image object{pointer-events:none}
sup.footnote,sup.footnoteref{font-size:.875em;position:static;vertical-align:super}
sup.footnote a,sup.footnoteref a{text-decoration:none}
sup.footnote a:active,sup.footnoteref a:active{text-decoration:underline}
#footnotes{padding-top:.75em;padding-bottom:.75em;margin-bottom:.625em}
#footnotes hr{width:20%;min-width:6.25em;margin:-.25em 0 .75em;border-width:1px 0 0}
#footnotes .footnote{padding:0 .375em 0 .225em;line-height:1.3334;font-size:.875em;margin-left:1.2em;margin-bottom:.2em}
#footnotes .footnote a:first-of-type{font-weight:bold;text-decoration:none;margin-left:-1.05em}
#footnotes .footnote:last-of-type{margin-bottom:0}
#content #footnotes{margin-top:-.625em;margin-bottom:0;padding:.75em 0}
div.unbreakable{page-break-inside:avoid}
.big{font-size:larger}
.small{font-size:smaller}
.underline{text-decoration:underline}
.overline{text-decoration:overline}
.line-through{text-decoration:line-through}
.aqua{color:#00bfbf}
.aqua-background{background:#00fafa}
.black{color:#000}
.black-background{background:#000}
.blue{color:#0000bf}
.blue-background{background:#0000fa}
.fuchsia{color:#bf00bf}
.fuchsia-background{background:#fa00fa}
.gray{color:#606060}
.gray-background{background:#7d7d7d}
.green{color:#006000}
.green-background{background:#007d00}
.lime{color:#00bf00}
.lime-background{background:#00fa00}
.maroon{color:#600000}
.maroon-background{background:#7d0000}
.navy{color:#000060}
.navy-background{background:#00007d}
.olive{color:#606000}
.olive-background{background:#7d7d00}
.purple{color:#600060}
.purple-background{background:#7d007d}
.red{color:#bf0000}
.red-background{background:#fa0000}
.silver{color:#909090}
.silver-background{background:#bcbcbc}
.teal{color:#006060}
.teal-background{background:#007d7d}
.white{color:#bfbfbf}
.white-background{background:#fafafa}
.yellow{color:#bfbf00}
.yellow-background{background:#fafa00}
span.icon>.fa{cursor:default}
a span.icon>.fa{cursor:inherit}
.admonitionblock td.icon [class^="fa icon-"]{font-size:2.5em;text-shadow:1px 1px 2px rgba(0,0,0,.5);cursor:default}
.admonitionblock td.icon .icon-note::before{content:"\f05a";color:#19407c}
.admonitionblock td.icon .icon-tip::before{content:"\f0eb";text-shadow:1px 1px 2px rgba(155,155,0,.8);color:#111}
.admonitionblock td.icon .icon-warning::before{content:"\f071";color:#bf6900}
.admonitionblock td.icon .icon-caution::before{content:"\f06d";color:#bf3400}
.admonitionblock td.icon .icon-important::before{content:"\f06a";color:#bf0000}
.conum[data-value]{display:inline-block;color:#fff!important;background:rgba(0,0,0,.8);border-radius:50%;text-align:center;font-size:.75em;width:1.67em;height:1.67em;line-height:1.67em;font-family:"Open Sans","DejaVu Sans",sans-serif;font-style:normal;font-weight:bold}
.conum[data-value] *{color:#fff!important}
.conum[data-value]+b{display:none}
.conum[data-value]::after{content:attr(data-value)}
pre .conum[data-value]{position:relative;top:-.125em}
b.conum *{color:inherit!important}
.conum:not([data-value]):empty{display:none}
dt,th.tableblock,td.content,div.footnote{text-rendering:optimizeLegibility}
h1,h2,p,td.content,span.alt,summary{letter-spacing:-.01em}
p strong,td.content strong,div.footnote strong{letter-spacing:-.005em}
p,blockquote,dt,td.content,span.alt,summary{font-size:1.0625rem}
p{margin-bottom:1.25rem}
.sidebarblock p,.sidebarblock dt,.sidebarblock td.content,p.tableblock{font-size:1em}
.exampleblock>.content{background:#fffef7;border-color:#e0e0dc;box-shadow:0 1px 4px #e0e0dc}
.print-only{display:none!important}
@page{margin:1.25cm .75cm}
@media print{*{box-shadow:none!important;text-shadow:none!important}
html{font-size:80%}
a{color:inherit!important;text-decoration:underline!important}
a.bare,a[href^="#"],a[href^="mailto:"]{text-decoration:none!important}
a[href^="http:"]:not(.bare)::after,a[href^="https:"]:not(.bare)::after{content:"(" attr(href) ")";display:inline-block;font-size:.875em;padding-left:.25em}
abbr[title]{border-bottom:1px dotted}
abbr[title]::after{content:" (" attr(title) ")"}
pre,blockquote,tr,img,object,svg{page-break-inside:avoid}
thead{display:table-header-group}
svg{max-width:100%}
p,blockquote,dt,td.content{font-size:1em;orphans:3;widows:3}
h2,h3,#toctitle,.sidebarblock>.content>.title{page-break-after:avoid}
#header,#content,#footnotes,#footer{max-width:none}
#toc,.sidebarblock,.exampleblock>.content{background:none!important}
#toc{border-bottom:1px solid #dddddf!important;padding-bottom:0!important}
body.book #header{text-align:center}
body.book #header>h1:first-child{border:0!important;margin:2.5em 0 1em}
body.book #header .details{border:0!important;display:block;padding:0!important}
body.book #header .details span:first-child{margin-left:0!important}
body.book #header .details br{display:block}
body.book #header .details br+span::before{content:none!important}
body.book #toc{border:0!important;text-align:left!important;padding:0!important;margin:0!important}
body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-break-before:always}
.listingblock code[data-lang]::before{display:block}
#footer{padding:0 .9375em}
.hide-on-print{display:none!important}
.print-only{display:block!important}
.hide-for-print{display:none!important}
.show-for-print{display:inherit!important}}
@media amzn-kf8,print{#header>h1:first-child{margin-top:1.25rem}
.sect1{padding:0!important}
.sect1+.sect1{border:0}
#footer{background:none}
#footer-text{color:rgba(0,0,0,.6);font-size:.9em}}
@media amzn-kf8{#header,#content,#footnotes,#footer{padding:0}}
</style>
<style>
/*! Stylesheet for CodeRay to loosely match GitHub themes | MIT License */
pre.CodeRay{background:#f7f7f8}
.CodeRay .line-numbers{border-right:1px solid;opacity:.35;padding:0 .5em 0 0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}
.CodeRay span.line-numbers{display:inline-block;margin-right:.75em}
.CodeRay .line-numbers strong{color:#000}
table.CodeRay{border-collapse:separate;border:0;margin-bottom:0;background:none}
table.CodeRay td{vertical-align:top;line-height:inherit}
table.CodeRay td.line-numbers{text-align:right}
table.CodeRay td.code{padding:0 0 0 .75em}
.CodeRay .debug{color:#fff!important;background:navy!important}
.CodeRay .annotation{color:#007}
.CodeRay .attribute-name{color:navy}
.CodeRay .attribute-value{color:#700}
.CodeRay .binary{color:#509}
.CodeRay .comment{color:#998;font-style:italic}
.CodeRay .char{color:#04d}
.CodeRay .char .content{color:#04d}
.CodeRay .char .delimiter{color:#039}
.CodeRay .class{color:#458;font-weight:bold}
.CodeRay .complex{color:#a08}
.CodeRay .constant,.CodeRay .predefined-constant{color:teal}
.CodeRay .color{color:#099}
.CodeRay .class-variable{color:#369}
.CodeRay .decorator{color:#b0b}
.CodeRay .definition{color:#099}
.CodeRay .delimiter{color:#000}
.CodeRay .doc{color:#970}
.CodeRay .doctype{color:#34b}
.CodeRay .doc-string{color:#d42}
.CodeRay .escape{color:#666}
.CodeRay .entity{color:#800}
.CodeRay .error{color:#808}
.CodeRay .exception{color:inherit}
.CodeRay .filename{color:#099}
.CodeRay .function{color:#900;font-weight:bold}
.CodeRay .global-variable{color:teal}
.CodeRay .hex{color:#058}
.CodeRay .integer,.CodeRay .float{color:#099}
.CodeRay .include{color:#555}
.CodeRay .inline{color:#000}
.CodeRay .inline .inline{background:#ccc}
.CodeRay .inline .inline .inline{background:#bbb}
.CodeRay .inline .inline-delimiter{color:#d14}
.CodeRay .inline-delimiter{color:#d14}
.CodeRay .important{color:#555;font-weight:bold}
.CodeRay .interpreted{color:#b2b}
.CodeRay .instance-variable{color:teal}
.CodeRay .label{color:#970}
.CodeRay .local-variable{color:#963}
.CodeRay .octal{color:#40e}
.CodeRay .predefined{color:#369}
.CodeRay .preprocessor{color:#579}
.CodeRay .pseudo-class{color:#555}
.CodeRay .directive{font-weight:bold}
.CodeRay .type{font-weight:bold}
.CodeRay .predefined-type{color:inherit}
.CodeRay .reserved,.CodeRay .keyword{color:#000;font-weight:bold}
.CodeRay .key{color:#808}
.CodeRay .key .delimiter{color:#606}
.CodeRay .key .char{color:#80f}
.CodeRay .value{color:#088}
.CodeRay .regexp .delimiter{color:#808}
.CodeRay .regexp .content{color:#808}
.CodeRay .regexp .modifier{color:#808}
.CodeRay .regexp .char{color:#d14}
.CodeRay .regexp .function{color:#404;font-weight:bold}
.CodeRay .string{color:#d20}
.CodeRay .string .string .string{background:#ffd0d0}
.CodeRay .string .content{color:#d14}
.CodeRay .string .char{color:#d14}
.CodeRay .string .delimiter{color:#d14}
.CodeRay .shell{color:#d14}
.CodeRay .shell .delimiter{color:#d14}
.CodeRay .symbol{color:#990073}
.CodeRay .symbol .content{color:#a60}
.CodeRay .symbol .delimiter{color:#630}
.CodeRay .tag{color:teal}
.CodeRay .tag-special{color:#d70}
.CodeRay .variable{color:#036}
.CodeRay .insert{background:#afa}
.CodeRay .delete{background:#faa}
.CodeRay .change{color:#aaf;background:#007}
.CodeRay .head{color:#f8f;background:#505}
.CodeRay .insert .insert{color:#080}
.CodeRay .delete .delete{color:#800}
.CodeRay .change .change{color:#66f}
.CodeRay .head .head{color:#f4f}
</style>
<!--
~ QQQ - Low-code Application Framework for Engineers.
~ Copyright (C) 2021-2022. Kingsrook, LLC
~ 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
~ contact@kingsrook.com
~ https://github.com/Kingsrook/
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU Affero General Public License as
~ published by the Free Software Foundation, either version 3 of the
~ License, or (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU Affero General Public License for more details.
~
~ You should have received a copy of the GNU Affero General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<style>
pre
{
white-space: pre;
}
</style>
</head>
<body class="book toc2 toc-left">
<div id="header">
<h1>QQQ</h1>
<div id="toc" class="toc2">
<div id="toctitle">Table of Contents</div>
<ul class="sectlevel1">
<li><a href="#_introduction">Introduction</a></li>
<li><a href="#_meta_data">Meta Data</a>
<ul class="sectlevel2">
<li><a href="#_qqq_tables">QQQ Tables</a></li>
<li><a href="#_qqq_reports">QQQ Reports</a></li>
</ul>
</li>
<li><a href="#_actions">Actions</a>
<ul class="sectlevel2">
<li><a href="#_queryaction">QueryAction</a></li>
<li><a href="#_rendertemplateaction">RenderTemplateAction</a></li>
</ul>
</li>
</ul>
</div>
</div>
<div id="content">
<div class="sect1">
<h2 id="_introduction">Introduction</h2>
<div class="sectionbody">
<div class="paragraph">
<p>QQQ is &#8230;&#8203;</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Framework</p>
</li>
<li>
<p>Declarative</p>
</li>
<li>
<p>Easy thing easy; Hard thing possible</p>
</li>
<li>
<p>Customizable</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_meta_data">Meta Data</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_qqq_tables">QQQ Tables</h3>
<div class="paragraph">
<p>The core type of object in a QQQ Instance is the Table.
In the most common use-case, a QQQ Table may be the in-app representation of a Database table.
That is, it is a collection of records (or rows) of data, each of which has a set of fields (or columns).</p>
</div>
<div class="paragraph">
<p>QQQ also allows other types of data sources (<a href="Backends{relfilesuffix}">QQQ Backends</a>) to be used as tables, such as File systems, API&#8217;s, Java enums or objects, etc.
All of these backend types present the same interfaces (both user-interfaces, and application programming interfaces), regardless of their backend type.</p>
</div>
<div class="sect3">
<h4 id="_qtablemetadata">QTableMetaData</h4>
<div class="paragraph">
<p>Tables are defined in a QQQ Instance in a <code><strong>QTableMetaData</strong></code> object.
All tables must reference a <a href="Backends{relfilesuffix}">QQQ Backend</a>, a list of fields that define the shape of records in the table, and additional data to describe how to work with the table within its backend.</p>
</div>
<div class="paragraph">
<p><strong>QTableMetaData Properties:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>name</code> - <strong>String, Required</strong> - Unique name for the table within the QQQ Instance.</p>
</li>
<li>
<p><code>label</code> - <strong>String</strong> - User-facing label for the table, presented in User Interfaces.
Inferred from <code>name</code> if not set.</p>
</li>
<li>
<p><code>backendName</code> - <strong>String, Required</strong> - Name of a <a href="Backends{relfilesuffix}">QQQ Backend</a> in which this table&#8217;s data is managed.</p>
</li>
<li>
<p><code>fields</code> - <strong>Map of String → <a href="Fields{relfilesuffix}">QQQ Field</a>, Required</strong> - The columns of data that make up all records in this table.</p>
</li>
<li>
<p><code>primaryKeyField</code> - <strong>String, Conditional</strong> - Name of a <a href="Fields{relfilesuffix}">QQQ Field</a> that serves as the primary key (e.g., unique identifier) for records in this table.</p>
</li>
<li>
<p><code>uniqueKeys</code> - <strong>List of UniqueKey</strong> - Definition of additional unique constraints (from an RDBMS point of view) from the table.
e.g., sets of columns which must have unique values for each record in the table.</p>
</li>
<li>
<p><code>backendDetails</code> - <strong>QTableBackendDetails or subclass</strong> - Additional data to configure the table within its <a href="Backends{relfilesuffix}">QQQ Backend</a>.</p>
</li>
<li>
<p><code>automationDetails</code> - <strong>QTableAutomationDetails</strong> - Configuration of automated jobs that run against records in the table, e.g., upon insert or update.</p>
</li>
<li>
<p><code>customizers</code> - <strong>Map of String → QCodeReference</strong> - References to custom code that are injected into standard table actions, that allow applications to customize certain parts of how the table works.</p>
</li>
<li>
<p><code>parentAppName</code> - <strong>String</strong> - Name of a <a href="Apps{relfilesuffix}">QQQ App</a> that this table exists within.</p>
</li>
<li>
<p><code>icon</code> - <strong>QIcon</strong> - Icon associated with this table in certain user interfaces.</p>
</li>
<li>
<p><code>recordLabelFormat</code> - <strong>String</strong> - Java Format String, used with <code>recordLabelFields</code> to produce a label shown for records from the table.</p>
</li>
<li>
<p><code>recordLabelFields</code> - <strong>List of String, Conditional</strong> - Used with <code>recordLabelFormat</code> to provide values for any format specifiers in the format string.
These strings must be field names within the table.</p>
<div class="ulist">
<ul>
<li>
<p>Example of using <code>recordLabelFormat</code> and <code>recordLabelFields</code>:</p>
</li>
</ul>
</div>
</li>
</ul>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="comment">// given these fields in the table:</span>
<span class="keyword">new</span> QFieldMetaData(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, QFieldType.STRING)
<span class="keyword">new</span> QFieldMetaData(<span class="string"><span class="delimiter">&quot;</span><span class="content">birthDate</span><span class="delimiter">&quot;</span></span>, QFieldType.DATE)
<span class="comment">// We can produce a record label such as &quot;Darin Kelkhoff (1980-05-31)&quot; via:</span>
.withRecordLabelFormat(<span class="string"><span class="delimiter">&quot;</span><span class="content">%s (%s)</span><span class="delimiter">&quot;</span></span>)
.withRecordLabelFields(<span class="predefined-type">List</span>.of(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">birthDate</span><span class="delimiter">&quot;</span></span>))</code></pre>
</div>
</div>
<div class="ulist">
<ul>
<li>
<p><code>sections</code> - <strong>List of QFieldSection</strong> - Mechanism to organize fields within user interfaces, into logical sections.
If any sections are present in the table meta data, then all fields in the table must be listed in exactly 1 section.
If no sections are defined, then instance enrichment will define default sections.</p>
</li>
<li>
<p><code>associatedScripts</code> - <strong>List of AssociatedScript</strong> - Definition of user-defined scripts that can be associated with records within the table.</p>
</li>
<li>
<p><code>enabledCapabilities</code> and <code>disabledCapabilities</code> - <strong>Set of Capability enum values</strong> - Overrides from the backend level, for capabilities that this table does or does not possess.</p>
</li>
</ul>
</div>
<hr>
</div>
</div>
<div class="sect2">
<h3 id="_qqq_reports">QQQ Reports</h3>
<div class="paragraph">
<p>QQQ can generate reports based on <a href="Tables{relfilesuffix}">QQQ Tables</a> defined within a QQQ Instance.
Users can run reports, providing input values.
Alternatively, application code can run reports as needed, supplying input values.</p>
</div>
<div class="sect3">
<h4 id="_qreportmetadata">QReportMetaData</h4>
<div class="paragraph">
<p>Reports are defined in a QQQ Instance with a <code><strong>QReportMetaData</strong></code> object.
Reports are defined in terms of their sources of data (<code>QReportDataSource</code>), and their view(s) of that data (<code>QReportView</code>).</p>
</div>
<div class="paragraph">
<p><strong>QReportMetaData Properties:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>name</code> - <strong>String, Required</strong> - Unique name for the report within the QQQ Instance.</p>
</li>
<li>
<p><code>label</code> - <strong>String</strong> - User-facing label for the report, presented in User Interfaces.
Inferred from <code>name</code> if not set.</p>
</li>
<li>
<p><code>processName</code> - <strong>String</strong> - Name of a <a href="Processes{relfilesuffix}">QQQ Process</a> used to run the report in a User Interface.</p>
</li>
<li>
<p><code>inputFields</code> - <strong>List of <a href="Fields{relfilesuffix}">QQQ Field</a></strong> - Optional list of fields used as input to the report.</p>
<div class="ulist">
<ul>
<li>
<p>The values in these fields can be used via the syntax <code>${input.NAME}</code>, where <code>NAME</code> is the <code>name</code> attribute of the <code>inputField</code>.</p>
</li>
<li>
<p>For example:</p>
</li>
</ul>
</div>
</li>
</ul>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="comment">// given this inputField:</span>
<span class="keyword">new</span> QFieldMetaData(<span class="string"><span class="delimiter">&quot;</span><span class="content">storeId</span><span class="delimiter">&quot;</span></span>, QFieldType.INTEGER)
<span class="comment">// its run-time value can be accessed, e.g., in a query filter under a data source:</span>
<span class="keyword">new</span> QFilterCriteria(<span class="string"><span class="delimiter">&quot;</span><span class="content">storeId</span><span class="delimiter">&quot;</span></span>, QCriteriaOperator.EQUALS, <span class="predefined-type">List</span>.of(<span class="string"><span class="delimiter">&quot;</span><span class="content">${input.storeId}</span><span class="delimiter">&quot;</span></span>))
<span class="comment">// or in a report view's title or field formulas:</span>
.withTitleFields(<span class="predefined-type">List</span>.of(<span class="string"><span class="delimiter">&quot;</span><span class="content">${input.storeId}</span><span class="delimiter">&quot;</span></span>))
<span class="keyword">new</span> QReportField().withName(<span class="string"><span class="delimiter">&quot;</span><span class="content">storeId</span><span class="delimiter">&quot;</span></span>).withFormula(<span class="string"><span class="delimiter">&quot;</span><span class="content">${input.storeId}</span><span class="delimiter">&quot;</span></span>)</code></pre>
</div>
</div>
<div class="ulist">
<ul>
<li>
<p><code>dataSources</code> - <strong>List of QReportDataSource, Required</strong> - Definitions of the sources of data for the report.
At least one is required.</p>
</li>
</ul>
</div>
<div class="sect4">
<h5 id="_qreportdatasource">QReportDataSource</h5>
<div class="paragraph">
<p>Data sources for QQQ Reports can either reference <a href="Tables{relfilesuffix}">QQQ Tables</a> within the QQQ Instance, or they can provide custom code in the form of a <code>CodeReference</code> to a <code>Supplier</code>, for use cases such as a static data tab in an Excel report.</p>
</div>
<div class="paragraph">
<p><strong>QReportDataSource Properties:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>name</code> - <strong>String, Required</strong> - Unique name for the data source within its containing Report.</p>
</li>
<li>
<p><code>sourceTable</code> - <strong>String, Conditional</strong> - Reference to a <a href="Tables{relfilesuffix}">QQQ Table</a> in the QQQ Instance, which the data source queries data from.</p>
</li>
<li>
<p><code>queryFilter</code> - <strong>QQueryFilter</strong> - If a <code>sourceTable</code> is defined, then the filter specified here is used to filter and sort the records queried from that table when generating the report.</p>
</li>
<li>
<p><code>staticDataSupplier</code> - <strong>QCodeReference, Conditional</strong> - Reference to custom code which can be used to supply the data for the data source, as an alternative to querying a <code>sourceTable</code>.</p>
<div class="ulist">
<ul>
<li>
<p>Must be a <code>JAVA</code> code type</p>
</li>
<li>
<p>Must be a <code>REPORT_STATIC_DATA_SUPPLIER</code> code usage.</p>
</li>
<li>
<p>The referenced class must implement the interface: <code>Supplier&lt;List&lt;List&lt;Serializable&gt;&gt;&gt;</code>.</p>
</li>
</ul>
</div>
</li>
</ul>
</div>
</div>
<div class="sect4">
<h5 id="_qreportview">QReportView</h5>
<div class="paragraph">
<p>Report Views control how the source data for a report is organized and presented to the user in the output report file.
If a DataSource describes the rows for a report (e.g., what table provides what records), then a View may be thought of as describing the columns in the report.
A single report can have multiple views, specifically, for the use-case where an Excel file is being generated, in which case each View creates a tab or sheet within the <code>xlsx</code> file.</p>
</div>
<div class="paragraph">
<p><strong>QReportView Properties:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>name</code> - <strong>String, Required</strong> - Unique name for the view within its containing Report.</p>
</li>
<li>
<p><code>label</code> - <strong>String</strong> - Used as a sheet (tab) label in Excel formatted reports.</p>
</li>
<li>
<p><code>type</code> - <strong>enum of TABLE, SUMMARY, PIVOT. Required</strong> - Defines the type of view being defined.</p>
<div class="ulist">
<ul>
<li>
<p><strong>TABLE</strong> views are a simple listing of the records from the data source.</p>
</li>
<li>
<p><strong>SUMMARY</strong> views are essentially pre-computed Pivot Tables.
That is to say, the aggregation done by a Pivot Table in a spreadsheet file is done by QQQ while generating the report.
In this way, a non-spreadsheet report (e.g., PDF or CSV) can have summarized data, as though it were a Pivot Table in a live spreadsheet.</p>
</li>
<li>
<p><strong>PIVOT</strong> views produce actual Pivot Tables, and are only supported in Excel files <em>(and are not supported at the time of this writing)</em>.</p>
</li>
</ul>
</div>
</li>
<li>
<p><code>dataSourceName</code> - <strong>String, Required</strong> - Reference to a DataSource within the report, that is used to provide the rows for the view.</p>
</li>
<li>
<p><code>varianceDataSourceName</code> - <strong>String</strong> - Optional reference to a second DataSource within the report, that is used in <code><strong>SUMMARY</strong></code> type views for computing variances.</p>
<div class="ulist">
<ul>
<li>
<p>For example, given a Data Source with a filter that selects all sales records for a given year, a Variance Data Source may have a filter that selects the previous year, for doing comparissons.</p>
</li>
</ul>
</div>
</li>
<li>
<p><code>pivotFields</code> - <strong>List of String, Conditional</strong> - For <strong>SUMMARY</strong> or <strong>PIVOT</strong> type views, specify the field(s) used as pivot rows.</p>
<div class="ulist">
<ul>
<li>
<p>For example, in a summary view of orders, you may "pivot" on the <strong>customerId</strong> field, to produce one row per-customer, with aggregate data for that customer.</p>
</li>
</ul>
</div>
</li>
<li>
<p><code>titleFormat</code> - <strong>String</strong> - Java Format String, used with <code>titleFields</code> (if given), to produce a title row, e.g., first row in the view (before any rows from the data source).</p>
</li>
<li>
<p><code>titleFields</code> - <strong>List of String, Conditional</strong> - Used with <code>titleFormat</code>, to provide values for any format specifiers in the format string.
Syntax to reference a field (e.g., from a report input field) is: <code>${input.NAME}</code>, where <code>NAME</code> is the <code>name</code> attribute of the inputField.</p>
<div class="ulist">
<ul>
<li>
<p>Example of using <code>titleFormat</code> and <code>titleFields</code>:</p>
</li>
</ul>
</div>
</li>
</ul>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="comment">// given these inputFields:</span>
<span class="keyword">new</span> QFieldMetaData(<span class="string"><span class="delimiter">&quot;</span><span class="content">startDate</span><span class="delimiter">&quot;</span></span>, QFieldType.DATE)
<span class="keyword">new</span> QFieldMetaData(<span class="string"><span class="delimiter">&quot;</span><span class="content">endDate</span><span class="delimiter">&quot;</span></span>, QFieldType.DATE)
<span class="comment">// a view can have a title row like this:</span>
.withTitleFormat(<span class="string"><span class="delimiter">&quot;</span><span class="content">Weekly Sales Report - %s - %s</span><span class="delimiter">&quot;</span></span>)
.withTitleFields(<span class="predefined-type">List</span>.of(<span class="string"><span class="delimiter">&quot;</span><span class="content">${input.startDate}</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">${input.endDate}</span><span class="delimiter">&quot;</span></span>))</code></pre>
</div>
</div>
<div class="ulist">
<ul>
<li>
<p><code>includeHeaderRow</code> - <strong>boolean, default true</strong> - Indication that first row of the view should be the column labels.</p>
<div class="ulist">
<ul>
<li>
<p>If true, then header row is put in the view.</p>
</li>
<li>
<p>If false, then no header row is put in the view.</p>
</li>
</ul>
</div>
</li>
<li>
<p><code>includeTotalRow</code> - <strong>boolean, default false</strong> - Indication that a totals row should be added to the view.
All numeric columns are summed to produce values in the totals row.</p>
<div class="ulist">
<ul>
<li>
<p>If true, then totals row is put in the view.</p>
</li>
<li>
<p>If false, then no totals row is put in the view.</p>
</li>
</ul>
</div>
</li>
<li>
<p><code>includePivotSubTotals</code> - <strong>boolean, default false</strong> - For a <strong>SUMMARY</strong> or <strong>PIVOT</strong> type view, if there are more than 1 <strong>pivotFields</strong> being used, this field is an indication that each higher-level pivot should include sub-totals.</p>
<div class="ulist">
<ul>
<li>
<p><mark>TODO - provide example</mark></p>
</li>
</ul>
</div>
</li>
<li>
<p><code>columns</code> - <strong>List of QReportField, required</strong> - Definition of the columns to appear in the view. See section on QReportField for details.</p>
</li>
<li>
<p><code>orderByFields</code> - <strong>List of QFilterOrderBy, optional</strong> - For a <strong>SUMMARY</strong> or <strong>PIVOT</strong> type view, how to sort the rows.</p>
</li>
<li>
<p><code>recordTransformStep</code> - <strong>QCodeReference, subclass of <code>AbstractTransformStep</code></strong> - Custom code reference that can be used to transform records after they are queried from the data source, and before they are placed into the view.
Can be used to transform or customize values, or to look up additional values to add to the report.</p>
<div class="ulist">
<ul>
<li>
<p><mark>TODO - provide example</mark></p>
</li>
</ul>
</div>
</li>
<li>
<p><code>viewCustomizer</code> - <strong>QCodeReference, implementation of interface <code>Function&lt;QReportView, QReportView&gt;</code></strong> - Custom code reference that can be used to customize the report view, at runtime.
Can be used, for example, to dynamically define the report&#8217;s <strong>columns</strong>.</p>
<div class="ulist">
<ul>
<li>
<p><mark>TODO - provide example</mark></p>
</li>
</ul>
</div>
</li>
</ul>
</div>
<div class="sect5">
<h6 id="_qreportfield">QReportField</h6>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_actions">Actions</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_queryaction">QueryAction</h3>
<div class="paragraph">
<p>The <code><strong>QueryAction</strong></code> is the basic action that is used to get records from a <a href="Tables{relfilesuffix}">QQQ Table</a>.
In SQL/RDBMS terms, it is analogous to a <code>SELECT</code> statement, where 0 or more records may be found and returned.</p>
</div>
<div class="sect3">
<h4 id="_examples">Examples</h4>
<div class="sect4">
<h5 id="_simplest_form">Simplest Form</h5>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java">QueryInput input = <span class="keyword">new</span> QueryInput(qInstance);
input.setSession(session);
input.setTableName(<span class="string"><span class="delimiter">&quot;</span><span class="content">orders</span><span class="delimiter">&quot;</span></span>);
input.setFilter(<span class="keyword">new</span> QQueryFilter(<span class="keyword">new</span> QFilterCriteria(<span class="string"><span class="delimiter">&quot;</span><span class="content">total</span><span class="delimiter">&quot;</span></span>, GREATER_THAN, <span class="keyword">new</span> <span class="predefined-type">BigDecimal</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">3.50</span><span class="delimiter">&quot;</span></span>))));
QueryOutput output = <span class="keyword">new</span> QueryAction.execute(input);
<span class="predefined-type">List</span>&lt;QRecord&gt; records = output.getRecords();</code></pre>
</div>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_queryinput">QueryInput</h4>
<div class="ulist">
<ul>
<li>
<p><code>table</code> - <strong>String, Required</strong> - Name of the table being queried against.</p>
</li>
<li>
<p><code>filter</code> - <strong>QQueryFilter object</strong> - Specification for what records should be returned, based on <strong>QFilterCriteria</strong> objects, and how they should be sorted, based on <strong>QFilterOrderBy</strong> objects.</p>
</li>
<li>
<p><code>skip</code> - <strong>Integer</strong> - Optional number of records to be skipped at the beginning of the result set.
e.g., for implementing pagination.</p>
</li>
<li>
<p><code>limit</code> - <strong>Integer</strong> - Optional maximum number of records to be returned by the query.</p>
</li>
<li>
<p><code>transaction</code> - <strong>QBackendTransaction object</strong> - Optional transaction object.</p>
<div class="ulist">
<ul>
<li>
<p>Behavior for this object is backend-dependant.
In an RDBMS backend, this object is generally needed if you want your query to see data that may have been modified within the same transaction.</p>
</li>
</ul>
</div>
</li>
<li>
<p><code>recordPipe</code> - <strong>RecordPipe object</strong> - Optional object that records are placed into, for asynchronous processing.</p>
<div class="ulist">
<ul>
<li>
<p>If a <strong>recordPipe</strong> is used, then records cannot be retrieved from the <strong>QueryOutput</strong>.
Rather, such records must be read from the pipe&#8217;s <code>consumeAvailableRecords()</code> method.</p>
</li>
<li>
<p>A <strong>recordPipe</strong> should only be used when a <strong>QueryAction</strong> is running in a separate Thread from the record&#8217;s consumer.</p>
</li>
</ul>
</div>
</li>
<li>
<p><code>shouldTranslatePossibleValues</code> - <strong>boolean, default: false</strong> - Controls whether any fields in the table with a <strong>possibleValueSource</strong> assigned to them should have those possible values looked up
(e.g., to provide text translations in the generated records' <code>displayValues</code> map).</p>
<div class="ulist">
<ul>
<li>
<p>For example, if running a query to present results to a user, this would generally need to be <strong>true</strong>.
But if running a query to provide data as part of a process, then this can generally be left as <strong>false</strong>.</p>
</li>
</ul>
</div>
</li>
<li>
<p><code>shouldGenerateDisplayValues</code> - <strong>boolean, default: false</strong> - Controls whether if field level <strong>displayFormats</strong> should be used to populate the generated records' <code>displayValues</code> map.</p>
<div class="ulist">
<ul>
<li>
<p>For example, if running a query to present results to a user, this would generally need to be <strong>true</strong>.
But if running a query to provide data as part of a process, then this can generally be left as <strong>false</strong>.</p>
</li>
</ul>
</div>
</li>
<li>
<p><code>queryJoins</code> - <strong>List of QueryJoin objects</strong> - Optional list of tables to be joined with the main <strong>table</strong> specified in the <strong>QueryInput</strong>.
See QueryJoin below for further details.</p>
</li>
</ul>
</div>
<div class="sect4">
<h5 id="_qqueryfilter">QQueryFilter</h5>
<div class="paragraph">
<p>A key component of <strong>QueryInput</strong>, a <strong>QQueryFilter</strong> defines both what records should be included in a query&#8217;s results (e.g., an SQL <code>WHERE</code>), as well as how those results should be sorted (SQL <code>ORDER BY</code>).</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>criteria</code> - <strong>List of QFilterCriteria</strong> - Individual conditions or clauses to filter records.
They are combined using the <strong>booleanOperator</strong> specified in the <strong>QQueryFilter</strong>. See below for further details.</p>
</li>
<li>
<p><code>orderBys</code> - <strong>List of QFilterOrderBy</strong> - List of fields (and directions) to control the sorting of query results.
In general, multiple <strong>orderBys</strong> can be given (depending on backend implementations).</p>
</li>
<li>
<p><code>booleanOperator</code> - <strong>Enum of AND, OR, default: AND</strong> - Specifies the logical joining operator used among individual criteria.</p>
</li>
<li>
<p><code>subFilters</code> - <strong>List of QQueryFilter</strong> - To build arbitrarily complex queries, with nested boolean logic, 0 or more <strong>subFilters</strong> may be provided.</p>
<div class="ulist">
<ul>
<li>
<p>Each <strong>subFilter</strong> can include its own additional <strong>subFilters</strong>.</p>
</li>
<li>
<p>Each <strong>subFilter</strong> can specify a different <strong>booleanOperator</strong>.</p>
</li>
<li>
<p>For example, consider the following <strong>QQueryFilter</strong>, that uses two <strong>subFilters</strong>, and a mix of <strong>booleanOperators</strong></p>
</li>
</ul>
</div>
</li>
</ul>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"> queryInput.setFilter(<span class="keyword">new</span> QQueryFilter()
.withBooleanOperator(OR)
.withSubFilters(<span class="predefined-type">List</span>.of(
<span class="keyword">new</span> QQueryFilter().withBooleanOperator(AND)
.withCriteria(<span class="keyword">new</span> QFilterCriteria(<span class="string"><span class="delimiter">&quot;</span><span class="content">firstName</span><span class="delimiter">&quot;</span></span>, EQUALS, <span class="string"><span class="delimiter">&quot;</span><span class="content">James</span><span class="delimiter">&quot;</span></span>))
.withCriteria(<span class="keyword">new</span> QFilterCriteria(<span class="string"><span class="delimiter">&quot;</span><span class="content">lastName</span><span class="delimiter">&quot;</span></span>, EQUALS, <span class="string"><span class="delimiter">&quot;</span><span class="content">Maes</span><span class="delimiter">&quot;</span></span>)),
<span class="keyword">new</span> QQueryFilter().withBooleanOperator(AND)
.withCriteria(<span class="keyword">new</span> QFilterCriteria(<span class="string"><span class="delimiter">&quot;</span><span class="content">firstName</span><span class="delimiter">&quot;</span></span>, EQUALS, <span class="string"><span class="delimiter">&quot;</span><span class="content">Darin</span><span class="delimiter">&quot;</span></span>))
.withCriteria(<span class="keyword">new</span> QFilterCriteria(<span class="string"><span class="delimiter">&quot;</span><span class="content">lastName</span><span class="delimiter">&quot;</span></span>, EQUALS, <span class="string"><span class="delimiter">&quot;</span><span class="content">Kelkhoff</span><span class="delimiter">&quot;</span></span>))
)));
<span class="comment">// which would generate the following WHERE clause in an RDBMS backend:</span>
WHERE (first_name=<span class="string"><span class="delimiter">'</span><span class="content">James</span><span class="delimiter">'</span></span> AND last_name=<span class="string"><span class="delimiter">'</span><span class="content">Maes</span><span class="delimiter">'</span></span>) OR (first_name=<span class="string"><span class="delimiter">'</span><span class="content">Darin</span><span class="delimiter">'</span></span> AND last_name=<span class="string"><span class="delimiter">'</span><span class="content">Kelkhoff</span><span class="delimiter">'</span></span>)</code></pre>
</div>
</div>
<div class="sect5">
<h6 id="_qfiltercriteria">QFilterCriteria</h6>
<div class="ulist">
<ul>
<li>
<p><code>fieldName</code> - <strong>String, required</strong> - Reference to a field on the table being queried.</p>
<div class="ulist">
<ul>
<li>
<p>Or, in the case of a query with <strong>queryJoins</strong>, a qualified name of a field from a join-table (where the qualifier would be the joined table&#8217;s name or alias, followed by a dot)</p>
<div class="ulist">
<ul>
<li>
<p>For example: <code>orderLine.sku</code> or <code>orderBillToCustomer.firstName</code></p>
</li>
</ul>
</div>
</li>
</ul>
</div>
</li>
<li>
<p><code>operator</code> - <strong>Enum of QCriteriaOperator, required</strong> - Comparison operation to be applied to the field specified as <strong>fieldName</strong> and the <strong>values</strong> or <strong>otherFieldName</strong>.</p>
<div class="ulist">
<ul>
<li>
<p>e.g., <code>EQUALS</code>, <code>NOT_IN</code>, <code>GREATER_THAN</code>, <code>BETWEEN</code>, <code>IS_BLANK</code>, etc.</p>
</li>
</ul>
</div>
</li>
<li>
<p><code>values</code> - <strong>List of values, conditional</strong> - Provides the value(s) that the field is compared against.
The number of values (0, 1, 2, or more) be driven based on the <strong>operator</strong> being used.
If an <strong>otherFieldName</strong> is given, and the <strong>operator</strong> expects 1 value, then <strong>values</strong> is ignored, and <strong>otherFieldName</strong> is used.</p>
</li>
<li>
<p><code>otherFieldName</code> - <strong>String, conditional</strong> - Specifies that the <strong>fieldName</strong> should be compared against another field in the records, rather than the values in the <strong>values</strong> property.
Only used for <strong>operators</strong> that expect 1 value (e.g., <code>EQUALS</code> or <code>LESS_THAN_OR_EQUALS</code> - not <code>IS_NOT_BLANK</code> or <code>IN</code>).</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>QFilterCriteria definition examples:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="comment">// one-liners, via constructors that take (List&lt;Serializable&gt; values) or (Serializable... values) in 3rd position</span>
<span class="keyword">new</span> QFilterCriteria(<span class="string"><span class="delimiter">&quot;</span><span class="content">id</span><span class="delimiter">&quot;</span></span>, IN, <span class="predefined-type">List</span>.of(<span class="integer">1</span>, <span class="integer">2</span>, <span class="integer">3</span>))
<span class="keyword">new</span> QFilterCriteria(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, IS_BLANK)
<span class="keyword">new</span> QFilterCriteria(<span class="string"><span class="delimiter">&quot;</span><span class="content">orderNo</span><span class="delimiter">&quot;</span></span>, IN, orderNoList)
<span class="keyword">new</span> QFilterCriteria(<span class="string"><span class="delimiter">&quot;</span><span class="content">state</span><span class="delimiter">&quot;</span></span>, EQUALS, <span class="string"><span class="delimiter">&quot;</span><span class="content">MO</span><span class="delimiter">&quot;</span></span>);
<span class="comment">// long-form, with fluent setters</span>
<span class="keyword">new</span> QFilterCriteria()
.withFieldName(<span class="string"><span class="delimiter">&quot;</span><span class="content">quantity</span><span class="delimiter">&quot;</span></span>)
.withOpeartor(QCriteriaOperator.GREATER_THAN)
.withValues(<span class="predefined-type">List</span>.of(<span class="integer">47</span>));
<span class="comment">// to use otherFieldName, long-form must be used</span>
<span class="keyword">new</span> QFilterCriteria()
.withFieldName(<span class="string"><span class="delimiter">&quot;</span><span class="content">firstName</span><span class="delimiter">&quot;</span></span>)
.withOpeartor(QCriteriaOperator.EQUALS)
.withOtherFieldName(<span class="string"><span class="delimiter">&quot;</span><span class="content">lastName</span><span class="delimiter">&quot;</span></span>);
<span class="comment">// using otherFieldName to build a criterion that looks at two fields from join tables</span>
<span class="keyword">new</span> QFilterCriteria()
.withFieldName(<span class="string"><span class="delimiter">&quot;</span><span class="content">billToCustomer.lastName</span><span class="delimiter">&quot;</span></span>)
.withOpeartor(QCriteriaOperator.NOT_EQUALS)
.withOtherFieldName(<span class="string"><span class="delimiter">&quot;</span><span class="content">shipToCustomer.lastName</span><span class="delimiter">&quot;</span></span>);</code></pre>
</div>
</div>
</div>
<div class="sect5">
<h6 id="_qfilterorderby">QFilterOrderBy</h6>
<div class="ulist">
<ul>
<li>
<p><code>fieldName</code> - <strong>String, required</strong> - Reference to a field on the table being queried.</p>
<div class="ulist">
<ul>
<li>
<p>Or, in the case of a query with <strong>queryJoins</strong>, a qualified name of a field from a join-table (where the qualifier would be the joined table&#8217;s name or alias, followed by a dot)</p>
</li>
</ul>
</div>
</li>
<li>
<p><code>isAscending</code> - <strong>boolean, default: true</strong> - Specify if the sort is ascending or descending.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>QFilterCriteria definition examples:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="comment">// short-form, via constructors</span>
<span class="keyword">new</span> QFilterOrderBy(<span class="string"><span class="delimiter">&quot;</span><span class="content">id</span><span class="delimiter">&quot;</span></span>) <span class="comment">// isAscending defaults to true.</span>
<span class="keyword">new</span> QFilterOrderBy(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="predefined-constant">false</span>)
<span class="comment">// long-form, with fluent setters</span>
<span class="keyword">new</span> QFilterOrderBy()
.withFieldName(<span class="string"><span class="delimiter">&quot;</span><span class="content">birthDate</span><span class="delimiter">&quot;</span></span>)
.withIsAscending(<span class="predefined-constant">true</span>);</code></pre>
</div>
</div>
</div>
</div>
<div class="sect4">
<h5 id="_queryjoin">QueryJoin</h5>
<div class="ulist">
<ul>
<li>
<p><code>leftTableOrAlias</code> - <strong>String, required</strong> - Name of the table on the left side of the join.
If the table to be used here was given an alias from a previous <strong>queryJoin</strong>, then that alias name should be given here.</p>
<div class="ulist">
<ul>
<li>
<p>Will be inferred from <strong>joinMetaData</strong>, if <strong>leftTableOrAlias</strong> is not set when <strong>joinMetaData</strong> gets set (which will only use the leftTableName from the joinMetaData - never an alias)</p>
</li>
</ul>
</div>
</li>
<li>
<p><code>rightTable</code> - <strong>String, required</strong> - Name of the table on the right side of the join.</p>
<div class="ulist">
<ul>
<li>
<p>Will be inferred from <strong>joinMetaData</strong>, if <strong>rightTable</strong> is not set when <strong>joinMetaData</strong> gets set.</p>
</li>
</ul>
</div>
</li>
<li>
<p><code>joinMetaData</code> - <strong>QJoinMetaData object</strong> - Optional specification of a <a href="Joins{relfilesuffix}">QQQ Join</a> in the current QInstance.
If not set, will be looked up at runtime based on <strong>leftTableOrAlias</strong> and <strong>rightTable</strong>.</p>
<div class="ulist">
<ul>
<li>
<p>If set before <strong>leftTableOrAlias</strong> and <strong>rightTable</strong>, then they will be set based on the <strong>leftTable</strong> and <strong>rightTable</strong> in this object.</p>
</li>
</ul>
</div>
</li>
<li>
<p><code>alias</code> - <strong>String</strong> - Optional (unless multiple instances of the same table are being joined together, when it becomes required).
Behavior based on SQL <code>FROM</code> clause aliases.
If given, must be used as the part before the dot in field name specifications throughout the rest of the query input.</p>
</li>
<li>
<p><code>select</code> - <strong>boolean, default: false</strong> - Specify whether fields from the <strong>rightTable</strong> should be selected by the query.
If <strong>true</strong>, then the <code>QRecord</code> objects returned by this query will have values with corresponding to the (table-or-alias <code>.</code> field-name) form.</p>
</li>
<li>
<p><code>type</code> - <strong>Enum of INNER, LEFT, RIGHT, FULL, default: INNER</strong> - specifies the SQL-style type of join being performed.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>QueryJoin definition examples:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="comment">// selecting from an &quot;orderLine&quot; table joined to its corresponding &quot;order&quot; table</span>
queryInput.withQueryJoin(<span class="keyword">new</span> QueryJoin(<span class="string"><span class="delimiter">&quot;</span><span class="content">orderLine</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">order</span><span class="delimiter">&quot;</span></span>).withSelect(<span class="predefined-constant">true</span>));
...
queryOutput.getRecords().get(<span class="integer">0</span>).getValueBigDecimal(<span class="string"><span class="delimiter">&quot;</span><span class="content">order.grandTotal</span><span class="delimiter">&quot;</span></span>);
<span class="comment">// given an &quot;order&quot; table with 2 foreign keys to a customer table (billToCustomerId and shipToCustomerId)</span>
<span class="comment">// Note, we must supply the JoinMetaData to the QueryJoin, to drive what fields to join on in each case.</span>
queryInput.withQueryJoins(<span class="predefined-type">List</span>.of(
<span class="keyword">new</span> QueryJoin(instance.getJoin(<span class="string"><span class="delimiter">&quot;</span><span class="content">orderJoinShipToCustomer</span><span class="delimiter">&quot;</span></span>)
.withAlias(<span class="string"><span class="delimiter">&quot;</span><span class="content">shipToCustomer</span><span class="delimiter">&quot;</span></span>)
.withSelect(<span class="predefined-constant">true</span>)),
<span class="keyword">new</span> QueryJoin(instance.getJoin(<span class="string"><span class="delimiter">&quot;</span><span class="content">orderJoinBillToCustomer</span><span class="delimiter">&quot;</span></span>)
.withAlias(<span class="string"><span class="delimiter">&quot;</span><span class="content">billToCustomer</span><span class="delimiter">&quot;</span></span>)
.withSelect(<span class="predefined-constant">true</span>))));
...
record.getValueString(<span class="string"><span class="delimiter">&quot;</span><span class="content">billToCustomer.firstName</span><span class="delimiter">&quot;</span></span>)
+ <span class="string"><span class="delimiter">&quot;</span><span class="content"> placed an order for </span><span class="delimiter">&quot;</span></span>
+ record.getValueString(<span class="string"><span class="delimiter">&quot;</span><span class="content">shipToCustomer.firstName</span><span class="delimiter">&quot;</span></span>)</code></pre>
</div>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_queryoutput">QueryOutput</h4>
<div class="ulist">
<ul>
<li>
<p><code>records</code> - <strong>List of QRecord</strong> - List of 0 or more records that match the query filter.</p>
<div class="ulist">
<ul>
<li>
<p><em>Note: If a <strong>recordPipe</strong> was supplied to the QueryInput, then calling <code>queryOutput.getRecords()</code> will result in an <code>IllegalStateException</code> being thrown - as the records were placed into the pipe as they were fetched, and cannot all be accessed as a single list.</em></p>
</li>
</ul>
</div>
</li>
</ul>
</div>
<hr>
</div>
</div>
<div class="sect2">
<h3 id="_rendertemplateaction">RenderTemplateAction</h3>
<div class="paragraph">
<p>The <code><strong>RenderTemplateAction</strong></code> performs the job of taking a template - that is, a string of code, in a templating language, such as <a href="https://velocity.apache.org/engine/1.7/user-guide.html">Velocity</a>, and merging it with a set of data (known as a context), to produce some using-facing output, such as a String of HTML.</p>
</div>
<div class="sect3">
<h4 id="_examples_2">Examples</h4>
<div class="sect4">
<h5 id="_canonical_form">Canonical Form</h5>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java">RenderTemplateInput input = <span class="keyword">new</span> RenderTemplateInput(qInstance);
input.setSession(session);
input.setCode(<span class="string"><span class="delimiter">&quot;</span><span class="content">Hello, ${name}</span><span class="delimiter">&quot;</span></span>);
input.setTemplateType(TemplateType.VELOCITY);
input.setContext(<span class="predefined-type">Map</span>.of(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">Darin</span><span class="delimiter">&quot;</span></span>));
RenderTemplateOutput output = <span class="keyword">new</span> RenderTemplateAction.execute(input);
<span class="predefined-type">String</span> result = output.getResult();
assertEquals(<span class="string"><span class="delimiter">&quot;</span><span class="content">Hello, Darin</span><span class="delimiter">&quot;</span></span>, result);</code></pre>
</div>
</div>
</div>
<div class="sect4">
<h5 id="_convenient_form">Convenient Form</h5>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="predefined-type">String</span> result = RenderTemplateAction.renderVelocity(input, <span class="predefined-type">Map</span>.of(<span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">Darin</span><span class="delimiter">&quot;</span></span>), <span class="string"><span class="delimiter">&quot;</span><span class="content">Hello, ${name}</span><span class="delimiter">&quot;</span></span>);
assertEquals(<span class="string"><span class="delimiter">&quot;</span><span class="content">Hello, Darin</span><span class="delimiter">&quot;</span></span>, result);</code></pre>
</div>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_rendertemplateinput">RenderTemplateInput</h4>
<div class="ulist">
<ul>
<li>
<p><code>code</code> - <strong>String, Required</strong> - String of template code to be rendered, in the templating language specified by the <code>type</code> parameter.</p>
</li>
<li>
<p><code>type</code> - <strong>Enum of VELOCITY, Required</strong> - Specifies the language of the template code.</p>
</li>
<li>
<p><code>context</code> - <strong>Map of String → Object</strong> - Data to be made available to the template during rendering.</p>
</li>
</ul>
</div>
</div>
<div class="sect3">
<h4 id="_rendertemplateoutput">RenderTemplateOutput</h4>
<div class="ulist">
<ul>
<li>
<p><code>result</code> - <strong>String</strong> - Result of rendering the input template and context.</p>
</li>
</ul>
</div>
<hr>
</div>
</div>
</div>
</div>
</div>
<div id="footer">
<div id="footer-text">
Last updated 2022-11-23 15:52:31 -0600
</div>
</div>
</body>
</html>