2
This file is part of the Grantlee template system.
4
Copyright (c) 2009,2010 Stephen Kelly <steveire@gmail.com>
6
This library is free software; you can redistribute it and/or
7
modify it under the terms of the GNU Lesser General Public
8
License as published by the Free Software Foundation; either version
9
2 of the Licence, or (at your option) any later version.
11
This library is distributed in the hope that it will be useful,
12
but WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
Library General Public License for more details.
16
You should have received a copy of the GNU Lesser General Public
17
License along with this library. If not, see <http://www.gnu.org/licenses/>.
23
#include <QtCore/QStringList>
26
#include "exception.h"
29
ForNodeFactory::ForNodeFactory()
34
Node* ForNodeFactory::getNode( const QString &tagContent, Parser *p ) const
36
QStringList expr = smartSplit( tagContent );
38
if ( expr.size() < 4 ) {
39
throw Grantlee::Exception( TagSyntaxError,
40
QString( "'for' statements should have at least four words: %1" ).arg( tagContent ) );
46
int reversed = ForNode::IsNotReversed;
47
if ( expr.last() == "reversed" ) {
48
reversed = ForNode::IsReversed;
52
if ( expr.mid( expr.size() - 2 ).at( 0 ) != "in" ) {
53
throw Grantlee::Exception( TagSyntaxError,
54
QString( "'for' statements should use the form 'for x in y': %1" ).arg( tagContent ) );
57
Q_FOREACH( const QString &arg, expr.mid( 0, expr.size() - 2 ) ) {
58
vars << arg.split( ',', QString::SkipEmptyParts );
61
Q_FOREACH( const QString &var, vars ) {
63
throw Grantlee::Exception( TagSyntaxError, "'for' tag received invalid argument" );
66
FilterExpression fe( expr.last(), p );
68
ForNode *n = new ForNode( vars, fe, reversed, p );
70
NodeList loopNodes = p->parse( n, QStringList() << "empty" << "endfor" );
71
n->setLoopList( loopNodes );
74
if ( p->takeNextToken().content.trimmed() == "empty" ) {
75
emptyNodes = p->parse( n, QStringList() << "endfor" );
76
n->setEmptyList( emptyNodes );
77
// skip past the endfor tag
86
ForNode::ForNode( QStringList loopVars,
91
m_loopVars( loopVars ),
92
m_filterExpression( fe ),
93
m_isReversed( reversed )
98
void ForNode::setLoopList( NodeList loopNodeList )
100
m_loopNodeList = loopNodeList;
103
void ForNode::setEmptyList( NodeList emptyList )
105
m_emptyNodeList = emptyList;
108
// some magic variables injected into the context while rendering.
109
const QString forloop( "forloop" );
110
const QString parentloop( "parentloop" );
111
const QString counter0( "counter0" );
112
const QString counter( "counter" );
113
const QString revcounter0( "revcounter0" );
114
const QString revcounter( "revcounter" );
115
const QString first( "first" );
116
const QString last( "last" );
118
void ForNode::insertLoopVariables( Context *c, int listSize, int i )
120
QVariantHash forloopHash = c->lookup( "forloop" ).toHash();
121
forloopHash.insert( counter0, i );
122
forloopHash.insert( counter, i + 1 );
123
forloopHash.insert( revcounter, listSize - i );
124
forloopHash.insert( revcounter0, listSize - i - 1 );
125
forloopHash.insert( first, ( i == 0 ) );
126
forloopHash.insert( last, ( i == listSize - 1 ) );
127
c->insert( forloop, forloopHash );
130
void ForNode::renderLoop( OutputStream *stream, Context *c )
132
for ( int j = 0; j < m_loopNodeList.size();j++ ) {
133
m_loopNodeList[j]->render( stream, c );
137
void ForNode::handleHashItem( OutputStream *stream, Context *c, QString key, QVariant value, int listSize, int i, bool unpack )
140
insertLoopVariables( c, listSize, i );
143
// Iterating over a hash but not unpacking it.
144
// convert each key-value pair to a list and insert it in the context.
145
list << key << value;
146
c->insert( m_loopVars.at( 0 ), list );
149
c->insert( m_loopVars.at( 0 ), key );
150
c->insert( m_loopVars.at( 1 ), value );
152
renderLoop( stream, c );
155
void ForNode::iterateHash( OutputStream *stream, Context *c, QVariantHash varHash, bool unpack )
157
int listSize = varHash.size();
161
QHashIterator<QString, QVariant> it( varHash );
162
if ( m_isReversed == IsReversed ) {
163
while ( it.hasPrevious() ) {
165
handleHashItem( stream, c, it.key(), it.value(), listSize, i, unpack );
169
while ( it.hasNext() ) {
171
handleHashItem( stream, c, it.key(), it.value(), listSize, i, unpack );
177
void ForNode::render( OutputStream *stream, Context *c )
179
QVariantHash forloopHash;
181
QVariant parentLoopVariant = c->lookup( forloop );
182
if ( parentLoopVariant.isValid() ) {
183
// This is a nested loop.
184
forloopHash = parentLoopVariant.toHash();
185
forloopHash.insert( parentloop, parentLoopVariant.toHash() );
186
c->insert( forloop, forloopHash );
189
bool unpack = m_loopVars.size() > 1;
193
// if ( var.type() == QVariant::Hash ) {
194
// QVariantHash varHash = var.toHash();
195
// result = iterateHash( c, varHash, unpack );
200
// If it's an iterable type, iterate, otherwise it's a list of one.
201
QVariantList varList = m_filterExpression.toList( c );
203
int listSize = varList.size();
205
if ( listSize < 1 ) {
207
return m_emptyNodeList.render( stream, c );
210
for ( int i = 0; i < listSize; i++ ) {
211
insertLoopVariables( c, listSize, i );
214
if ( m_isReversed == IsReversed ) {
215
index = listSize - i - 1;
219
if ( varList[index].type() == QVariant::List ) {
220
QVariantList vList = varList[index].toList();
221
int varsSize = qMin( m_loopVars.size(), vList.size() );
223
for ( ; j < varsSize; ++j ) {
224
c->insert( m_loopVars.at( j ), vList.at( j ) );
226
// If any of the named vars don't have an item in the context,
227
// insert an invalid object for them.
228
for ( ; j < m_loopVars.size(); ++j ) {
229
c->insert( m_loopVars.at( j ), QVariant() );
233
// We don't have a hash, but we have to unpack several values from each item
234
// in the list. And each item in the list is not itself a list.
235
// Probably have a list of objects that we're taking properties from.
236
Q_FOREACH( const QString &loopVar, m_loopVars ) {
238
c->insert( "var", varList[index] );
239
QVariant v = FilterExpression( "var." + loopVar, 0 ).resolve( c );
241
c->insert( loopVar, v );
245
c->insert( m_loopVars[0], varList[index] );
247
renderLoop( stream, c );