Cutelee  6.1.0
variable.cpp
1 /*
2  This file is part of the Cutelee 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 #include <QJsonArray>
33 #include <QJsonDocument>
34 #include <QJsonObject>
35 #include <QJsonValue>
36 
37 using namespace Cutelee;
38 
39 namespace Cutelee
40 {
41 
43 {
44 public:
45  VariablePrivate(Variable *variable) : q_ptr(variable), m_localize(false) {}
46 
47  Q_DECLARE_PUBLIC(Variable)
48  Variable *const q_ptr;
49 
50  QString m_varString;
51  QVariant m_literal;
52  QStringList m_lookups;
53  bool m_localize;
54 };
55 }
56 
57 Variable::Variable(const Variable &other) : d_ptr(new VariablePrivate(this))
58 {
59  *this = other;
60 }
61 
62 Variable::Variable() : d_ptr(new VariablePrivate(this)) {}
63 
64 Variable::~Variable() { delete d_ptr; }
65 
67 {
68  if (&other == this)
69  return *this;
70  d_ptr->m_varString = other.d_ptr->m_varString;
71  d_ptr->m_literal = other.d_ptr->m_literal;
72  d_ptr->m_lookups = other.d_ptr->m_lookups;
73  d_ptr->m_localize = other.d_ptr->m_localize;
74  return *this;
75 }
76 
77 Variable::Variable(const QString &var) : d_ptr(new VariablePrivate(this))
78 {
79  Q_D(Variable);
80  d->m_varString = var;
81 
82  auto localVar = var;
83  if (var.startsWith(QStringLiteral("_("))) {
84  // The FilterExpression parser ensures this:
85  Q_ASSERT(var.endsWith(QLatin1Char(')')));
86  d->m_localize = true;
87  localVar = var.mid(2, var.size() - 3);
88  }
89  if (localVar.endsWith(QLatin1Char('.'))) {
90  delete d_ptr;
91  throw Cutelee::Exception(
92  TagSyntaxError,
93  QStringLiteral("Variable may not end with a dot: %1").arg(localVar));
94  }
95 
96  auto processedNumber = false;
97  {
98  const auto intResult = QLocale::c().toInt(localVar, &processedNumber);
99  if (processedNumber) {
100  d->m_literal = intResult;
101  } else {
102  const auto doubleResult
103  = QLocale::c().toDouble(localVar, &processedNumber);
104  if (processedNumber) {
105  d->m_literal = doubleResult;
106  }
107  }
108  }
109  if (!processedNumber) {
110  if (localVar.startsWith(QLatin1Char('"'))
111  || localVar.startsWith(QLatin1Char('\''))) {
112  // The FilterExpression parser ensures this:
113  Q_ASSERT(localVar.endsWith(QLatin1Char('\''))
114  || localVar.endsWith(QLatin1Char('"')));
115  const auto unesc = unescapeStringLiteral(localVar);
116  const auto ss = markSafe(unesc);
117  d->m_literal = QVariant::fromValue<Cutelee::SafeString>(ss);
118  } else {
119  if (localVar.contains(QStringLiteral("._"))
120  || (localVar.startsWith(QLatin1Char('_')))) {
121  delete d_ptr;
122  throw Cutelee::Exception(
123  TagSyntaxError,
124  QStringLiteral(
125  "Variables and attributes may not begin with underscores: %1")
126  .arg(localVar));
127  }
128  d->m_lookups = localVar.split(QLatin1Char('.'));
129  }
130  }
131 }
132 
133 bool Variable::isValid() const
134 {
135  Q_D(const Variable);
136  return !d->m_varString.isEmpty();
137 }
138 
140 {
141  Q_D(const Variable);
142  return !d->m_literal.isNull();
143 }
144 
145 bool Variable::isTrue(Context *c) const { return variantIsTrue(resolve(c)); }
146 
148 {
149  Q_D(const Variable);
150  return d->m_localize;
151 }
152 
154 {
155  Q_D(const Variable);
156  return d->m_literal;
157 }
158 
160 {
161  Q_D(const Variable);
162  return d->m_lookups;
163 }
164 
166 {
167 public:
168 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
169  static const QMetaObject *_smo() { return &QObject::staticQtMetaObject; }
170 #else
171  static const QMetaObject *_smo() { return &QObject::staticMetaObject; }
172 #endif
173 };
174 
176 {
177  Q_D(const Variable);
178  QVariant var;
179  if (!d->m_lookups.isEmpty()) {
180  auto i = 0;
181  if (d->m_lookups.at(i) == QStringLiteral("Qt")) {
182  ++i;
183  if (d->m_lookups.size() <= i)
184  return QVariant();
185 
186  const auto nextPart = d->m_lookups.at(i);
187  ++i;
188 
189  static auto globalMetaObject = StaticQtMetaObject::_smo();
190 
191  auto breakout = false;
192  for (auto j = 0; j < globalMetaObject->enumeratorCount(); ++j) {
193  const auto me = globalMetaObject->enumerator(j);
194 
195  if (QLatin1String(me.name()) == nextPart) {
196  const MetaEnumVariable mev(me);
197  var = QVariant::fromValue(mev);
198  break;
199  }
200 
201  for (auto k = 0; k < me.keyCount(); ++k) {
202  if (QLatin1String(me.key(k)) == nextPart) {
203  const MetaEnumVariable mev(me, k);
204  var = QVariant::fromValue(mev);
205  breakout = true;
206  break;
207  }
208  }
209  if (breakout)
210  break;
211  }
212  if (!var.isValid())
213  return QVariant();
214 
215  } else {
216  var = c->lookup(d->m_lookups.at(i++));
217  if (var.userType() == QMetaType::QJsonDocument) {
218  const auto jsonDoc = var.toJsonDocument();
219  if (jsonDoc.isArray()) {
220  var = jsonDoc.array().toVariantList();
221  } else if (jsonDoc.isObject()) {
222  var = jsonDoc.object().toVariantHash();
223  } else {
224  // JSON document is eather empty or null
225  return QVariant();
226  }
227  } else if (var.userType() == QMetaType::QJsonValue) {
228  const auto jsonVal = var.toJsonValue();
229  switch(jsonVal.type()) {
230  case QJsonValue::Bool:
231  var = jsonVal.toBool();
232  break;
233  case QJsonValue::Double:
234  var = jsonVal.toDouble();
235  break;
236  case QJsonValue::String:
237  var = jsonVal.toString();
238  break;
239  case QJsonValue::Array:
240  case QJsonValue::Object:
241  var = jsonVal.toVariant();
242  break;
243  default:
244  return QVariant();
245  }
246  } else if (var.userType() == QMetaType::QJsonArray) {
247  var = var.toJsonArray().toVariantList();
248  } else if (var.userType() == QMetaType::QJsonObject) {
249  var = var.toJsonObject().toVariantHash();
250  }
251  }
252  while (i < d->m_lookups.size()) {
253  var = MetaType::lookup(var, d->m_lookups.at(i++));
254  if (!var.isValid())
255  return QVariant();
256  }
257  } else {
258  if (isSafeString(d->m_literal))
259  var = QVariant::fromValue(getSafeString(d->m_literal));
260  else
261  var = d->m_literal;
262  }
263 
264  if (d->m_localize) {
265  return c->localizer()->localize(var);
266  }
267  return var;
268 }
The Context class holds the context to render a Template with.
Definition: context.h:119
std::shared_ptr< AbstractLocalizer > localizer() const
Definition: context.cpp:230
virtual QVariant lookup(const QString &str) const
Definition: context.cpp:100
An exception for use when implementing template tags.
Definition: exception.h:85
A container for static variables defined in Templates.
Definition: variable.h:53
QStringList lookups() const
Definition: variable.cpp:159
QVariant literal() const
Definition: variable.cpp:153
QVariant resolve(Context *c) const
Definition: variable.cpp:175
bool isTrue(Context *c) const
Definition: variable.cpp:145
bool isConstant() const
Definition: variable.cpp:139
Variable & operator=(const Variable &other)
Definition: variable.cpp:66
bool isLocalized() const
Definition: variable.cpp:147
bool isValid() const
Definition: variable.cpp:133
The Cutelee namespace holds all public Cutelee API.
Definition: Mainpage.dox:8
QString unescapeStringLiteral(const QString &input)
Definition: util.cpp:31
bool isSafeString(const QVariant &input)
Definition: util.cpp:117
Cutelee::SafeString getSafeString(const QVariant &input)
Definition: util.cpp:108
Cutelee::SafeString markSafe(const Cutelee::SafeString &input)
Definition: util.cpp:90
bool variantIsTrue(const QVariant &variant)
Definition: util.cpp:39
QVariantList toVariantList() const const
QVariantHash toVariantHash() const const
QLocale c()
double toDouble(const QString &s, bool *ok) const const
int toInt(const QString &s, bool *ok) const const
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const const
QString mid(int position, int n) const const
int size() const const
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const const
QVariant fromValue(const T &value)
bool isValid() const const
bool toBool() const const
double toDouble(bool *ok) const const
QJsonArray toJsonArray() const const
QJsonDocument toJsonDocument() const const
QJsonObject toJsonObject() const const
QJsonValue toJsonValue() const const
QString toString() const const
int userType() const const
Utility functions used throughout Cutelee.