~ubuntu-branches/ubuntu/precise/grantlee/precise

« back to all changes in this revision

Viewing changes to defaulttags/for.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Harald Sitter
  • Date: 2010-06-11 23:41:45 UTC
  • Revision ID: james.westby@ubuntu.com-20100611234145-oas7rhdrbwy8j55c
Tags: upstream-0.1.1
ImportĀ upstreamĀ versionĀ 0.1.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
  This file is part of the Grantlee template system.
 
3
 
 
4
  Copyright (c) 2009,2010 Stephen Kelly <steveire@gmail.com>
 
5
 
 
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.
 
10
 
 
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.
 
15
 
 
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/>.
 
18
 
 
19
*/
 
20
 
 
21
#include "for.h"
 
22
 
 
23
#include <QtCore/QStringList>
 
24
 
 
25
#include "parser.h"
 
26
#include "exception.h"
 
27
 
 
28
 
 
29
ForNodeFactory::ForNodeFactory()
 
30
{
 
31
 
 
32
}
 
33
 
 
34
Node* ForNodeFactory::getNode( const QString &tagContent, Parser *p ) const
 
35
{
 
36
  QStringList expr = smartSplit( tagContent );
 
37
 
 
38
  if ( expr.size() < 4 ) {
 
39
    throw Grantlee::Exception( TagSyntaxError,
 
40
        QString( "'for' statements should have at least four words: %1" ).arg( tagContent ) );
 
41
  }
 
42
 
 
43
  expr.takeAt( 0 );
 
44
  QStringList vars;
 
45
 
 
46
  int reversed = ForNode::IsNotReversed;
 
47
  if ( expr.last() == "reversed" ) {
 
48
    reversed = ForNode::IsReversed;
 
49
    expr.removeLast();
 
50
  }
 
51
 
 
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 ) );
 
55
  }
 
56
 
 
57
  Q_FOREACH( const QString &arg, expr.mid( 0, expr.size() - 2 ) ) {
 
58
    vars << arg.split( ',', QString::SkipEmptyParts );
 
59
  }
 
60
 
 
61
  Q_FOREACH( const QString &var, vars ) {
 
62
    if ( var.isNull() )
 
63
      throw Grantlee::Exception( TagSyntaxError, "'for' tag received invalid argument" );
 
64
  }
 
65
 
 
66
  FilterExpression fe( expr.last(), p );
 
67
 
 
68
  ForNode *n = new ForNode( vars, fe, reversed, p );
 
69
 
 
70
  NodeList loopNodes = p->parse( n, QStringList() << "empty" << "endfor" );
 
71
  n->setLoopList( loopNodes );
 
72
 
 
73
  NodeList emptyNodes;
 
74
  if ( p->takeNextToken().content.trimmed() == "empty" ) {
 
75
    emptyNodes = p->parse( n, QStringList() << "endfor" );
 
76
    n->setEmptyList( emptyNodes );
 
77
    // skip past the endfor tag
 
78
    p->removeNextToken();
 
79
  }
 
80
 
 
81
  return n;
 
82
}
 
83
 
 
84
 
 
85
 
 
86
ForNode::ForNode( QStringList loopVars,
 
87
                  FilterExpression fe,
 
88
                  int reversed,
 
89
                  QObject *parent )
 
90
    : Node( parent ),
 
91
    m_loopVars( loopVars ),
 
92
    m_filterExpression( fe ),
 
93
    m_isReversed( reversed )
 
94
{
 
95
 
 
96
}
 
97
 
 
98
void ForNode::setLoopList( NodeList loopNodeList )
 
99
{
 
100
  m_loopNodeList = loopNodeList;
 
101
}
 
102
 
 
103
void ForNode::setEmptyList( NodeList emptyList )
 
104
{
 
105
  m_emptyNodeList = emptyList;
 
106
}
 
107
 
 
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" );
 
117
 
 
118
void ForNode::insertLoopVariables( Context *c, int listSize, int i )
 
119
{
 
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 );
 
128
}
 
129
 
 
130
void ForNode::renderLoop( OutputStream *stream, Context *c )
 
