~ubuntu-branches/ubuntu/trusty/grantlee/trusty

« back to all changes in this revision

Viewing changes to corelib/parser.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 "parser.h"
 
22
 
 
23
#include <QtCore/QFile>
 
24
 
 
25
#include "nodebuiltins_p.h"
 
26
#include "taglibraryinterface.h"
 
27
#include "template.h"
 
28
#include "template_p.h"
 
29
#include "engine.h"
 
30
#include "filter.h"
 
31
#include "exception.h"
 
32
 
 
33
#include "grantlee_version.h"
 
34
 
 
35
using namespace Grantlee;
 
36
 
 
37
namespace Grantlee
 
38
{
 
39
 
 
40
class ParserPrivate
 
41
{
 
42
public:
 
43
  ParserPrivate( Parser *parser, const QList<Token> &tokenList )
 
44
      : q_ptr( parser ),
 
45
      m_tokenList( tokenList ) {
 
46
 
 
47
  }
 
48
 
 
49
  NodeList extendNodeList( NodeList list, Node *node );
 
50
 
 
51
  /**
 
52
    Parses the template to create a Nodelist.
 
53
    The given @p parent is the parent of each node in the returned list.
 
54
  */
 
55
  NodeList parse( QObject *parent, const QStringList &stopAt = QStringList() );
 
56
 
 
57
  void openLibrary( TagLibraryInterface * library );
 
58
  Q_DECLARE_PUBLIC( Parser )
 
59
  Parser *q_ptr;
 
60
 
 
61
  QList<Token> m_tokenList;
 
62
 
 
63
  QHash<QString, AbstractNodeFactory*> m_nodeFactories;
 
64
  QHash<QString, Filter::Ptr> m_filters;
 
65
 
 
66
  NodeList m_nodeList;
 
67
};
 
68
 
 
69
}
 
70
 
 
71
void ParserPrivate::openLibrary( TagLibraryInterface *library )
 
72
{
 
73
  Q_Q( Parser );
 
74
  QHashIterator<QString, AbstractNodeFactory*> nodeIt( library->nodeFactories() );
 
75
 
 
76
  TemplateImpl *ti = qobject_cast<TemplateImpl *>( q->parent() );
 
77
 
 
78
  Engine const *cengine = ti->engine();
 
79
  if ( !cengine )
 
80
    return;
 
81
  Engine *engine = const_cast<Engine *>( cengine );
 
82
 
 
83
  while ( nodeIt.hasNext() ) {
 
84
    nodeIt.next();
 
85
    nodeIt.value()->setEngine( engine );
 
86
    m_nodeFactories.insert( nodeIt.key(), nodeIt.value() );
 
87
  }
 
88
  QHashIterator<QString, Filter*> filterIt( library->filters() );
 
89
  while ( filterIt.hasNext() ) {
 
90
    filterIt.next();
 
91
    Filter::Ptr f = Filter::Ptr( filterIt.value() );
 
92
    m_filters.insert( filterIt.key(), f );
 
93
  }
 
94
}
 
95
 
 
96
Parser::Parser( const QList<Token> &tokenList, QObject *parent )
 
97
    : QObject( parent ), d_ptr( new ParserPrivate( this, tokenList ) )
 
98
{
 
99
  Q_D( Parser );
 
100
 
 
101
  TemplateImpl *ti = qobject_cast<TemplateImpl *>( parent );
 
102
 
 
103
  Engine const *cengine = ti->engine();
 
104
  if ( !cengine )
 
105
    return;
 
106
  Engine *engine = const_cast<Engine *>( cengine );
 
107
  engine->loadDefaultLibraries();
 
108
  Q_FOREACH( const QString &libraryName, engine->defaultLibraries() ) {
 
109
    TagLibraryInterface *library = engine->loadLibrary( libraryName );
 
110
    if ( !library )
 
111
      continue;
 
112
    d->openLibrary( library );
 
113
  }
 
114
}
 
115
 
 
116
Parser::~Parser()
 
