Author: rinrab
Date: Tue Dec 17 20:07:43 2024
New Revision: 1922567

URL: http://svn.apache.org/viewvc?rev=1922567&view=rev
Log:
Implement wrapper around XML parser into an svn_stream_t.

The current XML parser API needs a bunch of function to invoke. Firstly
it requires to create a parser using svn_xml_make_parser(), then push
the chunks using svn_xml_parse(), and finally close/free/dispose the
parser using svn_xml_free_parser(). This API sometimes over complicate
the usage or may require some additional work for the users.

This commit introduces a wrapper into an svn_stream_t. It may help the
users to create a parser from any kind of stream or a file.

Use code like this to parse an XML file, assuming callbacks implemented
separately:

[[[
svn_stream_t *fstream;
svn_stream_t *xml_stream;
svn_xml_parser_t *xml_parser;

SVN_ERR(svn_stream_open_readonly(&fstream, fpath, pool, pool));

xml_parser = svn_xml_make_parser(baton, start_handler, end_handler,
                                 data_handler, pool);
xml_stream = svn_xml_make_parse_stream(xml_parser, pool);

SVN_ERR(svn_stream_copy3(fstream, xml_stream, NULL, NULL, pool));
/* svn_stream_copy3() automatically closes fstream and xml_stream */
]]]

...This makes it simpler a lot, due to a unified stream.

Also adding the declaration of the svn_xml_make_parse_stream() function with
its docstring to the log-message:

[[[
/** Create a stream that wraps the XML parser described at @a parser.
 *
 * The stream produced will implement 'write' and 'close' methods. It
 * will push the data to the parser on write operation, and flush it on
 * close.
 *
 * This stream can be used as a generic writable stream, so the callers
 * may pipe any data there, for example, using the svn_stream_copy3
 * function, in case of a file source.
 *
 * @since New in 1.15.
 */
svn_stream_t *
svn_xml_make_parse_stream(svn_xml_parser_t *parser,
                          apr_pool_t *result_pool);
]]]

Currently there are no usages of this function, only the tests have been
introduced. In the feature, this could be used for the xpatch parser.

This stream would have been implemented in a separate file, xml_stream.c, as
part of the libsvn_subr API.

* subversion/include/svn_xml.h
  (includes): Add 'svn_io.h' for svn_stream_t.
  (svn_xml_make_parse_stream): Declare function.
* subversion/libsvn_subr/xml_stream.c: New file.
  (xml_stream_baton_t): New baton.
  (xml_stream_write, xml_stream_close): New functions, implementing
   svn_stream_t interface.
  (svn_xml_make_parse_stream): Implement function.
* subversion/tests/libsvn_subr/xml-test.c
  (test_xml_parse_stream, test_xml_parse_stream_invalid_xml): New tests.
  (test_funcs): Run these tests.

Added:
    subversion/trunk/subversion/libsvn_subr/xml_stream.c   (with props)
Modified:
    subversion/trunk/subversion/include/svn_xml.h
    subversion/trunk/subversion/tests/libsvn_subr/xml-test.c

Modified: subversion/trunk/subversion/include/svn_xml.h
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/include/svn_xml.h?rev=1922567&r1=1922566&r2=1922567&view=diff
==============================================================================
--- subversion/trunk/subversion/include/svn_xml.h (original)
+++ subversion/trunk/subversion/include/svn_xml.h Tue Dec 17 20:07:43 2024
@@ -33,6 +33,7 @@
 
 #include "svn_types.h"
 #include "svn_string.h"