131
{
 
132
  for ( int j = 0; j < m_loopNodeList.size();j++ ) {
 
133
    m_loopNodeList[j]->render( stream, c );
 
134
  }
 
135
}
 
136
 
 
137
void ForNode::handleHashItem( OutputStream *stream, Context *c, QString key, QVariant value, int listSize, int i, bool unpack )
 
138
{
 
139
  QVariantList list;
 
140
  insertLoopVariables( c, listSize, i );
 
141
 
 
142
  if ( !unpack ) {
 
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 );
 
147
    list.clear();
 
148
  } else {
 
149
    c->insert( m_loopVars.at( 0 ), key );
 
150
    c->insert( m_loopVars.at( 1 ), value );
 
151
  }
 
152
  renderLoop( stream, c );
 
153
}
 
154
 
 
155
void ForNode::iterateHash( OutputStream *stream, Context *c, QVariantHash varHash, bool unpack )
 
156
{
 
157
  int listSize = varHash.size();
 
158
  int i = 0;
 
159
  QVariantList list;
 
160
 
 
161
  QHashIterator<QString, QVariant> it( varHash );
 
162
  if ( m_isReversed == IsReversed ) {
 
163
    while ( it.hasPrevious() ) {
 
164
      it.previous();
 
165
      handleHashItem( stream, c, it.key(), it.value(), listSize, i, unpack );
 
166
      ++i;
 
167
    }
 
168
  } else {
 
169
    while ( it.hasNext() ) {
 
170
      it.next();
 
171
      handleHashItem( stream, c, it.key(), it.value(), listSize, i, unpack );
 
172
      ++i;
 
173
    }
 
174
  }
 
175
}
 
176
 
 
177
void ForNode::render( OutputStream *stream, Context *c )
 
178
{
 
179
  QVariantHash forloopHash;
 
180
 
 
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 );
 
187
  }
 
188
 
 
189
  bool unpack = m_loopVars.size() > 1;
 
190
 
 
191
  c->push();
 
192
 
 
193
//   if ( var.type() == QVariant::Hash ) {
 
194
//     QVariantHash varHash = var.toHash();
 
195
//     result = iterateHash( c, varHash, unpack );
 
196
//     c->pop();
 
197
//     return result;
 
198
//   }
 
199
 
 
200
  // If it's an iterable type, iterate, otherwise it's a list of one.
 
201
  QVariantList varList = m_filterExpression.toList( c );
 
202
  NodeList nodeList;
 
203
  int listSize = varList.size();
 
204
 
 
205
  if ( listSize < 1 ) {
 
206
    c->pop();
 
207
    return m_emptyNodeList.render( stream, c );
 
208
  }
 
209
 
 
210
  for ( int i = 0; i < listSize; i++ ) {
 
211
    insertLoopVariables( c, listSize, i );
 
212
 
 
213
    int index = i;
 
214
    if ( m_isReversed == IsReversed ) {
 
215
      index = listSize - i - 1;
 
216
    }
 
217
 
 
218
    if ( unpack ) {
 
219
      if ( varList[index].type() == QVariant::List ) {
 
220
        QVariantList vList = varList[index].toList();
 
221
        int varsSize = qMin( m_loopVars.size(), vList.size() );
 
222
        int j = 0;
 
223
        for ( ; j < varsSize; ++j ) {
 
224
          c->insert( m_loopVars.at( j ), vList.at( j ) );
 
225
        }
 
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() );
 
230
        }
 
231
 
 
232
      } else {
 
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 ) {
 
237
          c->push();
 
238
          c->insert( "var", varList[index] );
 
239
          QVariant v = FilterExpression( "var." + loopVar, 0 ).resolve( c );
 
240
          c->pop();
 
241
          c->insert( loopVar, v );
 
242
        }
 
243
      }
 
244
    } else {
 
245
      c->insert( m_loopVars[0], varList[index] );
 
246
    }
 
247
    renderLoop( stream, c );
 
248
  }
 
249
  c->pop();
 
250
}
 
251