117
{
 
118
  // Don't delete filters here because filters must out-live the parser in the
 
119
  // filter expressions.
 
120
  qDeleteAll( d_ptr->m_nodeFactories );
 
121
  delete d_ptr;
 
122
}
 
123
 
 
124
 
 
125
void Parser::setTokens( const QList< Token >& tokenList )
 
126
{
 
127
  Q_D( Parser );
 
128
  d->m_tokenList = tokenList;
 
129
}
 
130
 
 
131
 
 
132
void Parser::loadLib( const QString &name )
 
133
{
 
134
  Q_D( Parser );
 
135
  TemplateImpl *ti = qobject_cast<TemplateImpl *>( parent() );
 
136
  Engine const *cengine = ti->engine();
 
137
  if ( !cengine )
 
138
    return;
 
139
  Engine *engine = const_cast<Engine *>( cengine );
 
140
  TagLibraryInterface *library = engine->loadLibrary( name );
 
141
  if ( !library )
 
142
    return;
 
143
  d->openLibrary( library );
 
144
}
 
145
 
 
146
NodeList ParserPrivate::extendNodeList( NodeList list, Node *node )
 
147
{
 
148
  if ( node->mustBeFirst() && list.containsNonText() ) {
 
149
    throw Grantlee::Exception( TagSyntaxError, QString(
 
150
        "Node appeared twice in template: %1" ).arg( node->metaObject()->className() ) );
 
151
  }
 
152
 
 
153
  list.append( node );
 
154
  return list;
 
155
}
 
156
 
 
157
void Parser::skipPast( const QString &tag )
 
158
{
 
159
  while ( hasNextToken() ) {
 
160
    Token token = takeNextToken();
 
161
    if ( token.tokenType == BlockToken && token.content.trimmed() == tag )
 
162
      return;
 
163
  }
 
164
  throw Grantlee::Exception( UnclosedBlockTagError, QString( "No closing tag found for %1" ).arg( tag ) );
 
165
}
 
166
 
 
167
Filter::Ptr Parser::getFilter( const QString &name ) const
 
168
{
 
169
  Q_D( const Parser );
 
170
  if ( !d->m_filters.contains( name ) )
 
171
    throw Grantlee::Exception( UnknownFilterError, QString( "Unknown filter: %1" ).arg( name ) );
 
172
  return d->m_filters.value( name );
 
173
}
 
174
 
 
175
NodeList Parser::parse( Node *parent, const QString &stopAt )
 
176
{
 
177
  Q_D( Parser );
 
178
  return d->parse( parent, QStringList() << stopAt );
 
179
}
 
180
 
 
181
NodeList Parser::parse( TemplateImpl *parent, const QStringList &stopAt )
 
182
{
 
183
  Q_D( Parser );
 
184
  return d->parse( parent, stopAt );
 
185
}
 
186
 
 
187
NodeList Parser::parse( Node *parent, const QStringList &stopAt )
 
188
{
 
189
  Q_D( Parser );
 
190
  return d->parse( parent, stopAt );
 
191
}
 
192
 
 
193
NodeList ParserPrivate::parse( QObject *parent, const QStringList &stopAt )
 
