You are viewing a plain text version of this content. The canonical link for it is here.
Posted to c-commits@axis.apache.org by ro...@apache.org on 2026/01/23 13:54:39 UTC
(axis-axis2-c-core) 08/10: Fix adb_XXXX_add_ functions to produce correct XML for arrays (AXIS2C-1182)
This is an automated email from the ASF dual-hosted git repository.
robertlazarski pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/axis-axis2-c-core.git
commit 478d111e28ad909ea48a5fbc6b4e5e2860e9648e
Author: Robert Lazarski <ro...@gmail.com>
AuthorDate: Wed Jan 21 17:00:59 2026 -1000
Fix adb_XXXX_add_ functions to produce correct XML for arrays (AXIS2C-1182)
The add_ functions for array properties (maxOccurs > 1) were producing
empty XML tags because the struct used a single pointer instead of an
array list, causing values to be overwritten on each add_ call.
Changes:
- Use axutil_array_list_t* for array elements in generated structs
- Generate add_X(), get_X_at(), sizeof_X() array accessor functions
- Box primitive types (int, bool, double) when adding to arrays since
axutil_array_list stores void pointers
- Update serialization to loop through arrays creating separate XML elements
- Update deserialization to collect repeated elements into array list
- Properly free boxed primitives and array contents in destructor
Also fixes compilation errors for nested arrays containing primitive types
(e.g., int maxOccurs="unbounded") by boxing/unboxing values properly.
Includes 8 unit tests covering array struct type, accessor functions,
serialization, deserialization, header declarations, and destructor.
Co-Authored-By: Claude Opus 4.5 <no...@anthropic.com>
---
tools/codegen/native/src/stub_generator.c | 423 +++++++++++++++++----
tools/codegen/native/test/Makefile.am | 1 +
.../native/test/include/adb_test_framework.h | 27 ++
tools/codegen/native/test/src/adb_test_framework.c | 1 +
.../test/src/test_axis2c_1182_array_add_function.c | 316 +++++++++++++++
5 files changed, 693 insertions(+), 75 deletions(-)
diff --git a/tools/codegen/native/src/stub_generator.c b/tools/codegen/native/src/stub_generator.c
index 874485b0d..ad34e107a 100644
--- a/tools/codegen/native/src/stub_generator.c
+++ b/tools/codegen/native/src/stub_generator.c
@@ -903,6 +903,9 @@ generate_adb_complex_types(wsdl2c_context_t *context, const axutil_env_t *env)
const char *elem_name = elem->c_name ? elem->c_name : elem->name;
const char *c_type = "axis2_char_t*"; /* Default to string */
+ /* AXIS2C-1182: Check if this is an array element */
+ axis2_bool_t is_array = (elem->max_occurs > 1 || elem->max_occurs == -1);
+
/* AXIS2C-1421: Typeless elements are treated as anyType (axiom_node_t*) */
if (elem->is_typeless || elem->is_any_type) {
c_type = "axiom_node_t*";
@@ -925,21 +928,67 @@ generate_adb_complex_types(wsdl2c_context_t *context, const axutil_env_t *env)
}
}
- /* Getter - AXIS2C-1330: include prefix */
- fprintf(header_file, "/**\n");
- fprintf(header_file, " * Getter for %s\n", elem_name);
- fprintf(header_file, " */\n");
- fprintf(header_file, "%s AXIS2_CALL\n", c_type);
- fprintf(header_file, "%sadb_%s_get_%s(%sadb_%s_t* _this, const axutil_env_t *env);\n\n",
- prefix, type_name, elem_name, prefix, type_name);
-
- /* Setter - AXIS2C-1330: include prefix */
- fprintf(header_file, "/**\n");
- fprintf(header_file, " * Setter for %s\n", elem_name);
- fprintf(header_file, " */\n");
- fprintf(header_file, "axis2_status_t AXIS2_CALL\n");
- fprintf(header_file, "%sadb_%s_set_%s(%sadb_%s_t* _this, const axutil_env_t *env, %s value);\n\n",
- prefix, type_name, elem_name, prefix, type_name, c_type);
+ if (is_array) {
+ /* AXIS2C-1182: Array element - generate array-specific accessors */
+
+ /* Getter returns the whole array list */
+ fprintf(header_file, "/**\n");
+ fprintf(header_file, " * Getter for %s array\n", elem_name);
+ fprintf(header_file, " */\n");
+ fprintf(header_file, "axutil_array_list_t* AXIS2_CALL\n");
+ fprintf(header_file, "%sadb_%s_get_%s(%sadb_%s_t* _this, const axutil_env_t *env);\n\n",
+ prefix, type_name, elem_name, prefix, type_name);
+
+ /* Setter replaces the whole array list */
+ fprintf(header_file, "/**\n");
+ fprintf(header_file, " * Setter for %s array\n", elem_name);
+ fprintf(header_file, " */\n");
+ fprintf(header_file, "axis2_status_t AXIS2_CALL\n");
+ fprintf(header_file, "%sadb_%s_set_%s(%sadb_%s_t* _this, const axutil_env_t *env, axutil_array_list_t* value);\n\n",
+ prefix, type_name, elem_name, prefix, type_name);
+
+ /* add_ function to add single item */
+ fprintf(header_file, "/**\n");
+ fprintf(header_file, " * Add a single item to %s array\n", elem_name);
+ fprintf(header_file, " */\n");
+ fprintf(header_file, "axis2_status_t AXIS2_CALL\n");
+ fprintf(header_file, "%sadb_%s_add_%s(%sadb_%s_t* _this, const axutil_env_t *env, %s value);\n\n",
+ prefix, type_name, elem_name, prefix, type_name, c_type);
+
+ /* get_at function to get item at index */
+ fprintf(header_file, "/**\n");
+ fprintf(header_file, " * Get item at index from %s array\n", elem_name);
+ fprintf(header_file, " */\n");
+ fprintf(header_file, "%s AXIS2_CALL\n", c_type);
+ fprintf(header_file, "%sadb_%s_get_%s_at(%sadb_%s_t* _this, const axutil_env_t *env, int index);\n\n",
+ prefix, type_name, elem_name, prefix, type_name);
+
+ /* sizeof_ function to get array size */
+ fprintf(header_file, "/**\n");
+ fprintf(header_file, " * Get size of %s array\n", elem_name);
+ fprintf(header_file, " */\n");
+ fprintf(header_file, "int AXIS2_CALL\n");
+ fprintf(header_file, "%sadb_%s_sizeof_%s(%sadb_%s_t* _this, const axutil_env_t *env);\n\n",
+ prefix, type_name, elem_name, prefix, type_name);
+ } else {
+ /* Non-array: standard getter/setter */
+
+ /* Getter - AXIS2C-1330: include prefix */
+ fprintf(header_file, "/**\n");
+ fprintf(header_file, " * Getter for %s\n", elem_name);
+ fprintf(header_file, " */\n");
+ fprintf(header_file, "%s AXIS2_CALL\n", c_type);
+ fprintf(header_file, "%sadb_%s_get_%s(%sadb_%s_t* _this, const axutil_env_t *env);\n\n",
+ prefix, type_name, elem_name, prefix, type_name);
+
+ /* Setter - AXIS2C-1330: include prefix */
+ fprintf(header_file, "/**\n");
+ fprintf(header_file, " * Setter for %s\n", elem_name);
+ fprintf(header_file, " */\n");
+ fprintf(header_file, "axis2_status_t AXIS2_CALL\n");
+ fprintf(header_file, "%sadb_%s_set_%s(%sadb_%s_t* _this, const axutil_env_t *env, %s value);\n\n",
+ prefix, type_name, elem_name, prefix, type_name, c_type);
+ }
}
}
@@ -1012,8 +1061,15 @@ generate_adb_complex_types(wsdl2c_context_t *context, const axutil_env_t *env)
const char *elem_name = elem->c_name ? elem->c_name : elem->name;
const char *c_type = "axis2_char_t*";
+ /* AXIS2C-1182: Check if this is an array element (maxOccurs > 1 or unbounded) */
+ axis2_bool_t is_array = (elem->max_occurs > 1 || elem->max_occurs == -1);
+
+ if (is_array) {
+ /* AXIS2C-1182: Array elements use axutil_array_list_t* */
+ c_type = "axutil_array_list_t*";
+ }
/* AXIS2C-1421: Typeless elements are treated as anyType (axiom_node_t*) */
- if (elem->is_typeless || elem->is_any_type) {
+ else if (elem->is_typeless || elem->is_any_type) {
c_type = "axiom_node_t*";
}
else if (elem->type) {
@@ -1076,8 +1132,24 @@ generate_adb_complex_types(wsdl2c_context_t *context, const axutil_env_t *env)
const char *elem_name = elem->c_name ? elem->c_name : elem->name;
+ /* AXIS2C-1182: Check if this is an array element */
+ axis2_bool_t is_array = (elem->max_occurs > 1 || elem->max_occurs == -1);
+
+ if (is_array) {
+ /* AXIS2C-1182: Free array list and its string contents */
+ fprintf(source_file, " if (_this->%s) {\n", elem_name);
+ fprintf(source_file, " int _arr_i, _arr_size = axutil_array_list_size(_this->%s, env);\n", elem_name);
+ fprintf(source_file, " for (_arr_i = 0; _arr_i < _arr_size; _arr_i++) {\n");
+ fprintf(source_file, " void *_arr_item = axutil_array_list_get(_this->%s, env, _arr_i);\n", elem_name);
+ fprintf(source_file, " if (_arr_item) {\n");
+ fprintf(source_file, " AXIS2_FREE(env->allocator, _arr_item);\n");
+ fprintf(source_file, " }\n");
+ fprintf(source_file, " }\n");
+ fprintf(source_file, " axutil_array_list_free(_this->%s, env);\n", elem_name);
+ fprintf(source_file, " }\n");
+ }
/* Determine how to free this property based on type */
- if (elem->type && strstr(elem->type, "base64Binary")) {
+ else if (elem->type && strstr(elem->type, "base64Binary")) {
/* AXIS2C-1529: Use axutil_base64_binary_free for base64Binary */
fprintf(source_file, " if (_this->%s) {\n", elem_name);
fprintf(source_file, " axutil_base64_binary_free(_this->%s, env);\n", elem_name);
@@ -1118,6 +1190,9 @@ generate_adb_complex_types(wsdl2c_context_t *context, const axutil_env_t *env)
const char *c_type = "axis2_char_t*";
const char *default_val = "NULL";
+ /* AXIS2C-1182: Check if this is an array element */
+ axis2_bool_t is_array = (elem->max_occurs > 1 || elem->max_occurs == -1);
+
/* AXIS2C-1421: Typeless elements are treated as anyType (axiom_node_t*) */
if (elem->is_typeless || elem->is_any_type) {
c_type = "axiom_node_t*";
@@ -1146,25 +1221,114 @@ generate_adb_complex_types(wsdl2c_context_t *context, const axutil_env_t *env)
}
}
- /* Getter - AXIS2C-1330: include prefix */
- fprintf(source_file, "%s AXIS2_CALL\n", c_type);
- fprintf(source_file, "%sadb_%s_get_%s(%sadb_%s_t* _this, const axutil_env_t *env)\n",
- prefix, type_name, elem_name, prefix, type_name);
- fprintf(source_file, "{\n");
- fprintf(source_file, " AXIS2_ENV_CHECK(env, %s);\n", default_val);
- fprintf(source_file, " return _this ? _this->%s : %s;\n", elem_name, default_val);
- fprintf(source_file, "}\n\n");
-
- /* Setter - AXIS2C-1330: include prefix */
- fprintf(source_file, "axis2_status_t AXIS2_CALL\n");
- fprintf(source_file, "%sadb_%s_set_%s(%sadb_%s_t* _this, const axutil_env_t *env, %s value)\n",
- prefix, type_name, elem_name, prefix, type_name, c_type);
- fprintf(source_file, "{\n");
- fprintf(source_file, " AXIS2_ENV_CHECK(env, AXIS2_FAILURE);\n");
- fprintf(source_file, " AXIS2_PARAM_CHECK(env->error, _this, AXIS2_FAILURE);\n");
- fprintf(source_file, " _this->%s = value;\n", elem_name);
- fprintf(source_file, " return AXIS2_SUCCESS;\n");
- fprintf(source_file, "}\n\n");
+ if (is_array) {
+ /* AXIS2C-1182: Generate array accessor functions */
+
+ /* Check if this is a primitive type that needs boxing */
+ axis2_bool_t is_primitive = (elem->type &&
+ (strstr(elem->type, "int") || strstr(elem->type, "Integer") ||
+ strstr(elem->type, "boolean") || strstr(elem->type, "bool") ||
+ strstr(elem->type, "double") || strstr(elem->type, "float")));
+
+ /* get_ returns the whole array list */
+ fprintf(source_file, "axutil_array_list_t* AXIS2_CALL\n");
+ fprintf(source_file, "%sadb_%s_get_%s(%sadb_%s_t* _this, const axutil_env_t *env)\n",
+ prefix, type_name, elem_name, prefix, type_name);
+ fprintf(source_file, "{\n");
+ fprintf(source_file, " AXIS2_ENV_CHECK(env, NULL);\n");
+ fprintf(source_file, " return _this ? _this->%s : NULL;\n", elem_name);
+ fprintf(source_file, "}\n\n");
+
+ /* set_ replaces the whole array list */
+ fprintf(source_file, "axis2_status_t AXIS2_CALL\n");
+ fprintf(source_file, "%sadb_%s_set_%s(%sadb_%s_t* _this, const axutil_env_t *env, axutil_array_list_t* value)\n",
+ prefix, type_name, elem_name, prefix, type_name);
+ fprintf(source_file, "{\n");
+ fprintf(source_file, " AXIS2_ENV_CHECK(env, AXIS2_FAILURE);\n");
+ fprintf(source_file, " AXIS2_PARAM_CHECK(env->error, _this, AXIS2_FAILURE);\n");
+ fprintf(source_file, " _this->%s = value;\n", elem_name);
+ fprintf(source_file, " return AXIS2_SUCCESS;\n");
+ fprintf(source_file, "}\n\n");
+
+ /* AXIS2C-1182: add_ adds a single item to the array */
+ fprintf(source_file, "axis2_status_t AXIS2_CALL\n");
+ fprintf(source_file, "%sadb_%s_add_%s(%sadb_%s_t* _this, const axutil_env_t *env, %s value)\n",
+ prefix, type_name, elem_name, prefix, type_name, c_type);
+ fprintf(source_file, "{\n");
+ fprintf(source_file, " AXIS2_ENV_CHECK(env, AXIS2_FAILURE);\n");
+ fprintf(source_file, " AXIS2_PARAM_CHECK(env->error, _this, AXIS2_FAILURE);\n");
+ fprintf(source_file, " if (!_this->%s) {\n", elem_name);
+ fprintf(source_file, " _this->%s = axutil_array_list_create(env, 4);\n", elem_name);
+ fprintf(source_file, " if (!_this->%s) {\n", elem_name);
+ fprintf(source_file, " AXIS2_ERROR_SET(env->error, AXIS2_ERROR_NO_MEMORY, AXIS2_FAILURE);\n");
+ fprintf(source_file, " return AXIS2_FAILURE;\n");
+ fprintf(source_file, " }\n");
+ fprintf(source_file, " }\n");
+
+ if (is_primitive) {
+ /* Box primitive types - allocate memory for the value */
+ fprintf(source_file, " %s *_boxed = AXIS2_MALLOC(env->allocator, sizeof(%s));\n", c_type, c_type);
+ fprintf(source_file, " if (!_boxed) {\n");
+ fprintf(source_file, " AXIS2_ERROR_SET(env->error, AXIS2_ERROR_NO_MEMORY, AXIS2_FAILURE);\n");
+ fprintf(source_file, " return AXIS2_FAILURE;\n");
+ fprintf(source_file, " }\n");
+ fprintf(source_file, " *_boxed = value;\n");
+ fprintf(source_file, " return axutil_array_list_add(_this->%s, env, _boxed);\n", elem_name);
+ } else {
+ /* Pointer types can be added directly */
+ fprintf(source_file, " return axutil_array_list_add(_this->%s, env, value);\n", elem_name);
+ }
+ fprintf(source_file, "}\n\n");
+
+ /* get_at returns a single item at index */
+ fprintf(source_file, "%s AXIS2_CALL\n", c_type);
+ fprintf(source_file, "%sadb_%s_get_%s_at(%sadb_%s_t* _this, const axutil_env_t *env, int index)\n",
+ prefix, type_name, elem_name, prefix, type_name);
+ fprintf(source_file, "{\n");
+ fprintf(source_file, " AXIS2_ENV_CHECK(env, %s);\n", default_val);
+ fprintf(source_file, " if (!_this || !_this->%s) return %s;\n", elem_name, default_val);
+
+ if (is_primitive) {
+ /* Unbox primitive types - dereference the pointer */
+ fprintf(source_file, " %s *_boxed = (%s*)axutil_array_list_get(_this->%s, env, index);\n", c_type, c_type, elem_name);
+ fprintf(source_file, " return _boxed ? *_boxed : %s;\n", default_val);
+ } else {
+ fprintf(source_file, " return (%s)axutil_array_list_get(_this->%s, env, index);\n", c_type, elem_name);
+ }
+ fprintf(source_file, "}\n\n");
+
+ /* sizeof_ returns the array size */
+ fprintf(source_file, "int AXIS2_CALL\n");
+ fprintf(source_file, "%sadb_%s_sizeof_%s(%sadb_%s_t* _this, const axutil_env_t *env)\n",
+ prefix, type_name, elem_name, prefix, type_name);
+ fprintf(source_file, "{\n");
+ fprintf(source_file, " AXIS2_ENV_CHECK(env, 0);\n");
+ fprintf(source_file, " if (!_this || !_this->%s) return 0;\n", elem_name);
+ fprintf(source_file, " return axutil_array_list_size(_this->%s, env);\n", elem_name);
+ fprintf(source_file, "}\n\n");
+ } else {
+ /* Non-array: standard getter/setter */
+
+ /* Getter - AXIS2C-1330: include prefix */
+ fprintf(source_file, "%s AXIS2_CALL\n", c_type);
+ fprintf(source_file, "%sadb_%s_get_%s(%sadb_%s_t* _this, const axutil_env_t *env)\n",
+ prefix, type_name, elem_name, prefix, type_name);
+ fprintf(source_file, "{\n");
+ fprintf(source_file, " AXIS2_ENV_CHECK(env, %s);\n", default_val);
+ fprintf(source_file, " return _this ? _this->%s : %s;\n", elem_name, default_val);
+ fprintf(source_file, "}\n\n");
+
+ /* Setter - AXIS2C-1330: include prefix */
+ fprintf(source_file, "axis2_status_t AXIS2_CALL\n");
+ fprintf(source_file, "%sadb_%s_set_%s(%sadb_%s_t* _this, const axutil_env_t *env, %s value)\n",
+ prefix, type_name, elem_name, prefix, type_name, c_type);
+ fprintf(source_file, "{\n");
+ fprintf(source_file, " AXIS2_ENV_CHECK(env, AXIS2_FAILURE);\n");
+ fprintf(source_file, " AXIS2_PARAM_CHECK(env->error, _this, AXIS2_FAILURE);\n");
+ fprintf(source_file, " _this->%s = value;\n", elem_name);
+ fprintf(source_file, " return AXIS2_SUCCESS;\n");
+ fprintf(source_file, "}\n\n");
+ }
}
}
@@ -1197,46 +1361,110 @@ generate_adb_complex_types(wsdl2c_context_t *context, const axutil_env_t *env)
const char *elem_name = elem->c_name ? elem->c_name : elem->name;
const char *xml_name = elem->name; /* Use original name for XML */
+ /* AXIS2C-1182: Check if this is an array element */
+ axis2_bool_t is_array = (elem->max_occurs > 1 || elem->max_occurs == -1);
+
fprintf(source_file, " /* Serialize %s */\n", elem_name);
- fprintf(source_file, " if (_this->%s) {\n", elem_name);
- fprintf(source_file, " ns = axiom_namespace_create(env, \"\", NULL);\n");
- fprintf(source_file, " current_element = axiom_element_create(env, parent, \"%s\", ns, ¤t_node);\n", xml_name);
- fprintf(source_file, " if (current_element) {\n");
-
- /* Handle different types for serialization */
- if (elem->type && strstr(elem->type, "base64Binary")) {
- /* AXIS2C-1529: Memory-safe base64Binary serialization */
- fprintf(source_file, " /* AXIS2C-1529: base64Binary - get encoded value and FREE it after use */\n");
- fprintf(source_file, " text_value = axutil_base64_binary_get_encoded_binary(_this->%s, env);\n", elem_name);
- fprintf(source_file, " if (text_value) {\n");
- fprintf(source_file, " axiom_element_set_text(current_element, env, text_value, current_node);\n");
- fprintf(source_file, " /* AXIS2C-1529 FIX: Free the encoded string to prevent memory leak */\n");
- fprintf(source_file, " AXIS2_FREE(env->allocator, text_value);\n");
- fprintf(source_file, " text_value = NULL;\n");
- fprintf(source_file, " }\n");
- } else if (elem->type && strstr(elem->type, "dateTime")) {
- fprintf(source_file, " text_value = axutil_date_time_serialize_date_time(_this->%s, env);\n", elem_name);
- fprintf(source_file, " if (text_value) {\n");
- fprintf(source_file, " axiom_element_set_text(current_element, env, text_value, current_node);\n");
- fprintf(source_file, " AXIS2_FREE(env->allocator, text_value);\n");
- fprintf(source_file, " text_value = NULL;\n");
- fprintf(source_file, " }\n");
- } else if (elem->type && (strstr(elem->type, "int") || strstr(elem->type, "Integer"))) {
- fprintf(source_file, " sprintf(tmp_buf, \"%%d\", _this->%s);\n", elem_name);
- fprintf(source_file, " axiom_element_set_text(current_element, env, tmp_buf, current_node);\n");
- } else if (elem->type && (strstr(elem->type, "boolean") || strstr(elem->type, "bool"))) {
- fprintf(source_file, " axiom_element_set_text(current_element, env, _this->%s ? \"true\" : \"false\", current_node);\n", elem_name);
- } else if (elem->type && (strstr(elem->type, "double") || strstr(elem->type, "float"))) {
- fprintf(source_file, " sprintf(tmp_buf, \"%%g\", _this->%s);\n", elem_name);
- fprintf(source_file, " axiom_element_set_text(current_element, env, tmp_buf, current_node);\n");
- } else if (!elem->is_typeless && !elem->is_any_type) {
- /* String type */
- fprintf(source_file, " axiom_element_set_text(current_element, env, _this->%s, current_node);\n", elem_name);
- }
- /* anyType/typeless: the axiom_node_t* should be attached as child, not as text */
- fprintf(source_file, " }\n");
- fprintf(source_file, " }\n\n");
+ if (is_array) {
+ /* AXIS2C-1182: Loop through array and serialize each item */
+ /* Check if this is a primitive type that needs unboxing */
+ axis2_bool_t is_primitive = (elem->type &&
+ (strstr(elem->type, "int") || strstr(elem->type, "Integer") ||
+ strstr(elem->type, "boolean") || strstr(elem->type, "bool") ||
+ strstr(elem->type, "double") || strstr(elem->type, "float")));
+
+ fprintf(source_file, " if (_this->%s) {\n", elem_name);
+ fprintf(source_file, " int _ser_i, _ser_size = axutil_array_list_size(_this->%s, env);\n", elem_name);
+ fprintf(source_file, " for (_ser_i = 0; _ser_i < _ser_size; _ser_i++) {\n");
+
+ if (is_primitive) {
+ /* Primitive types are boxed - need to unbox and convert to string */
+ if (elem->type && (strstr(elem->type, "int") || strstr(elem->type, "Integer"))) {
+ fprintf(source_file, " int *_arr_item = (int*)axutil_array_list_get(_this->%s, env, _ser_i);\n", elem_name);
+ fprintf(source_file, " if (_arr_item) {\n");
+ fprintf(source_file, " ns = axiom_namespace_create(env, \"\", NULL);\n");
+ fprintf(source_file, " current_element = axiom_element_create(env, parent, \"%s\", ns, ¤t_node);\n", xml_name);
+ fprintf(source_file, " if (current_element) {\n");
+ fprintf(source_file, " sprintf(tmp_buf, \"%%d\", *_arr_item);\n");
+ fprintf(source_file, " axiom_element_set_text(current_element, env, tmp_buf, current_node);\n");
+ fprintf(source_file, " }\n");
+ fprintf(source_file, " }\n");
+ } else if (elem->type && (strstr(elem->type, "boolean") || strstr(elem->type, "bool"))) {
+ fprintf(source_file, " axis2_bool_t *_arr_item = (axis2_bool_t*)axutil_array_list_get(_this->%s, env, _ser_i);\n", elem_name);
+ fprintf(source_file, " if (_arr_item) {\n");
+ fprintf(source_file, " ns = axiom_namespace_create(env, \"\", NULL);\n");
+ fprintf(source_file, " current_element = axiom_element_create(env, parent, \"%s\", ns, ¤t_node);\n", xml_name);
+ fprintf(source_file, " if (current_element) {\n");
+ fprintf(source_file, " axiom_element_set_text(current_element, env, *_arr_item ? \"true\" : \"false\", current_node);\n");
+ fprintf(source_file, " }\n");
+ fprintf(source_file, " }\n");
+ } else if (elem->type && (strstr(elem->type, "double") || strstr(elem->type, "float"))) {
+ fprintf(source_file, " double *_arr_item = (double*)axutil_array_list_get(_this->%s, env, _ser_i);\n", elem_name);
+ fprintf(source_file, " if (_arr_item) {\n");
+ fprintf(source_file, " ns = axiom_namespace_create(env, \"\", NULL);\n");
+ fprintf(source_file, " current_element = axiom_element_create(env, parent, \"%s\", ns, ¤t_node);\n", xml_name);
+ fprintf(source_file, " if (current_element) {\n");
+ fprintf(source_file, " sprintf(tmp_buf, \"%%g\", *_arr_item);\n");
+ fprintf(source_file, " axiom_element_set_text(current_element, env, tmp_buf, current_node);\n");
+ fprintf(source_file, " }\n");
+ fprintf(source_file, " }\n");
+ }
+ } else {
+ /* String or pointer types - no unboxing needed */
+ fprintf(source_file, " axis2_char_t *_arr_item = (axis2_char_t*)axutil_array_list_get(_this->%s, env, _ser_i);\n", elem_name);
+ fprintf(source_file, " if (_arr_item) {\n");
+ fprintf(source_file, " ns = axiom_namespace_create(env, \"\", NULL);\n");
+ fprintf(source_file, " current_element = axiom_element_create(env, parent, \"%s\", ns, ¤t_node);\n", xml_name);
+ fprintf(source_file, " if (current_element) {\n");
+ fprintf(source_file, " axiom_element_set_text(current_element, env, _arr_item, current_node);\n");
+ fprintf(source_file, " }\n");
+ fprintf(source_file, " }\n");
+ }
+ fprintf(source_file, " }\n");
+ fprintf(source_file, " }\n\n");
+ } else {
+ /* Non-array: single element serialization */
+ fprintf(source_file, " if (_this->%s) {\n", elem_name);
+ fprintf(source_file, " ns = axiom_namespace_create(env, \"\", NULL);\n");
+ fprintf(source_file, " current_element = axiom_element_create(env, parent, \"%s\", ns, ¤t_node);\n", xml_name);
+ fprintf(source_file, " if (current_element) {\n");
+
+ /* Handle different types for serialization */
+ if (elem->type && strstr(elem->type, "base64Binary")) {
+ /* AXIS2C-1529: Memory-safe base64Binary serialization */
+ fprintf(source_file, " /* AXIS2C-1529: base64Binary - get encoded value and FREE it after use */\n");
+ fprintf(source_file, " text_value = axutil_base64_binary_get_encoded_binary(_this->%s, env);\n", elem_name);
+ fprintf(source_file, " if (text_value) {\n");
+ fprintf(source_file, " axiom_element_set_text(current_element, env, text_value, current_node);\n");
+ fprintf(source_file, " /* AXIS2C-1529 FIX: Free the encoded string to prevent memory leak */\n");
+ fprintf(source_file, " AXIS2_FREE(env->allocator, text_value);\n");
+ fprintf(source_file, " text_value = NULL;\n");
+ fprintf(source_file, " }\n");
+ } else if (elem->type && strstr(elem->type, "dateTime")) {
+ fprintf(source_file, " text_value = axutil_date_time_serialize_date_time(_this->%s, env);\n", elem_name);
+ fprintf(source_file, " if (text_value) {\n");
+ fprintf(source_file, " axiom_element_set_text(current_element, env, text_value, current_node);\n");
+ fprintf(source_file, " AXIS2_FREE(env->allocator, text_value);\n");
+ fprintf(source_file, " text_value = NULL;\n");
+ fprintf(source_file, " }\n");
+ } else if (elem->type && (strstr(elem->type, "int") || strstr(elem->type, "Integer"))) {
+ fprintf(source_file, " sprintf(tmp_buf, \"%%d\", _this->%s);\n", elem_name);
+ fprintf(source_file, " axiom_element_set_text(current_element, env, tmp_buf, current_node);\n");
+ } else if (elem->type && (strstr(elem->type, "boolean") || strstr(elem->type, "bool"))) {
+ fprintf(source_file, " axiom_element_set_text(current_element, env, _this->%s ? \"true\" : \"false\", current_node);\n", elem_name);
+ } else if (elem->type && (strstr(elem->type, "double") || strstr(elem->type, "float"))) {
+ fprintf(source_file, " sprintf(tmp_buf, \"%%g\", _this->%s);\n", elem_name);
+ fprintf(source_file, " axiom_element_set_text(current_element, env, tmp_buf, current_node);\n");
+ } else if (!elem->is_typeless && !elem->is_any_type) {
+ /* String type */
+ fprintf(source_file, " axiom_element_set_text(current_element, env, _this->%s, current_node);\n", elem_name);
+ }
+ /* anyType/typeless: the axiom_node_t* should be attached as child, not as text */
+
+ fprintf(source_file, " }\n");
+ fprintf(source_file, " }\n\n");
+ }
}
}
@@ -1280,12 +1508,57 @@ generate_adb_complex_types(wsdl2c_context_t *context, const axutil_env_t *env)
const char *elem_name = elem->c_name ? elem->c_name : elem->name;
const char *xml_name = elem->name;
+ /* AXIS2C-1182: Check if this is an array element */
+ axis2_bool_t is_array = (elem->max_occurs > 1 || elem->max_occurs == -1);
+
fprintf(source_file, " %sif (axutil_strcmp(local_name, \"%s\") == 0) {\n",
j > 0 ? "else " : "", xml_name);
fprintf(source_file, " text_value = axiom_element_get_text(current_element, env, current_node);\n");
+ if (is_array) {
+ /* AXIS2C-1182: Collect array elements into axutil_array_list_t */
+ /* Check if this is a primitive type that needs boxing */
+ axis2_bool_t is_primitive = (elem->type &&
+ (strstr(elem->type, "int") || strstr(elem->type, "Integer") ||
+ strstr(elem->type, "boolean") || strstr(elem->type, "bool") ||
+ strstr(elem->type, "double") || strstr(elem->type, "float")));
+
+ fprintf(source_file, " if (text_value) {\n");
+ fprintf(source_file, " if (!obj->%s) {\n", elem_name);
+ fprintf(source_file, " obj->%s = axutil_array_list_create(env, 4);\n", elem_name);
+ fprintf(source_file, " }\n");
+ fprintf(source_file, " if (obj->%s) {\n", elem_name);
+
+ if (is_primitive) {
+ /* Box primitive types when deserializing */
+ if (elem->type && (strstr(elem->type, "int") || strstr(elem->type, "Integer"))) {
+ fprintf(source_file, " int *_boxed = AXIS2_MALLOC(env->allocator, sizeof(int));\n");
+ fprintf(source_file, " if (_boxed) {\n");
+ fprintf(source_file, " *_boxed = atoi(text_value);\n");
+ fprintf(source_file, " axutil_array_list_add(obj->%s, env, _boxed);\n", elem_name);
+ fprintf(source_file, " }\n");
+ } else if (elem->type && (strstr(elem->type, "boolean") || strstr(elem->type, "bool"))) {
+ fprintf(source_file, " axis2_bool_t *_boxed = AXIS2_MALLOC(env->allocator, sizeof(axis2_bool_t));\n");
+ fprintf(source_file, " if (_boxed) {\n");
+ fprintf(source_file, " *_boxed = (axutil_strcmp(text_value, \"true\") == 0 || axutil_strcmp(text_value, \"1\") == 0) ? AXIS2_TRUE : AXIS2_FALSE;\n");
+ fprintf(source_file, " axutil_array_list_add(obj->%s, env, _boxed);\n", elem_name);
+ fprintf(source_file, " }\n");
+ } else if (elem->type && (strstr(elem->type, "double") || strstr(elem->type, "float"))) {
+ fprintf(source_file, " double *_boxed = AXIS2_MALLOC(env->allocator, sizeof(double));\n");
+ fprintf(source_file, " if (_boxed) {\n");
+ fprintf(source_file, " *_boxed = atof(text_value);\n");
+ fprintf(source_file, " axutil_array_list_add(obj->%s, env, _boxed);\n", elem_name);
+ fprintf(source_file, " }\n");
+ }
+ } else {
+ /* String types - store duplicated string */
+ fprintf(source_file, " axutil_array_list_add(obj->%s, env, axutil_strdup(env, text_value));\n", elem_name);
+ }
+ fprintf(source_file, " }\n");
+ fprintf(source_file, " }\n");
+ }
/* Handle different types for deserialization */
- if (elem->type && strstr(elem->type, "base64Binary")) {
+ else if (elem->type && strstr(elem->type, "base64Binary")) {
fprintf(source_file, " if (text_value) {\n");
fprintf(source_file, " axutil_base64_binary_t *b64 = axutil_base64_binary_create(env);\n");
fprintf(source_file, " if (b64) {\n");
diff --git a/tools/codegen/native/test/Makefile.am b/tools/codegen/native/test/Makefile.am
index 3312f6355..1b58e8e40 100644
--- a/tools/codegen/native/test/Makefile.am
+++ b/tools/codegen/native/test/Makefile.am
@@ -42,6 +42,7 @@ adb_test_runner_SOURCES = \
src/test_axis2c_1573_identifier_sanitization.c \
src/test_axis2c_1529_base64_memleak.c \
src/test_axis2c_1613_error_on_null.c \
+ src/test_axis2c_1182_array_add_function.c \
src/axis2_stub_compat.c
# Include directories
diff --git a/tools/codegen/native/test/include/adb_test_framework.h b/tools/codegen/native/test/include/adb_test_framework.h
index 70eed7ef6..bf7e30dd3 100644
--- a/tools/codegen/native/test/include/adb_test_framework.h
+++ b/tools/codegen/native/test/include/adb_test_framework.h
@@ -365,6 +365,33 @@ extern int axis2c_1529_test_count;
extern adb_test_case_t axis2c_1613_tests[];
extern int axis2c_1613_test_count;
+/* AXIS2C-1182 array add function tests - Correct Array Serialization
+ *
+ * AXIS2C-1182: adb_XXXX_add_ functions failing to produce correct XML
+ * Analysis (2026-01-21): When using adb_Predicate_add_nodes(), the generated
+ * XML produces empty <nodes/> tags instead of separate <nodes>...</nodes>
+ * elements for each item. This is because the struct was using a single
+ * pointer instead of an axutil_array_list_t*.
+ *
+ * Fix: Native generator now:
+ * - Uses axutil_array_list_t* for array elements (maxOccurs > 1)
+ * - Generates add_X() to add items to the array list
+ * - Generates get_X_at() to get item at index
+ * - Generates sizeof_X() to get array size
+ * - Serializes array by looping through list creating separate XML elements
+ * - Deserializes array by collecting repeated elements into the list
+ *
+ * Test scenarios:
+ * - Generated struct uses axutil_array_list_t* for array elements
+ * - add_ function creates and populates array list
+ * - get_at function retrieves items by index
+ * - sizeof_ function returns array size
+ * - Serialization creates multiple XML elements
+ * - Deserialization collects multiple elements into array
+ */
+extern adb_test_case_t axis2c_1182_tests[];
+extern int axis2c_1182_test_count;
+
/* Global test statistics */
extern adb_test_stats_t g_test_stats;
diff --git a/tools/codegen/native/test/src/adb_test_framework.c b/tools/codegen/native/test/src/adb_test_framework.c
index 94a22a9ff..fcdb18a9b 100644
--- a/tools/codegen/native/test/src/adb_test_framework.c
+++ b/tools/codegen/native/test/src/adb_test_framework.c
@@ -470,6 +470,7 @@ int main(int argc, char *argv[]) {
adb_run_test_suite(axis2c_1573_tests, axis2c_1573_test_count);
adb_run_test_suite(axis2c_1529_tests, axis2c_1529_test_count);
adb_run_test_suite(axis2c_1613_tests, axis2c_1613_test_count);
+ adb_run_test_suite(axis2c_1182_tests, axis2c_1182_test_count);
adb_test_cleanup();
diff --git a/tools/codegen/native/test/src/test_axis2c_1182_array_add_function.c b/tools/codegen/native/test/src/test_axis2c_1182_array_add_function.c
new file mode 100644
index 000000000..496af2f72
--- /dev/null
+++ b/tools/codegen/native/test/src/test_axis2c_1182_array_add_function.c
@@ -0,0 +1,316 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * AXIS2C-1182: adb_XXXX_add_ functions failing to produce correct XML
+ *
+ * Analysis: When using adb_Predicate_add_nodes(), the generated XML produces
+ * empty <nodes/> tags instead of separate <nodes>...</nodes> elements for each
+ * item. This is because the struct was using a single pointer instead of an
+ * axutil_array_list_t*, so add_ just overwrote the value each time.
+ *
+ * Fix: The native generator now:
+ * - Uses axutil_array_list_t* for array elements (maxOccurs > 1)
+ * - Generates add_X() to add items to the array list
+ * - Generates get_X_at() to get item at index
+ * - Generates sizeof_X() to get array size
+ * - Serializes array by looping through list creating separate XML elements
+ * - Deserializes array by collecting repeated elements into the list
+ *
+ * Test scenarios:
+ * - Generated struct uses axutil_array_list_t* for array elements
+ * - add_ function creates and populates array list
+ * - get_at function retrieves items by index
+ * - sizeof_ function returns array size
+ * - Serialization creates multiple XML elements
+ * - Deserialization collects multiple elements into array
+ */
+
+#include "adb_test_framework.h"
+
+/* Test 1: Verify generated struct uses axutil_array_list_t* for arrays */
+adb_test_result_t test_axis2c_1182_array_struct_type(void) {
+ printf("Testing AXIS2C-1182: Generated struct uses axutil_array_list_t* for arrays...\n");
+
+ adb_codegen_test_t test_config = {
+ .wsdl_path = "wsdl/axis2c_1182_test.wsdl",
+ .output_dir = "output/axis2c_1182_test",
+ .databinding = "adb",
+ .unwrap = 0,
+ .server_side = 0
+ };
+
+ /* WSDL with array element (maxOccurs="unbounded") */
+ const char *array_wsdl =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<definitions xmlns=\"http://schemas.xmlsoap.org/wsdl/\"\n"
+ " xmlns:tns=\"http://axis2c.apache.org/test/\"\n"
+ " xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"\n"
+ " xmlns:soap=\"http://schemas.xmlsoap.org/wsdl/soap/\"\n"
+ " targetNamespace=\"http://axis2c.apache.org/test/\">\n"
+ " <types>\n"
+ " <xsd:schema targetNamespace=\"http://axis2c.apache.org/test/\">\n"
+ " <xsd:complexType name=\"Predicate\">\n"
+ " <xsd:sequence>\n"
+ " <xsd:element name=\"nodes\" type=\"xsd:string\" maxOccurs=\"unbounded\"/>\n"
+ " </xsd:sequence>\n"
+ " </xsd:complexType>\n"
+ " <xsd:element name=\"PredicateRequest\" type=\"tns:Predicate\"/>\n"
+ " <xsd:element name=\"PredicateResponse\" type=\"xsd:string\"/>\n"
+ " </xsd:schema>\n"
+ " </types>\n"
+ " <message name=\"PredicateMessage\">\n"
+ " <part name=\"body\" element=\"tns:PredicateRequest\"/>\n"
+ " </message>\n"
+ " <message name=\"PredicateResponseMessage\">\n"
+ " <part name=\"body\" element=\"tns:PredicateResponse\"/>\n"
+ " </message>\n"
+ " <portType name=\"PredicatePortType\">\n"
+ " <operation name=\"testPredicate\">\n"
+ " <input message=\"tns:PredicateMessage\"/>\n"
+ " <output message=\"tns:PredicateResponseMessage\"/>\n"
+ " </operation>\n"
+ " </portType>\n"
+ " <binding name=\"PredicateBinding\" type=\"tns:PredicatePortType\">\n"
+ " <soap:binding style=\"document\" transport=\"http://schemas.xmlsoap.org/soap/http\"/>\n"
+ " <operation name=\"testPredicate\">\n"
+ " <soap:operation soapAction=\"urn:testPredicate\"/>\n"
+ " <input><soap:body use=\"literal\"/></input>\n"
+ " <output><soap:body use=\"literal\"/></output>\n"
+ " </operation>\n"
+ " </binding>\n"
+ " <service name=\"PredicateService\">\n"
+ " <port name=\"PredicatePort\" binding=\"tns:PredicateBinding\">\n"
+ " <soap:address location=\"http://localhost:8080/axis2/services/PredicateService\"/>\n"
+ " </port>\n"
+ " </service>\n"
+ "</definitions>\n";
+
+ /* Create directories */
+ if (system("mkdir -p wsdl output/axis2c_1182_test") != 0) {
+ printf("Warning: Failed to create directories\n");
+ }
+
+ /* Write WSDL */
+ FILE *wsdl_file = fopen("wsdl/axis2c_1182_test.wsdl", "w");
+ ADB_ASSERT_NOT_NULL(wsdl_file, "Could not create WSDL file");
+ fputs(array_wsdl, wsdl_file);
+ fclose(wsdl_file);
+
+ /* Generate code */
+ adb_test_result_t result = adb_test_code_generation(&test_config);
+ ADB_ASSERT_TRUE(result == ADB_TEST_SUCCESS, "Code generation failed");
+
+ /* Verify generated struct uses axutil_array_list_t* */
+ FILE *source = fopen("output/axis2c_1182_test/src/adb_Predicate.c", "r");
+ ADB_ASSERT_NOT_NULL(source, "Could not open generated source file");
+
+ char line[512];
+ int found_array_list_type = 0;
+ while (fgets(line, sizeof(line), source)) {
+ /* Look for the struct definition with axutil_array_list_t* */
+ if (strstr(line, "axutil_array_list_t*") && strstr(line, "nodes")) {
+ found_array_list_type = 1;
+ break;
+ }
+ }
+ fclose(source);
+
+ ADB_ASSERT_TRUE(found_array_list_type, "Generated struct should use axutil_array_list_t* for nodes");
+
+ printf(" PASS: Generated struct uses axutil_array_list_t* for array elements\n");
+ return ADB_TEST_SUCCESS;
+}
+
+/* Test 2: Verify add_ function is generated */
+adb_test_result_t test_axis2c_1182_add_function_generated(void) {
+ printf("Testing AXIS2C-1182: add_ function is generated...\n");
+
+ /* Uses output from previous test */
+ FILE *source = fopen("output/axis2c_1182_test/src/adb_Predicate.c", "r");
+ ADB_ASSERT_NOT_NULL(source, "Could not open generated source file");
+
+ char line[512];
+ int found_add_function = 0;
+ while (fgets(line, sizeof(line), source)) {
+ if (strstr(line, "adb_Predicate_add_nodes")) {
+ found_add_function = 1;
+ break;
+ }
+ }
+ fclose(source);
+
+ ADB_ASSERT_TRUE(found_add_function, "Generated code should have add_nodes function");
+
+ printf(" PASS: add_nodes() function is generated\n");
+ return ADB_TEST_SUCCESS;
+}
+
+/* Test 3: Verify get_at function is generated */
+adb_test_result_t test_axis2c_1182_get_at_function_generated(void) {
+ printf("Testing AXIS2C-1182: get_at function is generated...\n");
+
+ FILE *source = fopen("output/axis2c_1182_test/src/adb_Predicate.c", "r");
+ ADB_ASSERT_NOT_NULL(source, "Could not open generated source file");
+
+ char line[512];
+ int found_get_at_function = 0;
+ while (fgets(line, sizeof(line), source)) {
+ if (strstr(line, "adb_Predicate_get_nodes_at")) {
+ found_get_at_function = 1;
+ break;
+ }
+ }
+ fclose(source);
+
+ ADB_ASSERT_TRUE(found_get_at_function, "Generated code should have get_nodes_at function");
+
+ printf(" PASS: get_nodes_at() function is generated\n");
+ return ADB_TEST_SUCCESS;
+}
+
+/* Test 4: Verify sizeof_ function is generated */
+adb_test_result_t test_axis2c_1182_sizeof_function_generated(void) {
+ printf("Testing AXIS2C-1182: sizeof_ function is generated...\n");
+
+ FILE *source = fopen("output/axis2c_1182_test/src/adb_Predicate.c", "r");
+ ADB_ASSERT_NOT_NULL(source, "Could not open generated source file");
+
+ char line[512];
+ int found_sizeof_function = 0;
+ while (fgets(line, sizeof(line), source)) {
+ if (strstr(line, "adb_Predicate_sizeof_nodes")) {
+ found_sizeof_function = 1;
+ break;
+ }
+ }
+ fclose(source);
+
+ ADB_ASSERT_TRUE(found_sizeof_function, "Generated code should have sizeof_nodes function");
+
+ printf(" PASS: sizeof_nodes() function is generated\n");
+ return ADB_TEST_SUCCESS;
+}
+
+/* Test 5: Verify serialization loops through array */
+adb_test_result_t test_axis2c_1182_serialization_loops(void) {
+ printf("Testing AXIS2C-1182: Serialization loops through array...\n");
+
+ FILE *source = fopen("output/axis2c_1182_test/src/adb_Predicate.c", "r");
+ ADB_ASSERT_NOT_NULL(source, "Could not open generated source file");
+
+ char buffer[32768];
+ size_t bytes_read = fread(buffer, 1, sizeof(buffer) - 1, source);
+ buffer[bytes_read] = '\0';
+ fclose(source);
+
+ /* Check for serialization loop pattern */
+ int found_ser_loop = (strstr(buffer, "_ser_size = axutil_array_list_size") != NULL) &&
+ (strstr(buffer, "for (_ser_i = 0; _ser_i < _ser_size") != NULL);
+
+ ADB_ASSERT_TRUE(found_ser_loop, "Serialization should loop through array list");
+
+ printf(" PASS: Serialization loops through array creating separate elements\n");
+ return ADB_TEST_SUCCESS;
+}
+
+/* Test 6: Verify deserialization collects into array */
+adb_test_result_t test_axis2c_1182_deserialization_collects(void) {
+ printf("Testing AXIS2C-1182: Deserialization collects into array...\n");
+
+ FILE *source = fopen("output/axis2c_1182_test/src/adb_Predicate.c", "r");
+ ADB_ASSERT_NOT_NULL(source, "Could not open generated source file");
+
+ char buffer[32768];
+ size_t bytes_read = fread(buffer, 1, sizeof(buffer) - 1, source);
+ buffer[bytes_read] = '\0';
+ fclose(source);
+
+ /* Check for deserialization array collection pattern */
+ int found_deser_collect = (strstr(buffer, "axutil_array_list_create(env, 4)") != NULL) &&
+ (strstr(buffer, "axutil_array_list_add(obj->nodes") != NULL);
+
+ ADB_ASSERT_TRUE(found_deser_collect, "Deserialization should collect elements into array list");
+
+ printf(" PASS: Deserialization collects multiple elements into array\n");
+ return ADB_TEST_SUCCESS;
+}
+
+/* Test 7: Verify header declarations for array functions */
+adb_test_result_t test_axis2c_1182_header_declarations(void) {
+ printf("Testing AXIS2C-1182: Header has array function declarations...\n");
+
+ FILE *header = fopen("output/axis2c_1182_test/src/adb_Predicate.h", "r");
+ ADB_ASSERT_NOT_NULL(header, "Could not open generated header file");
+
+ char buffer[16384];
+ size_t bytes_read = fread(buffer, 1, sizeof(buffer) - 1, header);
+ buffer[bytes_read] = '\0';
+ fclose(header);
+
+ /* Check for function declarations */
+ int has_get = strstr(buffer, "adb_Predicate_get_nodes(") != NULL;
+ int has_set = strstr(buffer, "adb_Predicate_set_nodes(") != NULL;
+ int has_add = strstr(buffer, "adb_Predicate_add_nodes(") != NULL;
+ int has_get_at = strstr(buffer, "adb_Predicate_get_nodes_at(") != NULL;
+ int has_sizeof = strstr(buffer, "adb_Predicate_sizeof_nodes(") != NULL;
+
+ ADB_ASSERT_TRUE(has_get, "Header should declare get_nodes()");
+ ADB_ASSERT_TRUE(has_set, "Header should declare set_nodes()");
+ ADB_ASSERT_TRUE(has_add, "Header should declare add_nodes()");
+ ADB_ASSERT_TRUE(has_get_at, "Header should declare get_nodes_at()");
+ ADB_ASSERT_TRUE(has_sizeof, "Header should declare sizeof_nodes()");
+
+ printf(" PASS: Header declares all array accessor functions\n");
+ return ADB_TEST_SUCCESS;
+}
+
+/* Test 8: Verify destructor frees array contents */
+adb_test_result_t test_axis2c_1182_destructor_frees_array(void) {
+ printf("Testing AXIS2C-1182: Destructor properly frees array contents...\n");
+
+ FILE *source = fopen("output/axis2c_1182_test/src/adb_Predicate.c", "r");
+ ADB_ASSERT_NOT_NULL(source, "Could not open generated source file");
+
+ char buffer[32768];
+ size_t bytes_read = fread(buffer, 1, sizeof(buffer) - 1, source);
+ buffer[bytes_read] = '\0';
+ fclose(source);
+
+ /* Check for destructor array cleanup pattern */
+ int found_arr_free = (strstr(buffer, "for (_arr_i = 0; _arr_i < _arr_size") != NULL) &&
+ (strstr(buffer, "axutil_array_list_free(") != NULL);
+
+ ADB_ASSERT_TRUE(found_arr_free, "Destructor should free array contents");
+
+ printf(" PASS: Destructor properly frees array list and contents\n");
+ return ADB_TEST_SUCCESS;
+}
+
+/* Test case array */
+adb_test_case_t axis2c_1182_tests[] = {
+ {"test_axis2c_1182_array_struct_type", "AXIS2C-1182: Generated struct uses axutil_array_list_t*", test_axis2c_1182_array_struct_type},
+ {"test_axis2c_1182_add_function_generated", "AXIS2C-1182: add_ function is generated", test_axis2c_1182_add_function_generated},
+ {"test_axis2c_1182_get_at_function_generated", "AXIS2C-1182: get_at function is generated", test_axis2c_1182_get_at_function_generated},
+ {"test_axis2c_1182_sizeof_function_generated", "AXIS2C-1182: sizeof_ function is generated", test_axis2c_1182_sizeof_function_generated},
+ {"test_axis2c_1182_serialization_loops", "AXIS2C-1182: Serialization loops through array", test_axis2c_1182_serialization_loops},
+ {"test_axis2c_1182_deserialization_collects", "AXIS2C-1182: Deserialization collects into array", test_axis2c_1182_deserialization_collects},
+ {"test_axis2c_1182_header_declarations", "AXIS2C-1182: Header has array function declarations", test_axis2c_1182_header_declarations},
+ {"test_axis2c_1182_destructor_frees_array", "AXIS2C-1182: Destructor properly frees array contents", test_axis2c_1182_destructor_frees_array}
+};
+
+int axis2c_1182_test_count = sizeof(axis2c_1182_tests) / sizeof(adb_test_case_t);