1 /*
2 * Copyright (C) 2018-2021 Alexander Borisov
3 *
4 * Author: Alexander Borisov <borisov@lexbor.com>
5 */
6
7 #include "lexbor/dom/interfaces/attr.h"
8 #include "lexbor/dom/interfaces/attr_res.h"
9 #include "lexbor/dom/interfaces/document.h"
10 #include "lexbor/dom/interfaces/element.h"
11
12
13 LXB_API lxb_dom_attr_data_t *
14 lxb_dom_attr_local_name_append(lexbor_hash_t *hash,
15 const lxb_char_t *name, size_t length);
16
17 LXB_API lxb_dom_attr_data_t *
18 lxb_dom_attr_qualified_name_append(lexbor_hash_t *hash, const lxb_char_t *name,
19 size_t length);
20
21 const lxb_ns_data_t *
22 lxb_ns_append(lexbor_hash_t *hash, const lxb_char_t *link, size_t length);
23
24
25 lxb_dom_attr_t *
lxb_dom_attr_interface_create(lxb_dom_document_t * document)26 lxb_dom_attr_interface_create(lxb_dom_document_t *document)
27 {
28 lxb_dom_attr_t *attr;
29
30 attr = lexbor_mraw_calloc(document->mraw, sizeof(lxb_dom_attr_t));
31 if (attr == NULL) {
32 return NULL;
33 }
34
35 lxb_dom_node_t *node = lxb_dom_interface_node(attr);
36
37 node->owner_document = lxb_dom_document_owner(document);
38 node->type = LXB_DOM_NODE_TYPE_ATTRIBUTE;
39
40 return attr;
41 }
42
43 lxb_dom_attr_t *
lxb_dom_attr_interface_clone(lxb_dom_document_t * document,const lxb_dom_attr_t * attr)44 lxb_dom_attr_interface_clone(lxb_dom_document_t *document,
45 const lxb_dom_attr_t *attr)
46 {
47 lxb_dom_attr_t *new;
48 const lxb_dom_attr_data_t *data;
49
50 new = lxb_dom_attr_interface_create(document);
51 if (new == NULL) {
52 return NULL;
53 }
54
55 new->node.ns = attr->node.ns;
56
57 if (document == attr->node.owner_document) {
58 new->qualified_name = attr->qualified_name;
59 }
60 else {
61 data = lxb_dom_attr_data_by_id(attr->node.owner_document->attrs,
62 attr->qualified_name);
63 if (data == NULL) {
64 goto failed;
65 }
66
67 if (data->attr_id < LXB_DOM_ATTR__LAST_ENTRY) {
68 new->qualified_name = attr->qualified_name;
69 }
70 else {
71 data = lxb_dom_attr_qualified_name_append(document->attrs,
72 lexbor_hash_entry_str(&data->entry),
73 data->entry.length);
74 if (data == NULL) {
75 goto failed;
76 }
77
78 new->qualified_name = (lxb_dom_attr_id_t) data;
79 }
80 }
81
82 if (lxb_dom_node_interface_copy(&new->node, &attr->node, true)
83 != LXB_STATUS_OK)
84 {
85 goto failed;
86 }
87
88 if (attr->value == NULL) {
89 return new;
90 }
91
92 new->value = lexbor_mraw_calloc(document->mraw, sizeof(lexbor_str_t));
93 if (new->value == NULL) {
94 goto failed;
95 }
96
97 if (lexbor_str_copy(new->value, attr->value, document->text) == NULL) {
98 goto failed;
99 }
100
101 return new;
102
103 failed:
104
105 return lxb_dom_attr_interface_destroy(new);
106 }
107
108 lxb_dom_attr_t *
lxb_dom_attr_interface_destroy(lxb_dom_attr_t * attr)109 lxb_dom_attr_interface_destroy(lxb_dom_attr_t *attr)
110 {
111 lexbor_str_t *value;
112 lxb_dom_document_t *doc = lxb_dom_interface_node(attr)->owner_document;
113
114 value = attr->value;
115
116 (void) lxb_dom_node_interface_destroy(lxb_dom_interface_node(attr));
117
118 if (value != NULL) {
119 if (value->data != NULL) {
120 lexbor_mraw_free(doc->text, value->data);
121 }
122
123 lexbor_mraw_free(doc->mraw, value);
124 }
125
126 return NULL;
127 }
128
129 lxb_status_t
lxb_dom_attr_set_name(lxb_dom_attr_t * attr,const lxb_char_t * name,size_t length,bool to_lowercase)130 lxb_dom_attr_set_name(lxb_dom_attr_t *attr, const lxb_char_t *name,
131 size_t length, bool to_lowercase)
132 {
133 lxb_dom_attr_data_t *data;
134 lxb_dom_document_t *doc = lxb_dom_interface_node(attr)->owner_document;
135
136 data = lxb_dom_attr_local_name_append(doc->attrs, name, length);
137 if (data == NULL) {
138 return LXB_STATUS_ERROR_MEMORY_ALLOCATION;
139 }
140
141 attr->node.local_name = data->attr_id;
142
143 if (to_lowercase == false) {
144 data = lxb_dom_attr_qualified_name_append(doc->attrs, name, length);
145 if (data == NULL) {
146 return LXB_STATUS_ERROR_MEMORY_ALLOCATION;
147 }
148
149 attr->qualified_name = (lxb_dom_attr_id_t) data;
150 }
151
152 return LXB_STATUS_OK;
153 }
154
155 lxb_status_t
lxb_dom_attr_set_name_ns(lxb_dom_attr_t * attr,const lxb_char_t * link,size_t link_length,const lxb_char_t * name,size_t name_length,bool to_lowercase)156 lxb_dom_attr_set_name_ns(lxb_dom_attr_t *attr, const lxb_char_t *link,
157 size_t link_length, const lxb_char_t *name,
158 size_t name_length, bool to_lowercase)
159 {
160 size_t length;
161 lxb_char_t *p;
162 const lxb_ns_data_t *ns_data;
163 lxb_dom_attr_data_t *data;
164 lxb_dom_document_t *doc = lxb_dom_interface_node(attr)->owner_document;
165
166 ns_data = lxb_ns_append(doc->ns, link, link_length);
167 if (ns_data == NULL || ns_data->ns_id == LXB_NS__UNDEF) {
168 return LXB_STATUS_ERROR;
169 }
170
171 attr->node.ns = ns_data->ns_id;
172
173 /* TODO: append check https://www.w3.org/TR/xml/#NT-Name */
174
175 p = (lxb_char_t *) memchr(name, ':', name_length);
176 if (p == NULL) {
177 return lxb_dom_attr_set_name(attr, name, name_length, to_lowercase);
178 }
179
180 length = p - name;
181
182 /* local name */
183 data = lxb_dom_attr_local_name_append(doc->attrs, &name[(length + 1)],
184 (name_length - (length + 1)));
185 if (data == NULL) {
186 return LXB_STATUS_ERROR_MEMORY_ALLOCATION;
187 }
188
189 attr->node.local_name = (lxb_dom_attr_id_t) data;
190
191 /* qualified name */
192 data = lxb_dom_attr_qualified_name_append(doc->attrs, name, name_length);
193 if (data == NULL) {
194 return LXB_STATUS_ERROR;
195 }
196
197 attr->qualified_name = (lxb_dom_attr_id_t) data;
198
199 /* prefix */
200 attr->node.prefix = (lxb_ns_prefix_id_t) lxb_ns_prefix_append(doc->ns, name,
201 length);
202 if (attr->node.prefix == 0) {
203 return LXB_STATUS_ERROR;
204 }
205
206 return LXB_STATUS_OK;
207 }
208
209 lxb_status_t
lxb_dom_attr_set_value(lxb_dom_attr_t * attr,const lxb_char_t * value,size_t value_len)210 lxb_dom_attr_set_value(lxb_dom_attr_t *attr,
211 const lxb_char_t *value, size_t value_len)
212 {
213 lxb_status_t status;
214 lxb_dom_document_t *doc = lxb_dom_interface_node(attr)->owner_document;
215
216 if (doc->ev_set_value != NULL) {
217 status = doc->ev_set_value(lxb_dom_interface_node(attr),
218 value, value_len);
219 if (status != LXB_STATUS_OK) {
220 return status;
221 }
222 }
223
224 if (attr->value == NULL) {
225 attr->value = lexbor_mraw_calloc(doc->mraw, sizeof(lexbor_str_t));
226 if (attr->value == NULL) {
227 return LXB_STATUS_ERROR_MEMORY_ALLOCATION;
228 }
229 }
230
231 if (attr->value->data == NULL) {
232 lexbor_str_init(attr->value, doc->text, value_len);
233 if (attr->value->data == NULL) {
234 return LXB_STATUS_ERROR_MEMORY_ALLOCATION;
235 }
236 }
237 else {
238 attr->value->length = 0;
239
240 if (lexbor_str_size(attr->value) <= value_len) {
241 const lxb_char_t *tmp;
242
243 tmp = lexbor_str_realloc(attr->value, doc->text, (value_len + 1));
244 if (tmp == NULL) {
245 return LXB_STATUS_ERROR_MEMORY_ALLOCATION;
246 }
247 }
248 }
249
250 memcpy(attr->value->data, value, sizeof(lxb_char_t) * value_len);
251
252 attr->value->data[value_len] = 0x00;
253 attr->value->length = value_len;
254
255 return LXB_STATUS_OK;
256 }
257
258 lxb_status_t
lxb_dom_attr_set_value_wo_copy(lxb_dom_attr_t * attr,lxb_char_t * value,size_t value_len)259 lxb_dom_attr_set_value_wo_copy(lxb_dom_attr_t *attr,
260 lxb_char_t *value, size_t value_len)
261 {
262 if (attr->value == NULL) {
263 lxb_dom_document_t *doc = lxb_dom_interface_node(attr)->owner_document;
264
265 attr->value = lexbor_mraw_alloc(doc->mraw, sizeof(lexbor_str_t));
266 if (attr->value == NULL) {
267 return LXB_STATUS_ERROR_MEMORY_ALLOCATION;
268 }
269 }
270
271 attr->value->data = value;
272 attr->value->length = value_len;
273
274 return LXB_STATUS_OK;
275 }
276
277 lxb_status_t
lxb_dom_attr_set_existing_value(lxb_dom_attr_t * attr,const lxb_char_t * value,size_t value_len)278 lxb_dom_attr_set_existing_value(lxb_dom_attr_t *attr,
279 const lxb_char_t *value, size_t value_len)
280 {
281 return lxb_dom_attr_set_value(attr, value, value_len);
282 }
283
284 lxb_status_t
lxb_dom_attr_clone_name_value(lxb_dom_attr_t * attr_from,lxb_dom_attr_t * attr_to)285 lxb_dom_attr_clone_name_value(lxb_dom_attr_t *attr_from,
286 lxb_dom_attr_t *attr_to)
287 {
288 attr_to->node.local_name = attr_from->node.local_name;
289 attr_to->qualified_name = attr_from->qualified_name;
290
291 return LXB_STATUS_OK;
292 }
293
294 bool
lxb_dom_attr_compare(lxb_dom_attr_t * first,lxb_dom_attr_t * second)295 lxb_dom_attr_compare(lxb_dom_attr_t *first, lxb_dom_attr_t *second)
296 {
297 if (first->node.local_name == second->node.local_name
298 && first->node.ns == second->node.ns
299 && first->qualified_name == second->qualified_name)
300 {
301 if (first->value == NULL) {
302 if (second->value == NULL) {
303 return true;
304 }
305
306 return false;
307 }
308
309 if (second->value != NULL
310 && first->value->length == second->value->length
311 && lexbor_str_data_ncmp(first->value->data, second->value->data,
312 first->value->length))
313 {
314 return true;
315 }
316 }
317
318 return false;
319 }
320
321 void
lxb_dom_attr_remove(lxb_dom_attr_t * attr)322 lxb_dom_attr_remove(lxb_dom_attr_t *attr)
323 {
324 lxb_dom_element_t *element = attr->owner;
325 lxb_dom_document_t *doc = lxb_dom_interface_node(attr)->owner_document;
326
327 if (doc->ev_remove != NULL) {
328 doc->ev_remove(lxb_dom_interface_node(attr));
329 }
330
331 if (element->attr_id == attr) {
332 element->attr_id = NULL;
333 }
334 else if (element->attr_class == attr) {
335 element->attr_class = NULL;
336 }
337
338 if (attr->prev != NULL) {
339 attr->prev->next = attr->next;
340 }
341 else {
342 element->first_attr = attr->next;
343 }
344
345 if (attr->next != NULL) {
346 attr->next->prev = attr->prev;
347 }
348 else {
349 element->last_attr = attr->prev;
350 }
351
352 attr->next = NULL;
353 attr->prev = NULL;
354 attr->owner = NULL;
355 }
356
357 lxb_dom_attr_data_t *
lxb_dom_attr_local_name_append(lexbor_hash_t * hash,const lxb_char_t * name,size_t length)358 lxb_dom_attr_local_name_append(lexbor_hash_t *hash,
359 const lxb_char_t *name, size_t length)
360 {
361 lxb_dom_attr_data_t *data;
362 const lexbor_shs_entry_t *entry;
363
364 if (name == NULL || length == 0) {
365 return NULL;
366 }
367
368 entry = lexbor_shs_entry_get_lower_static(lxb_dom_attr_res_shs_data,
369 name, length);
370 if (entry != NULL) {
371 return entry->value;
372 }
373
374 data = lexbor_hash_insert(hash, lexbor_hash_insert_lower, name, length);
375 if (data == NULL) {
376 return NULL;
377 }
378
379 data->attr_id = (uintptr_t) data;
380
381 return data;
382 }
383
384 lxb_dom_attr_data_t *
lxb_dom_attr_qualified_name_append(lexbor_hash_t * hash,const lxb_char_t * name,size_t length)385 lxb_dom_attr_qualified_name_append(lexbor_hash_t *hash, const lxb_char_t *name,
386 size_t length)
387 {
388 lxb_dom_attr_data_t *data;
389
390 if (name == NULL || length == 0) {
391 return NULL;
392 }
393
394 data = lexbor_hash_insert(hash, lexbor_hash_insert_raw, name, length);
395 if (data == NULL) {
396 return NULL;
397 }
398
399 data->attr_id = (uintptr_t) data;
400
401 return data;
402 }
403
404 const lxb_dom_attr_data_t *
lxb_dom_attr_data_undef(void)405 lxb_dom_attr_data_undef(void)
406 {
407 return &lxb_dom_attr_res_data_default[LXB_DOM_ATTR__UNDEF];
408 }
409
410 const lxb_dom_attr_data_t *
lxb_dom_attr_data_by_id(lexbor_hash_t * hash,lxb_dom_attr_id_t attr_id)411 lxb_dom_attr_data_by_id(lexbor_hash_t *hash, lxb_dom_attr_id_t attr_id)
412 {
413 if (attr_id >= LXB_DOM_ATTR__LAST_ENTRY) {
414 if (attr_id == LXB_DOM_ATTR__LAST_ENTRY) {
415 return NULL;
416 }
417
418 return (const lxb_dom_attr_data_t *) attr_id;
419 }
420
421 return &lxb_dom_attr_res_data_default[attr_id];
422 }
423
424 const lxb_dom_attr_data_t *
lxb_dom_attr_data_by_local_name(lexbor_hash_t * hash,const lxb_char_t * name,size_t length)425 lxb_dom_attr_data_by_local_name(lexbor_hash_t *hash,
426 const lxb_char_t *name, size_t length)
427 {
428 const lexbor_shs_entry_t *entry;
429
430 if (name == NULL || length == 0) {
431 return NULL;
432 }
433
434 entry = lexbor_shs_entry_get_lower_static(lxb_dom_attr_res_shs_data,
435 name, length);
436 if (entry != NULL) {
437 return entry->value;
438 }
439
440 return lexbor_hash_search(hash, lexbor_hash_search_lower, name, length);
441 }
442
443 const lxb_dom_attr_data_t *
lxb_dom_attr_data_by_qualified_name(lexbor_hash_t * hash,const lxb_char_t * name,size_t length)444 lxb_dom_attr_data_by_qualified_name(lexbor_hash_t *hash,
445 const lxb_char_t *name, size_t length)
446 {
447 const lexbor_shs_entry_t *entry;
448
449 if (name == NULL || length == 0) {
450 return NULL;
451 }
452
453 entry = lexbor_shs_entry_get_static(lxb_dom_attr_res_shs_data,
454 name, length);
455 if (entry != NULL) {
456 return entry->value;
457 }
458
459 return lexbor_hash_search(hash, lexbor_hash_search_raw, name, length);
460 }
461
462 const lxb_char_t *
lxb_dom_attr_qualified_name(lxb_dom_attr_t * attr,size_t * len)463 lxb_dom_attr_qualified_name(lxb_dom_attr_t *attr, size_t *len)
464 {
465 const lxb_dom_attr_data_t *data;
466
467 if (attr->qualified_name != 0) {
468 data = lxb_dom_attr_data_by_id(attr->node.owner_document->attrs,
469 attr->qualified_name);
470 }
471 else {
472 data = lxb_dom_attr_data_by_id(attr->node.owner_document->attrs,
473 attr->node.local_name);
474 }
475
476 if (len != NULL) {
477 *len = data->entry.length;
478 }
479
480 return lexbor_hash_entry_str(&data->entry);
481 }
482
483 /*
484 * No inline functions for ABI.
485 */
486 const lxb_char_t *
lxb_dom_attr_local_name_noi(lxb_dom_attr_t * attr,size_t * len)487 lxb_dom_attr_local_name_noi(lxb_dom_attr_t *attr, size_t *len)
488 {
489 return lxb_dom_attr_local_name(attr, len);
490 }
491
492 const lxb_char_t *
lxb_dom_attr_value_noi(lxb_dom_attr_t * attr,size_t * len)493 lxb_dom_attr_value_noi(lxb_dom_attr_t *attr, size_t *len)
494 {
495 return lxb_dom_attr_value(attr, len);
496 }
497