Cutelee  6.1.0
regroup.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 "regroup.h"
22 
23 #include "../lib/exception.h"
24 #include "parser.h"
25 #include "util.h"
26 
27 RegroupNodeFactory::RegroupNodeFactory() {}
28 
29 Node *RegroupNodeFactory::getNode(const QString &tagContent, Parser *p) const
30 {
31  auto expr = tagContent.split(QLatin1Char(' '));
32 
33  if (expr.size() != 6) {
34  throw Cutelee::Exception(
35  TagSyntaxError, QStringLiteral("widthratio takes five arguments"));
36  }
37  FilterExpression target(expr.at(1), p);
38  if (expr.at(2) != QStringLiteral("by")) {
39  throw Cutelee::Exception(TagSyntaxError,
40  QStringLiteral("second argument must be 'by'"));
41  }
42 
43  if (expr.at(4) != QStringLiteral("as")) {
44  throw Cutelee::Exception(TagSyntaxError,
45  QStringLiteral("fourth argument must be 'as'"));
46  }
47 
48  FilterExpression expression(
49  QStringLiteral("\"") + expr.at(3) + QStringLiteral("\""), p);
50 
51  auto name = expr.at(5);
52 
53  return new RegroupNode(target, expression, name, p);
54 }
55 
56 RegroupNode::RegroupNode(const FilterExpression &target,
57  const FilterExpression &expression,
58  const QString &varName, QObject *parent)
59  : Node(parent), m_target(target), m_expression(expression),
60  m_varName(varName)
61 {
62 }
63 
64 void RegroupNode::render(OutputStream *stream, Context *c) const
65 {
66  Q_UNUSED(stream)
67  auto objList = m_target.toList(c);
68  if (objList.isEmpty()) {
69  c->insert(m_varName, QVariantHash());
70  return;
71  }
72 
73  // What's going on?
74  //
75  // objList is a flat list of objects with a common parameter. For example,
76  // Person objects with
77  // a name parameter. The list is already sorted.
78  // Say the objList contains ["David Beckham", "David Blain", "Keira
79  // Nightly"]
80  // etc.
81  // We want to regroup the list into separate lists of people with the same
82  // first name.
83  // ie objHash should be: {"David": ["David Beckham", "David Blain"],
84  // "Keira":
85  // ["Keira Nightly"]}
86  //
87  // We then insert the objHash into the Context ready for rendering later in
88  // a
89  // for loop.
90 
91  QVariantList contextList;
92  const QString keyName = getSafeString(m_expression.resolve(c));
93  for (auto &var : objList) {
94  c->push();
95  c->insert(QStringLiteral("var"), var);
96  const QString key = getSafeString(
97  FilterExpression(QStringLiteral("var.") + keyName, 0).resolve(c));
98  c->pop();
99  QVariantHash hash;
100  if (!contextList.isEmpty()) {
101  auto hashVar = contextList.last();
102  hash = hashVar.value<QVariantHash>();
103  }
104 
105  const auto it = hash.constFind(QStringLiteral("grouper"));
106  if (it == hash.constEnd() || it.value() != key) {
107  contextList.append(QVariantHash{
108  {QStringLiteral("grouper"), key},
109  {QStringLiteral("list"), QVariantList{ var }},
110  });
111  } else {
112  QVariantList list;
113  auto itList = hash.find(QStringLiteral("list"));
114  if (itList != hash.end()) {
115  list = itList.value().value<QVariantList>();
116  list.append(var);
117  *itList = list;
118  } else {
119  list.append(var);
120  hash.insert(QStringLiteral("list"), list);
121  }
122  contextList[contextList.size() - 1] = hash;
123  }
124  }
125  c->insert(m_varName, contextList);
126 }
The Context class holds the context to render a Template with.
Definition: context.h:119
void insert(const QString &name, QObject *object)
Definition: context.cpp:145
An exception for use when implementing template tags.
Definition: exception.h:85
A FilterExpression object represents a filter expression in a template.
QVariantList toList(Context *c) const
QVariant resolve(OutputStream *stream, Context *c) const
Base class for all nodes.
Definition: node.h:78
The OutputStream class is used to render templates to a QTextStream.
Definition: outputstream.h:81
The Parser class processes a string template into a tree of nodes.
Definition: parser.h:49
Node * getNode(const QString &tagContent, Parser *p) const override
Definition: regroup.cpp:29
void render(OutputStream *stream, Context *c) const override
Definition: regroup.cpp:64
Cutelee::SafeString getSafeString(const QVariant &input)
Definition: util.cpp:108
Utility functions used throughout Cutelee.