xref: /php-src/ext/dom/lexbor/lexbor/dom/interfaces/attr.c (revision f0934090)
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