Adaptive Framework  0.9.0
All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
afw_yaml_from_value.c
Go to the documentation of this file.
1 // See the 'COPYING' file in the project root for licensing information.
2 /*
3  * AFW YAML convert from value
4  *
5  * Copyright (c) 2010-2023 Clemson University
6  *
7  */
8 
14 #include "afw.h"
15 #include "afw_yaml.h"
16 #include <apr_buckets.h>
17 
18 typedef struct from_value_wa_s {
19  afw_xctx_t *xctx;
20  const afw_pool_t *p;
21  const afw_object_options_t *options;
22  afw_boolean_t do_ws;
23  int indent;
24  afw_boolean_t skip_next_ws;
25  void * context;
26  afw_write_cb_t callback;
27  int object_depth;
29 
30 static void put_ws(
31  from_value_wa_t *wa);
32 
33 static from_value_wa_t * create_from_value_wa(
34  const afw_pool_t *p, afw_xctx_t *xctx);
35 
36 static void put_yaml_string(
37  from_value_wa_t *wa,
38  const afw_utf8_t *string);
39 
40 static void convert_string_to_literal_style_yaml(
41  from_value_wa_t *wa,
42  const afw_utf8_t *string);
43 
44 static void convert_string_to_yaml(
45  from_value_wa_t *wa,
46  const afw_value_t *value);
47 
48 static void convert_integer_to_yaml(
49  from_value_wa_t *wa,
50  afw_integer_t i);
51 
52 static void convert_number_to_yaml(
53  from_value_wa_t *wa,
54  double d);
55 
56 static void convert_boolean_to_yaml(
57  from_value_wa_t *wa,
58  afw_boolean_t b);
59 
60 static void convert_list_to_yaml(
61  from_value_wa_t *wa,
62  const afw_list_t *list);
63 
64 static void convert_object_to_yaml(
65  from_value_wa_t *wa,
66  const afw_object_t *obj);
67 
68 static void convert_value_to_yaml(
69  from_value_wa_t *wa,
70  const afw_value_t *value);
71 
72 AFW_DEFINE_STATIC_INLINE(void) impl_putc(from_value_wa_t *wa, afw_byte_t c)
73 {
74  char ch;
75 
76  ch = c;
77  wa->callback(wa->context, &ch, 1, wa->p, wa->xctx);
78 }
79 
80 
81 AFW_DEFINE_STATIC_INLINE(void) impl_puts(from_value_wa_t *wa,
82  const afw_utf8_z_t *s)
83 {
84  wa->callback(wa->context, s, strlen(s), wa->p, wa->xctx);
85 }
86 
87 
88 AFW_DEFINE_STATIC_INLINE(void) impl_write(from_value_wa_t *wa,
89  const void * buffer, afw_size_t size)
90 {
91  wa->callback(wa->context, buffer, size, wa->p, wa->xctx);
92 }
93 
94 
95 static void impl_printf(from_value_wa_t *wa,
96  const afw_utf8_z_t *format_z, ...)
97 {
98  va_list arg;
99  const afw_utf8_t *s;
100 
101  va_start(arg, format_z);
102  s = afw_utf8_printf_v(format_z, arg, wa->p, wa->xctx);
103  va_end(arg);
104  wa->callback(wa->context, s->s, s->len, wa->p, wa->xctx);
105 }
106 
107 
108 void put_ws(
109  from_value_wa_t *wa)
110 {
111  int indent;
112 
113  if (wa->do_ws) {
114  if (!wa->skip_next_ws) {
115  impl_putc(wa, '\n');
116  for (indent = 1; indent <= wa->indent; indent++) {
117  impl_puts(wa, " ");
118  }
119  }
120  wa->skip_next_ws = 0;
121  }
122 }
123 
124 from_value_wa_t * create_from_value_wa(
125  const afw_pool_t *p, afw_xctx_t *xctx)
126 {
127  from_value_wa_t *wa;
128 
130  wa->xctx = xctx;
131  wa->p = p;
132 
133  return wa;
134 }
135 
136 void put_yaml_string(
137  from_value_wa_t *wa,
138  const afw_utf8_t *string)
139 {
140  unsigned char c;
141  afw_size_t len;
142  const afw_utf8_octet_t *s;
143 
144  s = string->s;
145  len = string->len;
146 
147  while (len > 0) {
148  c = *s;
149 
150  /* If not a control character, output the character. */
151  if (c >= 32) {
152  impl_putc(wa, c);
153  }
154 
155  /*
156  * If control character, output as /u followed by character as four
157  * byte hex characters.
158  */
159  else {
160  impl_printf(wa, "\\u%04x", c);
161  }
162 
163  len--;
164  s++;
165  }
166 }
167 
168 
169 /*
170  * This write a string using the literal block scalar style as described in
171  * Chapter 8 at https://yaml.org/spec/1.2.2/#rule-c-indentation-indicator.
172  */
173 void convert_string_to_literal_style_yaml(
174  from_value_wa_t *wa,
175  const afw_utf8_t *string)
176 {
177  unsigned char c;
178  afw_size_t len;
179  const afw_utf8_octet_t *s;
180 
181  s = string->s;
182  len = string->len;
183 
184  /* Indicate literal style. */
185  impl_putc(wa, '|');
186 
187  /* If first char is space, add indentation indicator. */
188  if (*s == ' ') {
189  impl_printf(wa, "%d", wa->indent);
190  }
191 
192  /* If string ends with a newline, use KEEP chomping indicator. */
193  if (s[len - 1] == '\n') {
194  impl_putc(wa, '+');
195  }
196 
197  /* If string does not ends with a newline, use STRIP chomping indicator. */
198  else {
199  impl_putc(wa, '-');
200  }
201 
202  /* Put newline and indentation. */
203  put_ws(wa);
204 
205  /* Write string. */
206  while (len > 0) {
207  c = *s;
208 
209  /* If \n, line break and add indent. */
210  if (c == '\n') {
211  put_ws(wa);
212  }
213 
214  /*
215  * If not new line, just write character asis.
216  */
217  else {
218  impl_putc(wa, c);
219  }
220 
221  /* Increment. */
222  len--;
223  s++;
224  }
225 }
226 
227 
228 
229 /*
230  * If the string has newlines, use the literal style. If not, just use JSON's
231  * representation.
232  */
233 void convert_string_to_yaml(
234  from_value_wa_t *wa,
235  const afw_value_t *value)
236 {
237  const afw_utf8_t *string;
238  const afw_utf8_octet_t *c, *end;
239 
240  string = afw_value_as_utf8(value, wa->xctx->p, wa->xctx);
241  if (!string) {
242  AFW_THROW_ERROR_Z(general, "Error converting string.", wa->xctx);
243  }
244 
245  /* If string contains a newline, write as literal style. */
246  if (string->len > 0) {
247  for (c = string->s, end = c + string->len; c < end; c++)
248  {
249  if (*c == '\n') {
250  convert_string_to_literal_style_yaml(wa, string);
251  return;
252  }
253  }
254  }
255 
256  /* If there are no newlines, just write as JSON quoted string. */
257  string = afw_json_utf8_string_create(string, wa->p, wa->xctx);
258  impl_write(wa, string->s, string->len);
259 }
260 
261 
262 /*
263  * Convert integer.
264  */
265 void convert_integer_to_yaml(
266  from_value_wa_t *wa,
267  afw_integer_t i)
268 {
269  impl_printf(wa, "%" AFW_INTEGER_FMT, i);
270 }
271 
272 /*
273  * Since JSON numbers are a valid subset of YAML numbers, we'll just use
274  * JSON's representation.
275  *
276  * FIXME: I've read that numeric one (1) should be quoted, otherwise
277  * it will be parsed as boolean true.
278  */
279 void convert_number_to_yaml(
280  from_value_wa_t *wa,
281  double d)
282 {
283  /* IMPORTANT IMPORTANT IMPORTANT IMPORTANT IMPORTANT IMPORTANT
284  *
285  * Make sure s is one longer than precision plus 1 for \0 plus a
286  * little padding because of implementation differences that may
287  * not count leading sign in precision, etc. For %.23g, 30 is used
288  * just to be safe.
289  */
290  char s[30]; /* 23 + 1 for \0 + some padding for sign, etc. */
291 
292  /*
293  * %.23g will be able to print minus, plus 16 digits, plus period,
294  * plus 'e', plus sign, plus 3 digits.
295  *
296  * JSON only supports finite numbers. If not finite, return a string
297  * with "NaN", "-INF", or "INF".
298  */
299  if (afw_number_is_finite(d)) {
300  sprintf(s, "%.23G", d);
301  }
302  else if (afw_number_is_NaN(d)) {
303  strcpy(s, "\"" AFW_JSON_Q_NAN "\"");
304  }
305  else if (d <= DBL_MAX) {
306  strcpy(s, "\"" AFW_JSON_Q_MINUS_INFINITY "\"");
307  }
308  else {
309  strcpy(s, "\"" AFW_JSON_Q_INFINITY "\"");
310  }
311  impl_puts(wa, s);
312 }
313 
314 void convert_boolean_to_yaml(
315  from_value_wa_t *wa,
316  afw_boolean_t b)
317 {
318  if (b) {
319  impl_puts(wa,
320  AFW_JSON_Q_PRIMITIVE_BOOLEAN_TRUE);
321  }
322  else {
323  impl_puts(wa,
324  AFW_JSON_Q_PRIMITIVE_BOOLEAN_FALSE);
325  }
326 }
327 
328 void convert_list_to_yaml(
329  from_value_wa_t *wa,
330  const afw_list_t *list)
331 {
332  const afw_iterator_t *list_iterator;
333  const afw_value_t *next;
334 
335  impl_puts(wa, "- ");
336 
337 
338  list_iterator = NULL;
339  next = afw_list_get_next_value(list, &list_iterator, wa->p, wa->xctx);
340 
341  while (next) {
342  (wa->indent)++;
343  convert_value_to_yaml(wa, next);
344  (wa->indent)--;
345  put_ws(wa);
346  next = afw_list_get_next_value(list, &list_iterator,
347  wa->p, wa->xctx);
348  if (next) {
349  impl_puts(wa, "- ");
350  }
351  }
352 }
353 
354 void convert_object_to_yaml(
355  from_value_wa_t *wa,
356  const afw_object_t *obj)
357 {
358  const afw_iterator_t *property_iterator;
359  const afw_utf8_t *property_name;
360  const afw_value_t *next;
361  const afw_object_t *meta;
362 
363  /* Get first property. */
364  property_iterator = NULL;
365  next = afw_object_get_next_property(obj, &property_iterator,
366  &property_name, wa->xctx);
367 
368 
369  /* If object has meta, convert it first. */
371  wa->options, wa->p, wa->xctx);
372  if (meta) {
373  (wa->indent)++;
374  put_yaml_string(wa, &afw_s__meta_);
375  impl_puts(wa, ": ");
376  put_ws(wa);
377  convert_object_to_yaml(wa, meta);
378  (wa->indent)--;
379  if (next) {
380  put_ws(wa);
381  }
382  }
383 
384 
385  /* Add each object property. */
386  if (next) {
387  while (1) {
388  (wa->indent)++;
389  put_yaml_string(wa, property_name);
390 
391  impl_puts(wa, ": ");
392 
393  convert_value_to_yaml(wa, next);
394 
395  next = afw_object_get_next_property(obj,
396  &property_iterator, &property_name, wa->xctx);
397 
398  (wa->indent)--;
399  if (next)
400  put_ws(wa);
401  if (!next) {
402  break;
403  }
404  }
405  }
406 
407 }
408 
409 void convert_value_to_yaml(
410  from_value_wa_t *wa,
411  const afw_value_t *value)
412 {
413  const afw_data_type_t *value_data_type;
414  afw_value_info_t info;
415 
416  /* Change NULL value pointer to null. */
417  if (!value) {
418  value = afw_value_null;
419  }
420 
421  /* Value must be evaluated already. */
423  afw_value_get_info(value, &info, wa->p, wa->xctx);
424  AFW_THROW_ERROR_FZ(general, wa->xctx,
425  "Unevaluated value encountered producing yaml "
426  "(%" AFW_UTF8_FMT " %" AFW_UTF8_FMT ")",
427  AFW_UTF8_FMT_ARG(info.value_inf_id),
428  AFW_UTF8_FMT_OPTIONAL_ARG(info.detail)
429  );
430  }
431 
432  /* Get data type. */
433  value_data_type = afw_value_get_data_type(value, wa->xctx);
434 
435  /* If value is a list, convert list to yaml. */
436  if (afw_value_is_list(value)) {
437  put_ws(wa);
438  convert_list_to_yaml(wa, ((afw_value_list_t *)value)->internal);
439  }
440 
441  /* If value is object, convert object to yaml. */
442  else if (afw_value_is_object(value)) {
443  put_ws(wa);
444  convert_object_to_yaml(wa,
445  ((afw_value_object_t *)value)->internal);
446  }
447 
448  /* If value is single, process based on jsonPrimitive value of dataType. */
449  else if (afw_value_is_defined_and_evaluated(value)) {
450 
451  /* Primitive json type is null. */
452  if (afw_utf8_equal(&value_data_type->jsonPrimitive,
453  &AFW_JSON_S_PRIMITIVE_NULL))
454  {
455  impl_puts(wa, AFW_JSON_Q_PRIMITIVE_NULL);
456  }
457 
458  /* Primitive json type is string. */
459  else if (afw_utf8_equal(&value_data_type->jsonPrimitive,
460  &AFW_JSON_S_PRIMITIVE_STRING))
461  {
462  convert_string_to_yaml(wa, value);
463  }
464 
465  /* Primitive json type is number. */
466  else if (afw_utf8_equal(&value_data_type->jsonPrimitive,
467  &AFW_JSON_S_PRIMITIVE_NUMBER))
468  {
469  if (afw_value_is_integer(value)) {
470  convert_integer_to_yaml(wa,
471  ((const afw_value_integer_t *)value)->internal);
472  }
473  else if (afw_value_is_double(value)) {
474  convert_number_to_yaml(wa,
475  ((const afw_value_double_t *)value)->internal);
476  }
477  else {
478  AFW_THROW_ERROR_Z(general,
479  "jsonPrimitive number not supported for this data type",
480  wa->xctx);
481  }
482  }
483 
484  /* Primitive json type is boolean. */
485  else if (afw_utf8_equal(&value_data_type->jsonPrimitive,
486  &AFW_JSON_S_PRIMITIVE_BOOLEAN))
487  {
488  convert_boolean_to_yaml(wa,
489  ((afw_value_boolean_t *)value)->internal);
490  }
491  }
492 
493  /* If value is function, evaluated value and convert value to json. */
494  else if (0 ) {
500  }
501 
502  /* If none of above then it's an error. */
503  else {
504  AFW_THROW_ERROR_Z(general,
505  "Value type is invalid", wa->xctx);
506  }
507 }
508 
509 
510 /* Convert a value to json and write it. */
511 extern void afw_yaml_internal_write_value(
512  const afw_value_t *value,
513  const afw_object_options_t *options,
514  void * context,
515  afw_write_cb_t callback,
516  const afw_pool_t *p, afw_xctx_t *xctx)
517 {
518  from_value_wa_t *wa;
519 
520  /* Create and initialize workarea and associated resources. */
521  wa = create_from_value_wa(p, xctx);
522  wa->options = options;
523  wa->do_ws = 1;
524  wa->skip_next_ws = 0;
525  wa->context = context;
526  wa->callback = callback;
527 
528  /* begin the document */
529  impl_puts(wa, "---");
530  (wa->indent)++;
531 
532  /* Convert object to json. */
533  convert_value_to_yaml(wa, value);
534 }
535 
536 
537 
538 /* Convert a value to yaml. */
540  const afw_value_t *value,
541  const afw_pool_t *p, afw_xctx_t *xctx)
542 {
543  const afw_memory_writer_t *writer;
544  const afw_memory_t *raw;
545 
546  writer = afw_memory_create_writer(p, xctx);
547 
548  afw_yaml_internal_write_value(value, NULL,
549  writer->context, writer->callback, p, xctx);
550 
551  raw = afw_memory_writer_retrieve_and_release(writer, xctx);
552  return afw_utf8_from_raw(raw, p, xctx);
553 }
Adaptive Framework Core API.
Header file for Adaptive Framework YAML.
#define afw_value_is_double(A_VALUE)
Macro to determine if value is evaluated double.
#define afw_value_is_integer(A_VALUE)
Macro to determine if value is evaluated integer.
#define afw_value_is_list(A_VALUE)
Macro to determine if value is evaluated list.
#define afw_value_is_object(A_VALUE)
Macro to determine if value is evaluated object.
#define AFW_UTF8_FMT_ARG(A_STRING)
Convenience Macro for use with AFW_UTF8_FMT to specify arg.
Definition: afw_common.h:605
struct afw_iterator_s afw_iterator_t
#define AFW_UTF8_FMT_OPTIONAL_ARG(A_STRING)
Convenience Macro for use with AFW_UTF8_FMT to specify optional arg.
Definition: afw_common.h:616
#define AFW_INTEGER_FMT
Format string specifier used for afw_integer_t.
Definition: afw_common.h:326
_Bool afw_boolean_t
Definition: afw_common.h:373
unsigned char afw_byte_t
A byte of memory (unsigned).
Definition: afw_common.h:208
#define AFW_UTF8_FMT
Format string specifier used for afw_utf8_t.
Definition: afw_common.h:588
afw_utf8_octet_t afw_utf8_z_t
NFC normalized UTF-8 null terminated string.
Definition: afw_common.h:523
char afw_utf8_octet_t
8 bits of utf-8 codepoint.
Definition: afw_common.h:236
apr_size_t afw_size_t
size_t.
Definition: afw_common.h:151
afw_size_t(* afw_write_cb_t)(void *context, const void *buffer, afw_size_t size, const afw_pool_t *p, afw_xctx_t *xctx)
Typedef for write callback function.
Definition: afw_common.h:1226
apr_int64_t afw_integer_t
typedef for big signed int.
Definition: afw_common.h:321
#define AFW_THROW_ERROR_FZ(code, xctx, format_z,...)
Macro used to set error and 0 rv in xctx and throw it.
Definition: afw_error.h:319
#define AFW_THROW_ERROR_Z(code, message_z, xctx)
Macro used to set error and 0 rv in xctx and throw it.
Definition: afw_error.h:283
afw_json_utf8_string_create(const afw_utf8_t *string, const afw_pool_t *p, afw_xctx_t *xctx)
Create a json encoded quoted string.
#define afw_list_get_next_value(instance, iterator, p, xctx)
Call method get_next_value of interface afw_list.
afw_memory_writer_retrieve_and_release(const afw_memory_writer_t *writer, afw_xctx_t *xctx)
Retrieve memory as one chunk from memory writer and release writer.
Definition: afw_memory.c:441
afw_memory_create_writer(const afw_pool_t *p, afw_xctx_t *xctx)
Create a memory writer.
Definition: afw_memory.c:377
afw_boolean_t afw_number_is_NaN(double d)
Determine if double is not a number.
Definition: afw_number.h:84
afw_boolean_t afw_number_is_finite(double d)
Determine if double is finite.
Definition: afw_number.h:45
#define afw_object_get_next_property(instance, iterator, property_name, xctx)
Call method get_next_property of interface afw_object.
const afw_object_t * afw_object_meta_create_accessor_with_options(const afw_object_t *instance, const afw_object_options_t *options, const afw_pool_t *p, afw_xctx_t *xctx)
Create an object instance to access an object's meta with options.
const afw_utf8_t * afw_utf8_from_raw(const afw_memory_t *raw, const afw_pool_t *p, afw_xctx_t *xctx)
Convert raw to a utf-8 NFC normalizing if necessary in specified pool.
Definition: afw_utf8.h:199
afw_utf8_printf_v(const afw_utf8_z_t *format, va_list arg, const afw_pool_t *p, afw_xctx_t *xctx)
Create a utf-8 string using a c format string in specified pool.
Definition: afw_utf8.c:477
afw_boolean_t afw_utf8_equal(const afw_utf8_t *s1, const afw_utf8_t *s2)
Check to see if a string equals another string.
#define afw_value_get_data_type(instance, xctx)
Call method get_data_type of interface afw_value.
#define afw_value_get_info(instance, info, p, xctx)
Call method get_info of interface afw_value.
afw_value_as_utf8(const afw_value_t *value, const afw_pool_t *p, afw_xctx_t *xctx)
Definition: afw_value.c:456
#define afw_value_is_defined_and_evaluated(A_VALUE)
Macro to determine if value is defined and evaluated.
Definition: afw_value.h:481
afw_value_null
Adaptive value null.
Definition: afw_value.h:320
#define afw_xctx_calloc_type(type, xctx)
Macro to allocate cleared memory to hold type in xctx's pool.
Definition: afw_xctx.h:199
const afw_utf8_t * afw_yaml_from_value(const afw_value_t *value, const afw_pool_t *p, afw_xctx_t *xctx)
Convert an adaptive value to YAML.
Interface afw_data_type public struct.
Interface afw_list public struct.
Struct for memory pointer and size.
Definition: afw_common.h:505
Return value from afw_memory_create_writer().
Definition: afw_memory.h:102
Struct for object processing options.
Interface afw_object public struct.
Interface afw_pool public struct.
NFC normalized UTF-8 string.
Definition: afw_common.h:545
struct for data type boolean values.
struct for data type double values.
Filled in by afw_value get_info method.
Definition: afw_value.h:49
struct for data type integer values.
struct for data type list values.
struct for data type object values.
Interface afw_value public struct.
Interface afw_xctx public struct.