+#include "svn_io.h" /* for svn_stream_t */
 
 #ifdef __cplusplus
 extern "C" {
@@ -191,6 +192,23 @@ void
 svn_xml_free_parser(svn_xml_parser_t *svn_parser);
 
 
+/** Create a stream that wraps the XML parser described at @a parser.
+ *
+ * The stream produced will implement 'write' and 'close' methods. It
+ * will push the data to the parser on write operation, and flush it on
+ * close.
+ *
+ * This stream can be used as a generic writable stream, so the callers
+ * may pipe any data there, for example, using the svn_stream_copy3
+ * function, in case of a file source.
+ *
+ * @since New in 1.15.
+ */
+svn_stream_t *
+svn_xml_make_parse_stream(svn_xml_parser_t *parser,
+                          apr_pool_t *result_pool);
+
+
 /** Push @a len bytes of xml data in @a buf at @a svn_parser.
  *
  * If this is the final push, @a is_final must be set.

Added: subversion/trunk/subversion/libsvn_subr/xml_stream.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_subr/xml_stream.c?rev=1922567&view=auto
==============================================================================
--- subversion/trunk/subversion/libsvn_subr/xml_stream.c (added)
+++ subversion/trunk/subversion/libsvn_subr/xml_stream.c Tue Dec 17 20:07:43 
2024
@@ -0,0 +1,103 @@
+/*
+ * xml_stream.c:  implements a writable XML parse stream
+ *
+ * ====================================================================
+ *    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.
+ * ====================================================================
+ */
+
+
+
+/*** Includes. ***/
+
+#include "svn_error.h"
+#include "svn_io.h"
+#include "svn_xml.h"
+
+
+typedef struct xml_stream_baton_t
+{
+  /* Handle to an XML parser. NULL means that the parser has been already
+     disposed or we've closed the stream. */
+  svn_xml_parser_t *parser;
+} xml_stream_baton_t;
+
+
+/* This implements svn_write_fn_t. */
+static svn_error_t *
+xml_stream_write(void *baton, const char *data, apr_size_t *len)
+{
+  xml_stream_baton_t *b = baton;
+  svn_error_t *err;
+
+  err = svn_xml_parse(b->parser, data, *len, FALSE);
+
+  if (err)
+    {
+      /* Dispose the parser due to an error. */
+      svn_xml_free_parser(b->parser);
+      b->parser = NULL;
+    }
+
+  return svn_error_trace(err);
+}
+
+/* This implements svn_close_fn_t. */
+static svn_error_t *
+xml_stream_close(void *baton)
+{
+  xml_stream_baton_t *b = baton;
+
+  if (b->parser)
+    {
+      /* Dispose the parser with a final push because we are closing
+         the stream. */
+      SVN_ERR(svn_xml_parse(b->parser, NULL, 0, TRUE));
+      svn_xml_free_parser(b->parser);
+      b->parser = NULL;
+    }
+  else
+    {
+      /* We have nothing to do with a disposed stream; Probably it failed
+         with an error before or we are now closing it second time. */
+    }
+
+  return SVN_NO_ERROR;
+}
+
+
+
+/* Public Interface */
+
+svn_stream_t *
+svn_xml_make_parse_stream(svn_xml_parser_t *parser,
+                          apr_pool_t *result_pool)
+{
+  svn_stream_t *result;
+  xml_stream_baton_t *baton;
+
+  baton = apr_pcalloc(result_pool, sizeof(*baton));
+  baton->parser = parser;
+
+  result = svn_stream_create(baton, result_pool);
+
+  svn_stream_set_write(result, xml_stream_write);
+  svn_stream_set_close(result, xml_stream_close);
+
+  return result;
+}

Propchange: subversion/trunk/subversion/libsvn_subr/xml_stream.c
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: subversion/trunk/subversion/tests/libsvn_subr/xml-test.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/libsvn_subr/xml-test.c?rev=1922567&r1=1922566&r2=1922567&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/libsvn_subr/xml-test.c (original)
+++ subversion/trunk/subversion/tests/libsvn_subr/xml-test.c Tue Dec 17 
20:07:43 2024
@@ -332,6 +332,51 @@ test_xml_doctype_declaration(apr_pool_t
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *
+test_xml_parse_stream(apr_pool_t *pool)
+{
+  const char *xml = "<root><tag1>value</tag1><tag2 a='v' /></root>";
+  xml_callbacks_baton_t b;
+  svn_stream_t *stream;
+
+  /* Test parsing XML in one chunk.*/
+  b.buf = svn_stringbuf_create_empty(pool);
+  b.parser = svn_xml_make_parser(&b, strbuf_start_elem, strbuf_end_elem,
+                                 strbuf_cdata, pool);
+  stream = svn_xml_make_parse_stream(b.parser, pool);
+
+  SVN_ERR(svn_stream_puts(stream, xml));
+  SVN_ERR(svn_stream_close(stream));
+
+  SVN_TEST_STRING_ASSERT(b.buf->data,
+                         "<root><tag1>value</tag1><tag2 a=v></tag2></root>");
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+test_xml_parse_stream_invalid_xml(apr_pool_t *pool)
+{
+  const char *xml = "<root><tag1></tag1>";
+  xml_callbacks_baton_t b;
+  svn_stream_t *stream;
+  svn_error_t *err;
+  apr_status_t status;
+
+  /* Test parsing XML in one chunk.*/
+  b.buf = svn_stringbuf_create_empty(pool);
+  b.parser = svn_xml_make_parser(&b, strbuf_start_elem, strbuf_end_elem,
+                                 strbuf_cdata, pool);
+  stream = svn_xml_make_parse_stream(b.parser, pool);
+
+  err = svn_error_compose_create(svn_stream_puts(stream, xml),
+                                 svn_stream_close(stream));
+
+  SVN_TEST_ASSERT_ANY_ERROR(err);
+
+  return SVN_NO_ERROR;
+}
+
 /* The test table.  */
 static int max_threads = 1;
 
@@ -354,6 +399,10 @@ static struct svn_test_descriptor_t test
                    "test XML custom entity expansion"),
     SVN_TEST_PASS2(test_xml_doctype_declaration,
                    "test XML doctype declaration"),
+    SVN_TEST_PASS2(test_xml_parse_stream,
+                   "test XML's svn_stream_t wrapper"),
+    SVN_TEST_PASS2(test_xml_parse_stream_invalid_xml,
+                   "test XML's svn_stream_t wrapper for invalid XML"),
     SVN_TEST_NULL
   };
 


Reply via email to