194
{
 
195
  Q_Q( Parser );
 
196
  NodeList nodeList;
 
197
 
 
198
  while ( q->hasNextToken() ) {
 
199
    Token token = q->takeNextToken();
 
200
    if ( token.tokenType == TextToken ) {
 
201
      nodeList = extendNodeList( nodeList, new TextNode( token.content, parent ) );
 
202
    } else if ( token.tokenType == VariableToken ) {
 
203
      if ( token.content.isEmpty() ) {
 
204
        // Error. Empty variable
 
205
        QString message;
 
206
        if ( q->hasNextToken() )
 
207
          message = QString( "Empty variable before \"%1\", line %2, %3" ).arg( q->takeNextToken().content.left( 20 ) ).arg( token.linenumber ).arg( q->parent()->objectName() );
 
208
        else
 
209
          message = QString( "Empty variable at end of input." );
 
210
 
 
211
        throw Grantlee::Exception( EmptyVariableError, message );
 
212
      }
 
213
 
 
214
      FilterExpression filterExpression;
 
215
      try {
 
216
        filterExpression = FilterExpression( token.content, q );
 
217
      } catch ( Grantlee::Exception e )
 
218
      {
 
219
        throw Grantlee::Exception( e.errorCode(), QString( "%1, line %2, %3" ).arg( e.what() )
 
220
                                                                              .arg( token.linenumber )
 
221
                                                                              .arg( q->parent()->objectName() ) );
 
222
      }
 
223
 
 
224
      nodeList = extendNodeList( nodeList, new VariableNode( filterExpression, parent ) );
 
225
    } else if ( token.tokenType == BlockToken ) {
 
226
      if ( stopAt.contains( token.content ) ) {
 
227
        // put the token back.
 
228
        q->prependToken( token );
 
229
        return nodeList;
 
230
      }
 
231
 
 
232
      QStringList tagContents = token.content.split( ' ' );
 
233
      if ( tagContents.size() == 0 ) {
 
234
        QString message;
 
235
        if ( q->hasNextToken() )
 
236
          message = QString( "Empty block tag before \"%1\", line %2, %3" ).arg( token.content.left( 20 ) ).arg( token.linenumber ).arg( q->parent()->objectName() );
 
237
        else
 
238
          message = QString( "Empty block tag at end of input." );
 
239
 
 
240
        throw Grantlee::Exception( EmptyBlockTagError, message );
 
241
      }
 
242
      QString command = tagContents.at( 0 );
 
243
      AbstractNodeFactory *nodeFactory = m_nodeFactories[command];
 
244
 
 
245
      // unknown tag.
 
246
      if ( !nodeFactory ) {
 
247
        throw Grantlee::Exception( InvalidBlockTagError, QString( "Unknown tag: \"%1\", line %2, %3" ).arg( command ).arg( token.linenumber ).arg( q->parent()->objectName() ) );
 
248
      }
 
249
 
 
250
      // TODO: Make getNode take a Token instead?
 
251
      Node *n;
 
252
      try {
 
253
        n = nodeFactory->getNode( token.content, q );
 
254
      } catch ( Grantlee::Exception e ){
 
255
        throw Grantlee::Exception( e.errorCode(), QString( "%1, line %2, %3" ).arg( e.what() )
 
256
                                                                              .arg( token.linenumber )
 
257
                                                                              .arg( q->parent()->objectName() ) );
 
258
      }
 
259
      if ( !n ) {
 
260
        throw Grantlee::Exception( EmptyBlockTagError, QString( "Failed to get node from %1, line %2, %3" ).arg( command ).arg( token.linenumber ).arg( q->parent()->objectName() ) );
 
261
      }
 
262
 
 
263
      n->setParent( parent );
 
264
 
 
265
      nodeList = extendNodeList( nodeList, n );
 
266
    }
 
267
  }
 
268
 
 
269
  if ( !stopAt.isEmpty() ) {
 
270
    QString message = QString( "Unclosed tag in template %1. Expected one of: (%2)" ).arg( q->parent()->objectName() ).arg( stopAt.join( " " ) );
 
271
    throw Grantlee::Exception( UnclosedBlockTagError, message );
 
272
  }
 
273
 
 
274
  return nodeList;
 
275
 
 
276
}
 
277
 
 
278
bool Parser::hasNextToken() const
 
279
{
 
280
  Q_D( const Parser );
 
281
  return d->m_tokenList.size() > 0;
 
282
}
 
283
 
 
284
Token Parser::takeNextToken()
 
285
{
 
286
  Q_D( Parser );
 
287
  return d->m_tokenList.takeAt( 0 );
 
288
}
 
289
 
 
290
void Parser::removeNextToken()
 
291
{
 
292
  Q_D( Parser );
 
293
  if ( !d->m_tokenList.isEmpty() )
 
294
    d->m_tokenList.removeAt( 0 );
 
295
}
 
296
 
 
297
void Parser::prependToken( const Token &token )
 
298
{
 
299
  Q_D( Parser );
 
300
  d->m_tokenList.prepend( token );
 
301
}
 
302
 
 
303
#include "parser.moc"
 
304