File: el-processing.hpp

package info (click to toggle)
libzeep 5.1.8-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 3,596 kB
  • sloc: cpp: 27,393; xml: 7,798; javascript: 180; sh: 37; makefile: 8
file content (294 lines) | stat: -rw-r--r-- 9,788 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
// Copyright Maarten L. Hekkelman, Radboud University 2008-2013.
//        Copyright Maarten L. Hekkelman, 2014-2022
//   Distributed under the Boost Software License, Version 1.0.
//      (See accompanying file LICENSE_1_0.txt or copy at
//            http://www.boost.org/LICENSE_1_0.txt)
//
// expression language support
//

#pragma once

/// \file
/// definition of the routines that can parse and interpret el (expression language) code in a web application context

#include <zeep/config.hpp>

#include <map>

#include <zeep/exception.hpp>
#include <zeep/json/element.hpp>
#include <zeep/http/request.hpp>
#include <zeep/xml/node.hpp>

namespace zeep::http
{

using object = ::zeep::json::element;

class scope;
class basic_server;

/// \brief Process the text in \a text and return `true` if the result is
///        not empty, zero or false.
///
///	The expression in \a text is processed and if the result of this
/// expression is empty, false or zero then `false` is returned.
/// \param scope  The scope for this el script
/// \param text   The el script
/// \return       The result of the script
bool process_el(const scope& scope, std::string& text);

/// \brief Process the text in \a text and return the result if the expression is valid,
///        the value of \a text otherwise.
///
///	If the expression in \a text is valid, it is processed and the result
/// is returned, otherwise simply returns the text.
/// \param scope  The scope for this el script
/// \param text   The el script
/// \return       The result of the script
std::string process_el_2(const scope& scope, const std::string& text);

/// \brief Process the text in \a text. The result is put in \a result
///
///	The expression in \a text is processed and the result is returned
/// in \a result.
/// \param scope  The scope for this el script
/// \param text   The el script
/// \result		  The result of the script
object evaluate_el(const scope& scope, const std::string& text);

/// \brief Process the text in \a text and return a list of name/value pairs
///
///	The expressions found in \a text are processed and the result is
/// 				returned as a list of name/value pairs to be used in e.g.
///                 processing a m2:attr attribute.
/// \param scope  The scope for the el scripts
/// \param text   The text optionally containing el scripts.
/// \return       list of name/value pairs
std::vector<std::pair<std::string,std::string>> evaluate_el_attr(const scope& scope, const std::string& text);

/// \brief Process the text in \a text. This should be a comma separated list
/// of expressions that each should evaluate to true.
///
///	The expression in \a text is processed and the result is false if
/// one of the expressions in the comma separated list evaluates to false.
///
/// in \a result.
/// \param scope  The scope for this el script
/// \param text   The el script
/// \return       True in case all the expressions evaluate to true
bool evaluate_el_assert(const scope& scope, const std::string& text);

/// \brief Process the text in \a text and put the resulting z:with expressions in the scope
///
///	The expressions found in \a text are processed and the result is
///	returned as a list of name/value pairs to be used in e.g.
/// processing a m2:attr attribute.
/// \param scope  The scope for the el scripts
/// \param text   The text containing el scripts in the form var=val(,var=val)*.
void evaluate_el_with(scope& scope, const std::string& text);

/// \brief Evaluate the text in \a text as a potential link template
///
///	The expression found in \a text is processed and the result is
///	returned as a link template object. This function is called from
/// el::include/el::replace/el::insert attributes.
///
/// \param scope  The scope for the el scripts
/// \param text   The text containing the link specification
/// \result		  The resulting link
object evaluate_el_link(const scope& scope, const std::string& text);

// --------------------------------------------------------------------

class expression_utility_object_base
{
  public:

	virtual ~expression_utility_object_base() = default;

	static object evaluate(const scope& scope,
		const std::string& className, const std::string& methodName,
		const std::vector<object>& parameters)
	{
		for (auto inst = s_head; inst != nullptr; inst = inst->m_next)
		{
			if (className == inst->m_name)
				return inst->m_obj->evaluate(scope, methodName, parameters);
		}

		return {};
	}

  protected:

	virtual object evaluate(const scope& scope, const std::string& methodName,
		const std::vector<object>& parameters) const = 0;

	struct instance
	{
		expression_utility_object_base*	m_obj = nullptr;
		const char*						m_name;
		instance*						m_next = nullptr;
	};

