Cutelee  6.2.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  static const QMetaObject *_smo() { return &QObject::staticMetaObject; }
169 };
170 
172 {
173  Q_D(const Variable);
174  QVariant var;
175  if (!d->m_lookups.isEmpty()) {
176  auto i = 0;
177  if (d->m_lookups.at(i) == QStringLiteral("Qt")) {
178  ++i;
179  if (d->m_lookups.size() <= i)
180  return QVariant();
181 
182  const auto nextPart = d->m_lookups.at(i);
183  ++i;
184 
185  static auto globalMetaObject = StaticQtMetaObject::_smo();
186 
187  auto breakout = false;
188  for (auto j = 0; j < globalMetaObject->enumeratorCount(); ++j) {
189  const auto me = globalMetaObject->enumerator(j);
190 
191  if (QLatin1String(me.name()) == nextPart) {
192  const MetaEnumVariable mev(me);
193  var = QVariant::fromValue(mev);
194  break;
195  }
196 
197  for (auto k = 0; k < me.keyCount(); ++k) {
198  if (QLatin1String(me.key(k)) == nextPart) {
199  const MetaEnumVariable mev(me, k);
200  var = QVariant::fromValue(mev);
201  breakout = true;
202  break;
203  }
204  }
205  if (breakout)
206  break;
207  }
208  if (!var.isValid())
209  return QVariant();
210 
211  } else {
212  var = c->lookup(d->m_lookups.at(i++));
213  if (var.userType() == QMetaType::QJsonDocument) {
214  const auto jsonDoc = var.toJsonDocument();
215  if (jsonDoc.isArray()) {
216  var = jsonDoc.array().toVariantList();
217  } else if (jsonDoc.isObject()) {
218  var = jsonDoc.object().toVariantHash();
219  } else {
220  // JSON document is eather empty or null
221  return QVariant();
222  }
223  } else if (var.userType() == QMetaType::QJsonValue) {
224  const auto jsonVal = var.toJsonValue();
225  switch(jsonVal.type()) {
226  case QJsonValue::Bool:
227  var = jsonVal.toBool();
228  break;
229  case QJsonValue::Double:
230  var = jsonVal.toDouble();
231  break;
232  case QJsonValue::String:
233  var = jsonVal.toString();
234  break;
235  case QJsonValue::Array:
236  case QJsonValue::Object:
237  var = jsonVal.toVariant();
238  break;
239  default:
240  return QVariant();
241  }
242  } else if (var.userType() == QMetaType::QJsonArray) {
243  var = var.toJsonArray().toVariantList();
244  } else if (var.userType() == QMetaType::QJsonObject) {
245  var = var.toJsonObject().toVariantHash();
246  }
247  }
248  while (i < d->m_lookups.size()) {
249  var = MetaType::lookup(var, d->m_lookups.at(i++));
250  if (!var.isValid())
251  return QVariant();
252  }
253  } else {
254  if (isSafeString(d->m_literal))
255  var = QVariant::fromValue(getSafeString(d->m_literal));
256  else
257  var = d->m_literal;
258  }
259 
260  if (d->m_localize) {
261  return c->localizer()->localize(var);
262  }
263  return var;
264 }
bool variantIsTrue(const QVariant &variant)
Definition: util.cpp:39
QVariantHash toVariantHash() const const
bool isTrue(Context *c) const
Definition: variable.cpp:145
The Context class holds the context to render a Template with.
Definition: context.h:118
QJsonDocument toJsonDocument() const const
Variable & operator=(const Variable &other)
Definition: variable.cpp:66
QStringList lookups() const
Definition: variable.cpp:159
Cutelee::SafeString markSafe(const Cutelee::SafeString &input)
Definition: util.cpp:93
The Cutelee namespace holds all public Cutelee API.
Definition: Mainpage.dox:7
bool isLocalized() const
Definition: variable.cpp:147
QVariant fromValue(T &&value)
qsizetype size() const const
A container for static variables defined in Templates.
Definition: variable.h:52
bool isSafeString(const QVariant &input)
Definition: util.cpp:120
QLocale c()
QString unescapeStringLiteral(const QString &input)
Definition: util.cpp:31
QJsonObject toJsonObject() const const
QVariantList toVariantList() const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
bool isValid() const
Definition: variable.cpp:133
Utility functions used throughout Cutelee.
int toInt(QStringView s, bool *ok) const const
int userType() const const
double toDouble(QStringView s, bool *ok) const const
QString mid(qsizetype position, qsizetype n) const const
QVariant literal() const
Definition: variable.cpp:153
std::shared_ptr< AbstractLocalizer > localizer() const
Definition: context.cpp:230
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
QJsonArray toJsonArray() const const
bool toBool() const const
QJsonValue toJsonValue() const const
bool isValid() const const
double toDouble(bool *ok) const const
QVariant resolve(Context *c) const
Definition: variable.cpp:171
virtual QVariant lookup(const QString &str) const
Definition: context.cpp:100
QString toString() const const
bool isConstant() const
Definition: variable.cpp:139
Cutelee::SafeString getSafeString(const QVariant &input)
Definition: util.cpp:111
An exception for use when implementing template tags.
Definition: exception.h:84