Don't convert a QVariant with a QStringList of size one to a QString.
[grantlee:grantlee.git] / templates / lib / variable.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 "variable.h"
22
23 #include "abstractlocalizer.h"
24 #include "context.h"
25 #include "exception.h"
26 #include "metaenumvariable_p.h"
27 #include "metatype.h"
28 #include "util.h"
29
30 #include <QtCore/QMetaEnum>
31 #include <QtCore/QStringList>
32
33 using namespace Grantlee;
34
35 namespace Grantlee
36 {
37
38 class VariablePrivate
39 {
40 public:
41   VariablePrivate( Variable *variable )
42       : q_ptr( variable ),
43         m_localize( false )
44   {
45
46   }
47
48   Q_DECLARE_PUBLIC( Variable )
49   Variable * const q_ptr;
50
51   QString m_varString;
52   QVariant m_literal;
53   QStringList m_lookups;
54   bool m_localize;
55 };
56
57 }
58
59 Variable::Variable( const Variable &other )
60     : d_ptr( new VariablePrivate( this ) )
61 {
62   d_ptr->m_varString = other.d_ptr->m_varString;
63   d_ptr->m_literal = other.d_ptr->m_literal;
64   d_ptr->m_lookups = other.d_ptr->m_lookups;
65   d_ptr->m_localize = other.d_ptr->m_localize;
66 }
67
68 Variable::Variable()
69     : d_ptr( new VariablePrivate( this ) )
70 {
71 }
72
73 Variable::~Variable()
74 {
75   delete d_ptr;
76 }
77
78 Variable &Variable::operator=( const Variable & other )
79 {
80   d_ptr->m_varString = other.d_ptr->m_varString;
81   d_ptr->m_literal = other.d_ptr->m_literal;
82   d_ptr->m_lookups = other.d_ptr->m_lookups;
83   d_ptr->m_localize = other.d_ptr->m_localize;
84   return *this;
85 }
86
87 Variable::Variable( const QString &var )
88     : d_ptr( new VariablePrivate( this ) )
89 {
90   Q_D( Variable );
91   d->m_varString = var;
92
93   QVariant v( var );
94   QString localVar = var;
95   if ( var.startsWith( QLatin1String( "_(" ) ) && var.endsWith( QLatin1Char( ')' ) ) ) {
96     d->m_localize = true;
97     localVar = var.mid( 2, var.size() - 3 );
98     v = localVar;
99   }
100   if ( v.convert( QVariant::Double ) ) {
101     d->m_literal = v;
102     if ( !var.contains( QLatin1Char( '.' ) ) && !var.contains( QLatin1Char( 'e' ) ) ) {
103       if ( var.endsWith( QLatin1Char( '.' ) ) ) {
104 //         throw Grantlee::Exception( VariableSyntaxError, QString( "Variable may not end with a dot: %1" ).arg( v.toString() ) );
105       }
106       d->m_literal = v.toInt();
107     }
108   } else {
109     if (( localVar.startsWith( QLatin1Char( '"' ) ) && localVar.endsWith( QLatin1Char( '"' ) ) )
110         || ( localVar.startsWith( QLatin1Char( '\'' ) ) && localVar.endsWith( QLatin1Char( '\'' ) ) ) ) {
111       const QString unesc = unescapeStringLiteral( localVar );
112       const Grantlee::SafeString ss = markSafe( unesc );
113       d->m_literal = QVariant::fromValue<Grantlee::SafeString>( ss );
114     } else {
115       if ( localVar.contains( QLatin1String( "._" ) ) || ( localVar.startsWith( QLatin1Char( '_' ) ) ) ) {
116         throw Grantlee::Exception( TagSyntaxError,
117             QString::fromLatin1( "Variables and attributes may not begin with underscores: %1" ).arg( localVar ) );
118       }
119       d->m_lookups = localVar.split( QLatin1Char( '.' ) );
120     }
121   }
122 }
123
124 bool Variable::isValid() const
125 {
126   Q_D( const Variable );
127   return !d->m_varString.isEmpty();
128 }
129
130 bool Variable::isConstant() const
131 {
132   Q_D( const Variable );
133   return !d->m_literal.isNull();
134 }
135
136 bool Variable::isTrue( Context *c ) const
137 {
138   return variantIsTrue( resolve( c ) );
139 }
140
141 class StaticQtMetaObject : public QObject
142 {
143 public:
144   static const QMetaObject* _smo() { return &QObject::staticQtMetaObject; }
145 };
146
147 QVariant Variable::resolve( Context *c ) const
148 {
149   Q_D( const Variable );
150   QVariant var;
151   if ( !d->m_lookups.isEmpty() ) {
152     int i = 0;
153     if ( d->m_lookups.at( i ) == QLatin1String( "Qt" ) ) {
154       ++i;
155       const QString nextPart = d->m_lookups.at( i );
156       ++i;
157
158       static const QMetaObject *globalMetaObject = StaticQtMetaObject::_smo();
159
160       bool breakout = false;
161       for ( int j = 0; j < globalMetaObject->enumeratorCount(); ++j ) {
162         const QMetaEnum me = globalMetaObject->enumerator( j );
163
164         if ( QLatin1String( me.name() ) == nextPart ) {
165           const MetaEnumVariable mev( me );
166           var = QVariant::fromValue( mev );
167           break;
168         }
169
170         for ( int k = 0; k < me.keyCount(); ++k ) {
171           if ( QLatin1String( me.key( k ) ) == nextPart ) {
172             const MetaEnumVariable mev( me, k );
173             var = QVariant::fromValue( mev );
174             breakout = true;
175             break;
176           }
177         }
178         if ( breakout )
179           break;
180       }
181       if ( !var.isValid() )
182         return QVariant();
183
184     } else {
185       var = c->lookup( d->m_lookups.at( i++ ) );
186     }
187     while ( i < d->m_lookups.size() ) {
188       var = MetaType::lookup( var, d->m_lookups.at( i++ ) );
189       if ( !var.isValid() )
190         return QVariant();
191     }
192   } else {
193     if ( isSafeString( var ) )
194       var = QVariant::fromValue( getSafeString( d->m_literal ) );
195     else
196       var = d->m_literal;
197   }
198
199   if ( supportedOutputType( var ) ) {
200     if ( d->m_localize ) {
201       return c->localizer()->localize( var );
202     }
203     return var;
204   }
205   // QStringList with size == 1 is special cased to be convertible to QString.
206   // We make sure we don't accidentally invoke that.
207   if ( var.type() != QVariant::StringList && var.canConvert( QVariant::String ) ) {
208     if ( var.convert( QVariant::String ) ) {
209       if ( d->m_localize ) {
210         return QVariant::fromValue<Grantlee::SafeString>( c->localizer()->localize( var ) );
211       }
212       return QVariant::fromValue<Grantlee::SafeString>( var.toString() );
213     }
214     return QVariant();
215   }
216   // Could be a list, hash or enum.
217   return var;
218 }