	static instance* s_head;
};

template<typename OBJ>
class expression_utility_object : public expression_utility_object_base
{
  public:
	using implementation_type = OBJ;

  protected:

	expression_utility_object()
	{
		static instance s_next{ this, implementation_type::name(), s_head };
		s_head = &s_next;
	}
};

// --------------------------------------------------------------------

/// \brief The class that stores variables for the current scope
///
/// When processing tags and in expression language constructs we use
/// variables. These are stored in scope instances.

class scope
{
  public:

	/// \brief simple constructor, used where there's no request available
	scope();

	/// \brief constructor to be used only in debugging 
	///
	/// \param req		The incomming HTTP request
	scope(const request& req);

	/// \brief constructor used in a HTTP request context
	///
	/// \param server	The server that handles the incomming request
	/// \param req		The incomming HTTP request
	scope(const basic_server& server, const request& req);

	/// \brief chaining constructor
	///
	/// Scopes can be nested, introducing new namespaces
	/// \param next	The next scope up the chain.
	explicit scope(const scope& next);

	/// \brief put variable in the scope with \a name and \a value
	template <typename T>
	void put(const std::string& name, const T& value);

	/// \brief put variable of type array in the scope with \a name and values from \a begin to \a end
	template <typename ForwardIterator>
	void put(const std::string& name, ForwardIterator begin, ForwardIterator end);

	/// \brief return variable with \a name 
	///
	/// \param name				The name of the variable to return
	/// \param includeSelected	If this is true, and the variable was not found as a regular variable
	///							in the current scope, the selected objects will be search for members
	///							with \a name This is used by the tag processing lib v2 in _z2:object_
	/// \return					The value found or null if there was no such variable.
	const object& lookup(const std::string& name, bool includeSelected = false) const;

	/// \brief return variable with \a name 
	const object& operator[](const std::string& name) const;

	/// \brief return variable with \a name 
	///
	/// \param name				The name of the variable to return
	/// \param includeSelected	If this is true, and the variable was not found as a regular variable
	///							in the current scope, the selected objects will be search for members
	///							with \a name This is used by the tag processing lib v2 in _z2:object_
	/// \return					The value found or null if there was no such variable.
	object& lookup(const std::string& name);

	/// \brief return variable with \a name 
	object& operator[](const std::string& name);

	/// \brief return the HTTP request, will throw if the scope chain was not created with a request
	const request& get_request() const;

	/// \brief return the context_name of the server
	std::string get_context_name() const;

	/// \brief return the credentials of the current user
	json::element get_credentials() const;

    /// \brief select object \a o , used in z2:object constructs
    void select_object(const object& o);

	/// \brief a nodeset for a selector, cached to avoid recusive expansion
	///
	/// In tag processors it is sometimes needed to take a selection of zeep::xml::nodes
	/// and reuse these, as a copy when inserting templates e.g.
	using node_set_type = std::vector<std::unique_ptr<xml::node>>;

	/// \brief return the node_set_type with name \a name
	node_set_type get_nodeset(const std::string& name) const;

	/// \brief store node_set_type \a nodes with name \a name
	void set_nodeset(const std::string& name, node_set_type&& nodes);

	/// \brief return whether a node_set with name \a name is stored
	bool has_nodeset(const std::string& name) const
	{
		return m_nodesets.count(name) or (m_next != nullptr and m_next->has_nodeset(name));
	}

	/// \brief get the CSRF token from the request burried in \a scope
	std::string get_csrf_token() const;

  private:
	/// for debugging purposes
	friend std::ostream& operator<<(std::ostream& lhs, const scope& rhs);

	scope& operator=(const scope& );

	using data_map = std::map<std::string, object>;

	data_map m_data;
	scope *m_next;
	unsigned m_depth;
	const request *m_req;
	const basic_server* m_server;
    object m_selected;

	using nodeset_map = std::map<std::string,node_set_type>;

	nodeset_map m_nodesets;
};

template <typename T>
inline void scope::put(const std::string& name, const T& value)
{
	m_data[name] = value;
}

template <>
inline void scope::put(const std::string& name, const object& value)
{
	m_data[name] = value;
}

template <typename ForwardIterator>
inline void scope::put(const std::string& name, ForwardIterator begin, ForwardIterator end)
{
	std::vector<object> elements;
	while (begin != end)
		elements.push_back(object(*begin++));
	m_data[name] = elements;
}

// --------------------------------------------------------------------

} // namespace zeep::http