Use iterators instead of Q_FOREACH to avoid container copies.
[grantlee:johus-grantlee.git] / templates / lib / typeaccessors.cpp
1 /*
2   This file is part of the Grantlee template system.
3
4   Copyright (c) 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 "typeaccessor.h"
22
23 #include "metaenumvariable_p.h"
24 #include "safestring.h"
25
26 #include <QtCore/QRegExp>
27 #include <QtCore/QStringList>
28 #include <QtCore/QVariant>
29
30 namespace Grantlee
31 {
32
33 static QRegExp getIsTitleRegexp() {
34   QRegExp titleRe( QLatin1String( "\\b[a-z]" ) );
35   titleRe.setMinimal( true );
36   return titleRe;
37 }
38
39 static QRegExp getTitleRegexp() {
40   QRegExp titleRe( QLatin1String( "\\b(.)" ) );
41   titleRe.setMinimal( true );
42   return titleRe;
43 }
44
45 template <>
46 QVariant TypeAccessor<Grantlee::SafeString&>::lookUp( const Grantlee::SafeString &object, const QString &property )
47 {
48   if ( property == QLatin1String( "capitalize" ) ) {
49     const QString s = object.get();
50     return s.at( 0 ).toUpper() + s.right( s.length() - 1 );
51   }
52
53   static const QLatin1String falseString( "False" );
54   static const QLatin1String trueString( "True" );
55
56   if ( property == QLatin1String( "isalnum" ) ) {
57     const QString s = object.get();
58     QString::const_iterator it = s.constBegin();
59     while ( it != s.constEnd() ) {
60       if ( !it->isLetterOrNumber() )
61         return falseString;
62       ++it;
63     }
64     return trueString;
65   }
66   if ( property == QLatin1String( "isalpha" ) ) {
67     const QString s = object.get();
68     QString::const_iterator it = s.constBegin();
69     if ( it == s.constEnd() )
70       return falseString;
71     while ( it != s.constEnd() ) {
72       if ( !it->isLetter() )
73         return falseString;
74       ++it;
75     }
76     return trueString;
77   }
78   if ( property == QLatin1String( "isdigit" ) ) {
79     const QString s = object.get();
80     QString::const_iterator it = s.constBegin();
81     while ( it != s.constEnd() ) {
82       if ( !it->isNumber() )
83         return falseString;
84       ++it;
85     }
86     return trueString;
87   }
88   if ( property == QLatin1String( "islower" ) ) {
89     const QString s = object.get().toLower();
90     return ( s == object.get() ) ? trueString : falseString;
91   }
92   if ( property == QLatin1String( "isspace" ) ) {
93     const QString s = object.get().trimmed();
94     return ( s.isEmpty() ) ? trueString : falseString;
95   }
96   if ( property == QLatin1String( "istitle" ) ) {
97     const QString s = object.get();
98
99     static const QRegExp titleRe = getIsTitleRegexp();
100     return ( titleRe.indexIn( s ) < 0 ) ? trueString : falseString;
101   }
102   if ( property == QLatin1String( "isupper" ) ) {
103     const QString s = object.get().toUpper();
104     return ( s == object ) ? trueString : falseString;
105   }
106   if ( property == QLatin1String( "lower" ) ) {
107     return object.get().toLower();
108   }
109   if ( property == QLatin1String( "splitlines" ) ) {
110     const QStringList strings = object.get().split( QLatin1Char( '\n' ) );
111     QVariantList list;
112     QStringList::const_iterator it = strings.constBegin();
113     const QStringList::const_iterator end = strings.constEnd();
114     for ( ; it != end; ++it )
115       list << *it;
116     return list;
117   }
118   if ( property == QLatin1String( "strip" ) ) {
119     return object.get().trimmed();
120   }
121   if ( property == QLatin1String( "swapcase" ) ) {
122     const QString inputString = object.get();
123     QString s;
124     s.reserve( inputString.size() );
125     QString::const_iterator it = inputString.constBegin();
126     while ( it != inputString.constEnd() ) {
127       if ( it->isUpper() )
128         s += it->toLower();
129       else if ( it->isLower() )
130         s += it->toUpper();
131       else
132         s += *it;
133       ++it;
134     }
135     return s;
136   }
137   if ( property == QLatin1String( "title" ) ) {
138     static const QRegExp titleRe = getTitleRegexp();
139
140     const QString s = object.get();
141     QString output;
142     output.reserve( s.size() );
143     int pos = 0;
144     int nextPos = 0;
145     int matchedLength;
146
147     while ( ( pos = titleRe.indexIn( s, pos ) ) != -1 ) {
148       output += titleRe.cap( 1 ).toUpper();
149       matchedLength = titleRe.matchedLength();
150       if ( ( nextPos = titleRe.indexIn( s, pos + matchedLength ) ) != -1 )
151         output += s.mid( pos + matchedLength, nextPos - pos - 1 );
152       else
153         output += s.right( s.length() - ( pos + matchedLength ) );
154       pos += matchedLength;
155     }
156     return output;
157   }
158   if ( property == QLatin1String( "upper" ) ) {
159     return object.get().toUpper();
160   }
161   return QVariant();
162 }
163
164 QVariant doQobjectLookUp( const QObject * const object, const QString &property )
165 {
166   if ( property == QLatin1String( "children" ) ) {
167     const QObjectList childList = object->children();
168     if ( childList.isEmpty() )
169       return QVariant();
170     QVariantList children;
171
172     QObjectList::const_iterator it = childList.constBegin();
173     const QObjectList::const_iterator end = childList.constEnd();
174     for ( ; it != end; ++it )
175       children.append( QVariant::fromValue( *it ) );
176     return children;
177   }
178
179   if ( property == QLatin1String( "objectName" ) ) {
180     return object->objectName();
181   }
182   // Can't be const because of invokeMethod.
183   const QMetaObject *metaObj = object->metaObject();
184
185   QMetaProperty mp;
186   for ( int i = 0; i < metaObj->propertyCount(); ++i ) {
187     // TODO only read-only properties should be allowed here.
188     // This might also handle the variant messing I hit before.
189     mp = metaObj->property( i );
190
191     if ( QString::fromUtf8( mp.name() ) != property )
192       continue;
193
194     if ( mp.isEnumType() ) {
195       MetaEnumVariable mev( mp.enumerator(), mp.read( object ).toInt() );
196       return QVariant::fromValue( mev );
197     }
198
199     return mp.read( object );
200   }
201   QMetaEnum me;
202   for ( int i = 0; i < metaObj->enumeratorCount(); ++i ) {
203     me = metaObj->enumerator( i );
204
205     if ( QLatin1String( me.name() ) == property ) {
206       MetaEnumVariable mev( me );
207       return QVariant::fromValue( mev );
208     }
209
210     const int value = me.keyToValue( property.toLatin1().constData() );
211
212     if ( value < 0 )
213       continue;
214
215     const MetaEnumVariable mev( me, value );
216
217     return QVariant::fromValue( mev );
218   }
219   return object->property( property.toUtf8().constData() );
220 }
221
222 template <>
223 QVariant TypeAccessor<QObject*>::lookUp( const QObject * const object, const QString &property )
224 {
225   return doQobjectLookUp( object, property );
226 }
227
228 template <>
229 QVariant TypeAccessor<MetaEnumVariable&>::lookUp( const MetaEnumVariable &object, const QString &property )
230 {
231   if ( property == QLatin1String( "name" ) )
232     return QLatin1String( object.enumerator.name() );
233   if ( property == QLatin1String( "value" ) )
234     return object.value;
235   if ( property == QLatin1String( "key" ) )
236     return QLatin1String( object.enumerator.valueToKey( object.value ) );
237   if ( property == QLatin1String( "scope" ) )
238     return QLatin1String( object.enumerator.scope() );
239   if ( property == QLatin1String( "keyCount" ) )
240     return object.enumerator.keyCount();
241
242   bool ok = false;
243   const int listIndex = property.toInt( &ok );
244   if ( ok ) {
245     if ( listIndex >= object.enumerator.keyCount() )
246       return QVariant();
247
248     const MetaEnumVariable mev( object.enumerator, object.enumerator.value( listIndex ) );
249     return QVariant::fromValue( mev );
250   }
251
252   return QVariant();
253 }
254
255 }