Stroika Library 3.0d25
 
Loading...
Searching...
No Matches
Binding.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#include "Stroika/Foundation/StroikaPreComp.h"
5
8#include "Stroika/Foundation/Containers/Set.h"
9#include "Stroika/Foundation/DataExchange/XML/WriterUtils.h"
11
12#include "Binding.h"
13
14using namespace Stroika::Foundation;
17using namespace Stroika::Foundation::DataExchange::Variant::XML;
18
19using Containers::Set;
20
21namespace {
22 // shared_ptr's == compares the pointers - but a Binding is a value, so compare what they point to
23 bool SameBinding_ (const shared_ptr<Binding>& lhs, const shared_ptr<Binding>& rhs)
24 {
25 return (lhs == nullptr or rhs == nullptr) ? lhs == rhs : *lhs == *rhs;
26 }
27
28 bool IsLeaf_ (VariantValue::Type t)
29 {
30 return t != VariantValue::eNull and t != VariantValue::eMap and t != VariantValue::eArray;
31 }
32
33 // a member of this kind is written as one of its element's children (a presence marker is an empty one)
34 bool IsElement_ (Binding::Kind k)
35 {
36 return k == Binding::Kind::eElement or k == Binding::Kind::ePresenceMarker;
37 }
38
39 // where a Binding sits, which decides what it may be: an array of repeated elements repeats its member's element inside the
40 // object it is a member of - so as an array's item, or the document element, it has none to repeat
41 enum class Place_ {
42 eDocument,
43 eMember,
44 eItem
45 };
46
47 // why b, at where (a path, for the reason), is not complete - nullopt where it is. prefixes are the namespaces given a prefix
48 // in scope at b's element, as the Writer binds them
49 optional<String> WhyIncompleteAt_ (const Binding& b, const String& where, Place_ place, Mapping<URI, String> prefixes)
50 {
51 if (not b.fType) {
52 return "{}: declares no fType"_f(where);
53 }
54 for (const auto& i : b.fNamespacePrefixes) {
55 if (not DataExchange::XML::IsNCName (i.fValue)) {
56 return "{}: the prefix '{}' is not an XML name"_f(where, i.fValue);
57 }
58 prefixes.RemoveAll ([&] (const Common::KeyValuePair<URI, String>& p) { return p.fValue == i.fValue; });
59 prefixes.Add (i.fKey, i.fValue);
60 }
61 switch (*b.fType) {
62 case VariantValue::eNull:
63 return "{}: declares eNull, which no value but null has"_f(where);
64 case VariantValue::eMap: {
65 if (b.fItems != nullptr) {
66 return "{}: an object (eMap) with fItems"_f(where);
67 }
68 if (b.fUndeclaredMembers == Binding::UndeclaredMembers::eKeep) {
69 return "{}: keeps the members it does not declare (eKeep), untyped"_f(where);
70 }
71 Set<String> keys;
72 Set<String> elementNames;
73 Set<String> attributeNames;
74 bool hasText = false;
75 bool hasElements = false;
76 for (const Binding::Member& m : b.fMembers) {
77 String memberWhere = "{}/{}"_f(where, m.fKey);
78 if (keys.Contains (m.fKey)) {
79 return "{}: listed twice in fMembers"_f(memberWhere);
80 }
81 keys.Add (m.fKey);
82 if (m.fBinding == nullptr) {
83 return "{}: no Binding, so declares no fType"_f(memberWhere);
84 }
85 if (optional<String> why = WhyIncompleteAt_ (*m.fBinding, memberWhere, Place_::eMember, prefixes)) {
86 return why;
87 }
88 NameWithNamespace name = b.NameFor (m.fKey);
89 if (m.fBinding->fKind != Binding::Kind::eElement and not IsLeaf_ (*m.fBinding->fType)) {
90 return "{}: an attribute, text or presence marker that is not a leaf"_f(memberWhere);
91 }
92 if (m.fBinding->fKind == Binding::Kind::ePresenceMarker and m.fBinding->fType != VariantValue::eBoolean) {
93 return "{}: a presence marker (ePresenceMarker) that is not a Boolean"_f(memberWhere);
94 }
95 switch (m.fBinding->fKind) {
96 case Binding::Kind::eAttribute:
97 if (not DataExchange::XML::IsNCName (name.fName) or (name.fName == "xmlns"sv and not name.fNamespace)) {
98 return "{}: '{}' is not an XML attribute name"_f(memberWhere, name.fName);
99 }
100 if (name.fNamespace and not prefixes.ContainsKey (*name.fNamespace)) {
101 return "{}: an attribute in a namespace with no prefix in scope (fNamespacePrefixes)"_f(memberWhere);
102 }
103 if (attributeNames.Contains (name.fName)) {
104 return "{}: another attribute's local name too: '{}'"_f(memberWhere, name.fName);
105 }
106 attributeNames.Add (name.fName);
107 break;
108 case Binding::Kind::eText:
109 if (hasText) {
110 return "{}: a second text member"_f(memberWhere);
111 }
112 hasText = true;
113 break;
114 default:
115 if (not DataExchange::XML::IsNCName (name.fName)) {
116 return "{}: '{}' is not an XML name"_f(memberWhere, name.fName);
117 }
118 if (elementNames.Contains (name.fName)) {
119 return "{}: another member's element's local name too: '{}'"_f(memberWhere, name.fName);
120 }
121 elementNames.Add (name.fName);
122 hasElements = true;
123 break;
124 }
125 }
126 if (hasText and hasElements) {
127 return "{}: a text member beside child elements (mixed content)"_f(where);
128 }
129 } break;
130 case VariantValue::eArray: {
131 if (not b.fMembers.empty ()) {
132 return "{}: an array (eArray) with fMembers"_f(where);
133 }
134 if (b.fItems == nullptr) {
135 return "{}: an array with no fItems"_f(where);
136 }
137 if (b.fItems->fKind != Binding::Kind::eElement) {
138 return "{}[]: an array's item that is not an element"_f(where);
139 }
140 if (b.fItems->fName) {
141 if (not DataExchange::XML::IsNCName (b.fItems->fName->fName)) {
142 return "{}[]: '{}' is not an XML name"_f(where, b.fItems->fName->fName);
143 }
144 }
145 else if (place != Place_::eMember) {
146 return "{}: an array of repeated elements that is not an object's member, so has no element of its own to repeat"_f(where);
147 }
148 if (optional<String> why = WhyIncompleteAt_ (*b.fItems, "{}[]"_f(where), Place_::eItem, prefixes)) {
149 return why;
150 }
151 } break;
152 default:
153 if (not b.fMembers.empty () or b.fItems != nullptr) {
154 return "{}: a leaf ({}) with fMembers or fItems"_f(where, *b.fType);
155 }
156 break;
157 }
158 return nullopt;
159 }
160}
161
162/*
163 ********************************************************************************
164 *********************** Variant::XML::Binding::Member **************************
165 ********************************************************************************
166 */
168 : fKey{key}
169{
170}
171
172Binding::Member::Member (const String& key, const Binding& binding)
173 : fKey{key}
174 , fBinding{Memory::MakeSharedPtr<Binding> (binding)}
175{
176}
177
178bool Binding::Member::operator== (const Member& rhs) const
179{
180 return fKey == rhs.fKey and SameBinding_ (fBinding, rhs.fBinding);
181}
182
184{
185 StringBuilder sb;
186 sb << "{"sv << fKey;
187 if (fBinding != nullptr) {
188 sb << ": "sv << fBinding->ToString_ (false);
189 }
190 sb << "}"sv;
191 return sb;
192}
193
194/*
195 ********************************************************************************
196 ***************************** Variant::XML::Binding ****************************
197 ********************************************************************************
198 */
201
202const Binding* Binding::MemberBinding (const String& key) const
203{
204 for (const Member& i : fMembers) {
205 if (i.fKey == key) {
206 return i.fBinding.get ();
207 }
208 }
209 return nullptr;
210}
211
213{
214 if (const Binding* b = MemberBinding (key); b != nullptr and b->fName) {
215 return *b->fName;
216 }
217 return NameWithNamespace{key};
218}
219
221{
222 const Binding* b = MemberBinding (key);
223 return b == nullptr ? Kind::eElement : b->fKind;
224}
225
226String Binding::KeyForName (Kind kind, const String& name) const
227{
228 for (const Member& i : fMembers) {
229 if ((IsElement_ (kind) ? IsElement_ (KindFor (i.fKey)) : KindFor (i.fKey) == kind) and NameFor (i.fKey).fName == name) {
230 return i.fKey;
231 }
232 }
233 return name;
234}
235
236optional<String> Binding::TextMemberKey () const
237{
238 for (const Member& i : fMembers) {
239 if (i.fBinding != nullptr and i.fBinding->fKind == Kind::eText) {
240 return i.fKey;
241 }
242 }
243 return nullopt;
244}
245
250
252{
253 return not WhyIncomplete_ ().has_value ();
254}
255
256optional<String> Binding::WhyIncomplete_ () const
257{
258 // the document element has a name of its own, and is an element - and with no fName, the value is an object of exactly
259 // one member, which an object with any other number of members is not
260 if (not fName) {
261 return String{"the document element is not named (fName)"sv};
262 }
263 if (not DataExchange::XML::IsNCName (fName->fName)) {
264 return "the document element's name '{}' is not an XML name"_f(fName->fName);
265 }
266 if (fKind != Kind::eElement) {
267 return String{"the document element is not an element (fKind)"sv};
268 }
269 return WhyIncompleteAt_ (*this, fName->fName, Place_::eDocument, {});
270}
271
272bool Binding::operator== (const Binding& rhs) const
273{
274 return fKind == rhs.fKind and fName == rhs.fName and fType == rhs.fType and fMembers == rhs.fMembers and
275 fUndeclaredMembers == rhs.fUndeclaredMembers and SameBinding_ (fItems, rhs.fItems) and
277}
278
280{
281 return ToString_ (true);
282}
283
284String Binding::ToString_ (bool top) const
285{
286 StringBuilder sb;
287 sb << "{"sv;
288 switch (fKind) {
289 case Kind::eAttribute:
290 sb << "attribute"sv;
291 break;
292 case Kind::eText:
293 sb << "text"sv;
294 break;
295 case Kind::ePresenceMarker:
296 sb << "presence marker"sv;
297 break;
298 default:
299 sb << "element"sv;
300 break;
301 }
302 if (fName) {
303 sb << ", Name: "sv << *fName;
304 }
305 if (fType) {
306 sb << ", Type: "sv << *fType;
307 }
308 if (not fMembers.empty ()) {
309 sb << ", Members: "sv << fMembers;
310 }
311 if (fUndeclaredMembers) {
312 switch (*fUndeclaredMembers) {
314 sb << ", UndeclaredMembers: reject"sv;
315 break;
317 sb << ", UndeclaredMembers: skip"sv;
318 break;
320 sb << ", UndeclaredMembers: keep"sv;
321 break;
322 }
323 }
324 if (fItems != nullptr) {
325 sb << ", Items: "sv << fItems->ToString_ (false);
326 }
327 if (fDefaultArrayItems != nullptr) {
328 sb << ", DefaultItems: "sv << fDefaultArrayItems->ToString_ (false);
329 }
330 if (not fNamespacePrefixes.empty ()) {
331 sb << ", NamespacePrefixes: "sv << fNamespacePrefixes;
332 }
333 if (top) {
334 if (optional<String> why = WhyIncomplete_ ()) {
335 sb << ", Incomplete: "sv << *why;
336 }
337 else {
338 sb << ", Complete"sv;
339 }
340 }
341 sb << "}"sv;
342 return sb;
343}
Similar to String, but intended to more efficiently construct a String. Mutable type (String is large...
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
A Mapping uniquely associates two elements: a key and a value (use Assocation to allow multiple value...
nonvirtual bool Add(ArgByValueType< key_type > key, ArgByValueType< mapped_type > newElt, AddReplaceMode addReplaceMode=AddReplaceMode::eAddReplaces)
Definition Mapping.inl:186
nonvirtual bool ContainsKey(ArgByValueType< key_type > key) const
Definition Mapping.inl:175
nonvirtual void RemoveAll()
RemoveAll removes all, or all matching (predicate, iterator range, equals comparer or whatever) items...
Definition Mapping.inl:238
Set<T> is a container of T, where once an item is added, additionally adds () do nothing.
nonvirtual void Add(ArgByValueType< value_type > item)
Definition Set.inl:138
One member of an object: its key, and how that member differs from the default shape (nothing,...
Definition Binding.h:93
Member(const String &key)
the member's own key, and the default shape
Definition Binding.cpp:167
How a VariantValue is to be written as XML, and read back, where the default shape (.
Definition Binding.h:88
bool operator==(const Binding &rhs) const
Equal where every field is - fItems and fDefaultArrayItems by the Bindings they point to,...
Definition Binding.cpp:272
nonvirtual bool IsComplete() const
Whether, given this Binding as the top level Binding (the one a Reader or Writer is given),...
Definition Binding.cpp:251
nonvirtual String KeyForName(Kind kind, const String &name) const
Reading: the key of the member of that kind (an element - or presence marker - or an attribute) writt...
Definition Binding.cpp:226
static const Binding kDefaultArrayItems
The Binding each item of an array is written with where neither fItems nor fDefaultArrayItems gives o...
Definition Binding.h:278
nonvirtual optional< String > TextMemberKey() const
Reading: the key of the member that is this element's text, if one is.
Definition Binding.cpp:236
nonvirtual Kind KindFor(const String &key) const
That member's kind: its Binding's, else an element.
Definition Binding.cpp:220
nonvirtual NameWithNamespace NameFor(const String &key) const
The name that member is written with: its Binding's fName, else its own key.
Definition Binding.cpp:212
nonvirtual UndeclaredMembers GetUndeclaredMembers() const
fUndeclaredMembers, or its default: eReject where fType is declared, eKeep where not
Definition Binding.cpp:246
UndeclaredMembers
What an object does with a member fMembers does not declare - an element or attribute in a document,...
Definition Binding.h:170
Mapping< URI, String > fNamespacePrefixes
A prefix for each of these namespaces, declared on this element (xmlns:p=) and used for every element...
Definition Binding.h:312
static const Binding kRepeatedArrayItems
An item Binding with no name, so no element of the items' own: the member's own element is repeated,...
Definition Binding.h:284
nonvirtual const Binding * MemberBinding(const String &key) const
The Binding for the member of this object with that key - nullptr where it has none,...
Definition Binding.cpp:202
shared_ptr< Binding > fDefaultArrayItems
The Binding each item of an array is written with, where the array's own Binding has no fItems: its f...
Definition Binding.h:271
An element's or attribute's name (local-name, not including a prefix), plus the namespace that name i...
Definition Namespace.h:63
String fName
The LOCAL name - "Why" for <x:Why>
Definition Namespace.h:67
optional< URI > fNamespace
The namespace fName is in, however the document said so - nullopt for a name in no namespace.
Definition Namespace.h:72