Use a QVector of filters instead of a QList.
[grantlee:pinos-grantlee.git] / templates / lib / filterexpression.cpp
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.1 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   Lesser 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 "filterexpression.h"
22
23 #include "exception.h"
24 #include "filter.h"
25 #include "grantlee_latin1literal_p.h"
26 #include "metatype.h"
27 #include "parser.h"
28 #include "util.h"
29
30 typedef QPair<Grantlee::Filter::Ptr, Grantlee::Variable> ArgFilter;
31
32 namespace Grantlee
33 {
34
35 class FilterExpressionPrivate
36 {
37   FilterExpressionPrivate( FilterExpression *fe )
38       : q_ptr( fe )
39   {
40
41   }
42
43   Variable m_variable;
44   QVector<ArgFilter> m_filters;
45   QStringList m_filterNames;
46
47   Q_DECLARE_PUBLIC( FilterExpression )
48   FilterExpression * const q_ptr;
49 };
50
51 }
52
53 using namespace Grantlee;
54
55 static const char FILTER_SEPARATOR = '|';
56 static const char FILTER_ARGUMENT_SEPARATOR = ':';
57
58 static QRegExp getFilterRegexp()
59 {
60   const QString filterSep( QRegExp::escape( QChar::fromLatin1( FILTER_SEPARATOR ) ) );
61   const QString argSep( QRegExp::escape( QChar::fromLatin1( FILTER_ARGUMENT_SEPARATOR ) ) );
62
63   const QLatin1Literal varChars( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_." );
64   const QLatin1Literal numChars( "[-+\\.]?\\d[\\d\\.e]*" );
65   const QString i18nOpen( QRegExp::escape( QLatin1String( "_(" ) ) );
66   const QLatin1Literal doubleQuoteStringLiteral( "\"[^\"\\\\]*(?:\\\\.[^\"\\\\]*)*\"" );
67   const QLatin1Literal singleQuoteStringLiteral( "\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'" );
68   const QString i18nClose( QRegExp::escape( QLatin1String( ")" ) ) );
69   const QString variable = QLatin1Char( '[' ) + varChars + QLatin1Literal( "]+");
70
71   const QString localizedExpression = QLatin1Literal( "(?:" ) + i18nOpen + doubleQuoteStringLiteral + i18nClose + QLatin1Char( '|' )
72                                                               + i18nOpen + singleQuoteStringLiteral + i18nClose + QLatin1Char( '|' )
73                                                               + i18nOpen + numChars                 + i18nClose + QLatin1Char( '|' )
74                                                               + i18nOpen + variable                 + i18nClose + QLatin1Char( ')' );
75
76   const QString constantString = QLatin1Literal( "(?:" ) + doubleQuoteStringLiteral + QLatin1Char( '|' )
77                                                          + singleQuoteStringLiteral
78                                    + QLatin1Char( ')' );
79
80   const QString filterRawString = QLatin1Char( '^' ) + constantString + QLatin1Char( '|' )
81                                + QLatin1Char( '^' ) + localizedExpression + QLatin1Char( '|' )
82                                + QLatin1Char( '^' ) + variable + QLatin1Char( '|' )
83                                + numChars + QLatin1Char( '|' )
84                                + filterSep + QLatin1Literal( "\\w+|" )
85                                + argSep
86                                + QLatin1Literal( "(?:" )
87                                  + constantString + QLatin1Char( '|' ) + localizedExpression
88                                  + QLatin1Char( '|' )
89                                  + variable
90                                  + QLatin1Char( '|' )
91                                  + numChars
92                                  + QLatin1Char( '|' )
93                                  + filterSep
94                                + QLatin1Literal( "\\w+)" );
95
96   return QRegExp( filterRawString );
97 }
98
99 FilterExpression::FilterExpression( const QString &varString, Parser *parser )
100   : d_ptr( new FilterExpressionPrivate( this ) )
101 {
102   Q_D( FilterExpression );
103
104   int pos = 0;
105   int lastPos = 0;
106   int len;
107   QString subString;
108
109   QString vs = varString;
110
111   static const QRegExp sFilterRe = getFilterRegexp();
112
113   // This is one fo the few contstructors that can throw so we make sure to delete its d->pointer.
114   try {
115     while ( ( pos = sFilterRe.indexIn( vs, pos ) ) != -1 ) {
116       len = sFilterRe.matchedLength();
117       subString = vs.mid( pos, len );
118       const int ssSize = subString.size();
119
120       if ( pos != lastPos ) {
121         throw Grantlee::Exception( TagSyntaxError,
122             QString::fromLatin1( "Could not parse some characters: \"%1\"" ).arg( vs.mid( lastPos, pos ) ) );
123       }
124
125       if ( subString.startsWith( QLatin1Char( FILTER_SEPARATOR ) ) ) {
126         subString = subString.right( ssSize - 1 );
127         Filter::Ptr f = parser->getFilter( subString );
128
129         Q_ASSERT( f );
130
131         d->m_filterNames << subString;
132         d->m_filters << qMakePair<Filter::Ptr, Variable>( f, Variable() );
133
134       } else if ( subString.startsWith( QLatin1Char( FILTER_ARGUMENT_SEPARATOR ) ) ) {
135         subString = subString.right( ssSize - 1 );
136         const int lastFilter = d->m_filters.size();
137         if ( subString.startsWith( QLatin1Char( FILTER_SEPARATOR ) ) )
138           throw Grantlee::Exception( EmptyVariableError,
139               QString::fromLatin1( "Missing argument to filter: %1" ).arg( d->m_filterNames[lastFilter -1] ) );
140
141         d->m_filters[lastFilter -1].second = Variable( subString );
142       } else {
143         // Token is _("translated"), or "constant", or a variable;
144         d->m_variable = Variable( subString );
145       }
146
147       pos += len;
148       lastPos = pos;
149     }
150
151     const QString remainder = vs.right( vs.size() - lastPos );
152     if ( !remainder.isEmpty() ) {
153       throw Grantlee::Exception( TagSyntaxError,
154           QString::fromLatin1( "Could not parse the remainder, %1 from %2" ).arg( remainder ).arg( varString ) );
155     }
156   } catch ( ... ) {
157     delete d_ptr;
158     throw;
159   }
160 }
161
162 FilterExpression::FilterExpression( const FilterExpression &other )
163     : d_ptr( new FilterExpressionPrivate( this ) )
164 {
165   *this = other;
166 }
167
168 FilterExpression::FilterExpression()
169     : d_ptr( new FilterExpressionPrivate( this ) )
170 {
171 }
172
173 bool FilterExpression::isValid() const
174 {
175   Q_D( const FilterExpression );
176   return d->m_variable.isValid();
177 }
178
179 FilterExpression::~FilterExpression()
180 {
181   delete d_ptr;
182 }
183
184 Variable FilterExpression::variable() const
185 {
186   Q_D( const FilterExpression );
187   return d->m_variable;
188 }
189
190 FilterExpression &FilterExpression::operator=( const FilterExpression & other )
191 {
192   if (&other == this)
193     return *this;
194   d_ptr->m_variable = other.d_ptr->m_variable;
195   d_ptr->m_filters = other.d_ptr->m_filters;
196   d_ptr->m_filterNames = other.d_ptr->m_filterNames;
197   return *this;
198 }
199
200 QVariant FilterExpression::resolve( OutputStream *stream, Context *c ) const
201 {
202   Q_D( const FilterExpression );
203   QVariant var = d->m_variable.resolve( c );
204
205   QVector<ArgFilter>::const_iterator it = d->m_filters.constBegin();
206   const QVector<ArgFilter>::const_iterator end = d->m_filters.constEnd();
207   for ( ; it != end; ++it ) {
208     Filter::Ptr filter = it->first;
209     filter->setStream( stream );
210     const Variable argVar = it->second;
211     QVariant arg = argVar.resolve( c );
212
213     if ( arg.isValid() ) {
214       Grantlee::SafeString argString;
215       if ( arg.userType() == qMetaTypeId<Grantlee::SafeString>() ) {
216         argString = arg.value<Grantlee::SafeString>();
217       } else if ( arg.type() == QVariant::String ) {
218         argString = Grantlee::SafeString( arg.toString() );
219       }
220       if ( argVar.isConstant() ) {
221         argString = markSafe( argString );
222       }
223       if ( !argString.get().isEmpty() ) {
224         arg = argString;
225       }
226     }
227
228     const SafeString varString = getSafeString( var );
229
230     var = filter->doFilter( var, arg, c->autoEscape() );
231
232     if ( var.userType() == qMetaTypeId<Grantlee::SafeString>() || var.type() == QVariant::String ) {
233       if ( filter->isSafe() && varString.isSafe() ) {
234         var = markSafe( getSafeString( var ) );
235       } else if ( varString.needsEscape() ) {
236         var = markForEscaping( getSafeString( var ) );
237       } else {
238         var = getSafeString( var );
239       }
240     }
241   }
242   ( *stream ) << getSafeString( var ).get();
243   return var;
244 }
245
246 QVariant FilterExpression::resolve( Context *c ) const
247 {
248   OutputStream _dummy;
249   return resolve( &_dummy, c );
250 }
251
252 QVariantList FilterExpression::toList( Context *c ) const
253 {
254   const QVariant var = resolve( c );
255   return MetaType::toVariantList( var );
256 }
257
258 bool FilterExpression::isTrue( Context *c ) const
259 {
260   return variantIsTrue( resolve( c ) );
261 }
262
263 QStringList FilterExpression::filters() const
264 {
265   Q_D( const FilterExpression );
266   return d->m_filterNames;
267 }
268