# HG changeset patch # User Markus Mützel # Date 1601843724 -7200 # Sun Oct 04 22:35:24 2020 +0200 # Node ID 64c5e64894648c2a09184d04403a75d27b2308ed # Parent e8fa10f8920c0bbc1678dbeb5fc95b6d56ce176f Add "xmlread" and "xmlwrite" functions (bug #59245). * xmlread.m, xmlwrite.m: New functions. * dom_node.m, dom_document.m, dom_attribute.m: New classdef that mimick the XML DOM node, document, and attribute used in Java. * scripts/io/module.mk: Add new files to list. * NEWS: Announce new functions. * __unimplemented__.m: Remove files from list. * libinterp/corefcn/xmlwrite.cc: Interface to RapidXML. * libinterp/corefcn/module.mk: Add new file to list. * configure.ac: Add check for RapidXML (header-only library). * doc/interpreter/io.txi: Add docstrings of new functions to manual. diff -r e8fa10f8920c -r 64c5e6489464 NEWS --- a/NEWS Wed Nov 04 17:35:02 2020 +0100 +++ b/NEWS Sun Oct 04 22:35:24 2020 +0200 @@ -169,6 +169,8 @@ * `rng` * `startsWith` * `streamribbon` +* `xmlread` +* `xmlwrite` ### Deprecated functions and properties diff -r e8fa10f8920c -r 64c5e6489464 configure.ac --- a/configure.ac Wed Nov 04 17:35:02 2020 +0100 +++ b/configure.ac Sun Oct 04 22:35:24 2020 +0200 @@ -1371,6 +1371,17 @@ fi AC_LANG_POP([C++]) +### Check for RapidXML header only library. + +AC_LANG_PUSH(C++) +AC_CHECK_HEADERS([rapidxml/rapidxml.hpp rapidxml/rapidxml_print.hpp], + [have_rapidxml=yes], [have_rapidxml=no]) + +if test $have_rapidxml = yes; then + AC_DEFINE(HAVE_RAPIDXML, 1, [Define to 1 if RapidXML is available.]) +fi +AC_LANG_POP(C++) + ### Check for readline library. OCTAVE_ENABLE_READLINE diff -r e8fa10f8920c -r 64c5e6489464 doc/interpreter/io.txi --- a/doc/interpreter/io.txi Wed Nov 04 17:35:02 2020 +0100 +++ b/doc/interpreter/io.txi Sun Oct 04 22:35:24 2020 +0200 @@ -222,12 +222,25 @@ @DOCSTRING(csvread) -Formatted data from can be read from, or written to, text files as well. +Formatted data can be read from, or written to, text files as well. @DOCSTRING(textread) @DOCSTRING(textscan) +XML data can be read, the DOM node tree can be traversed and changed, and +the resulting tree can be exported. + +@DOCSTRING(xmlread) + +@DOCSTRING(xmlwrite) + +@c FIXME: How to insert documentation of classdef methods? +@DOCSTRING(dom_document) + +@c FIXME: How to insert documentation of classdef methods? +@DOCSTRING(dom_node) + The @code{importdata} function has the ability to work with a wide variety of data. diff -r e8fa10f8920c -r 64c5e6489464 libinterp/corefcn/module.mk --- a/libinterp/corefcn/module.mk Wed Nov 04 17:35:02 2020 +0100 +++ b/libinterp/corefcn/module.mk Sun Oct 04 22:35:24 2020 +0200 @@ -271,6 +271,7 @@ %reldir%/utils.cc \ %reldir%/variables.cc \ %reldir%/xdiv.cc \ + %reldir%/xmlwrite.cc \ %reldir%/xnorm.cc \ %reldir%/xpow.cc \ %reldir%/zfstream.cc \ diff -r e8fa10f8920c -r 64c5e6489464 libinterp/corefcn/xmlwrite.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libinterp/corefcn/xmlwrite.cc Sun Oct 04 22:35:24 2020 +0200 @@ -0,0 +1,1262 @@ +//////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2020 The Octave Project Developers +// +// See the file COPYRIGHT.md in the top-level directory of this +// distribution or . +// +// This file is part of Octave. +// +// Octave is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Octave is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Octave; see the file COPYING. If not, see +// . +// +//////////////////////////////////////////////////////////////////////// + + +//! @file ov-xml.cc +//! +//! Provides Octave's XML interface using RapidXML. + +#if defined (HAVE_CONFIG_H) +# include "config.h" +#endif + +#include +#include +#include +#include + +#include + +#include "defun.h" +#include "error.h" +#include "errwarn.h" +#include "lo-sysdep.h" + +#if defined (HAVE_RAPIDXML_RAPIDXML_HPP) +# include +#endif + +#if defined (HAVE_RAPIDXML_RAPIDXML_PRINT_HPP) +# include +#endif + +#if defined (HAVE_RAPIDXML) + +namespace octave +{ + class type_info; +} + +class OCTINTERP_API octave_xml_node : public octave_base_value +{ +public: + + octave_xml_node (void); + + octave_xml_node (const octave_xml_node& xml_obj) + : octave_base_value (xml_obj) + { } + + octave_xml_node (rapidxml::xml_node* node) + : m_xml_node (node) + { } + + ~octave_xml_node (void) { } + + octave_base_value * clone (void) const { return new octave_xml_node (*this); } + octave_base_value * empty_clone (void) const { return new octave_xml_node (); } + + bool is_defined (void) const { return true; } + + bool is_constant (void) const { return true; } + + bool isstruct (void) const { return false; } + + bool is_xml_document (void) const { return m_is_document; } + + dim_vector dims (void) const; + + void print (std::ostream& os, bool pr_as_read_syntax = false); + + void print_raw (std::ostream& os, bool pr_as_read_syntax = false) const; + + bool save_ascii (std::ostream& os); + + bool load_ascii (std::istream& is); + + bool save_binary (std::ostream& os, bool save_as_floats); + + bool load_binary (std::istream& is, bool swap, + octave::mach_info::float_format fmt); + + bool save_hdf5 (octave_hdf5_id loc_id, const char *name, + bool save_as_floats); + + bool load_hdf5 (octave_hdf5_id loc_id, const char *name); + + octave_value getChildNodes (void) const; + + +private: + + std::string m_filename; + char *m_buffer; + rapidxml::xml_node *m_xml_node; + +public: + + bool m_is_document = false; + + int type_id (void) const { return t_id; } + std::string type_name (void) const { return t_name; } + std::string class_name (void) const { return "XML_node"; } + + rapidxml::xml_node *node (void) const + { return m_xml_node; } + + octave_value get_owner_document (void); + + static int static_type_id (void) { return t_id; } + static std::string static_type_name (void) { return t_name; } + static std::string static_class_name (void) { return ""; } + static void register_type (octave::type_info&); + +private: + + static int t_id; + static const std::string t_name; +}; + + +//! Constructor. + +octave_xml_node::octave_xml_node (void) + : octave_base_value () +{ } + +int octave_xml_node::t_id (-1); + +const std::string octave_xml_node::t_name ("octave_xml_node"); + +void +octave_xml_node::register_type (octave::type_info& ti) +{ + t_id = ti.register_type (octave_xml_node::t_name, "", + octave_value (new octave_xml_node ())); +} + +dim_vector +octave_xml_node::dims (void) const +{ + return dim_vector (1, 1); +} + +void +octave_xml_node::print (std::ostream& os, bool) +{ + print_raw (os); + newline (os); +} + +void +octave_xml_node::print_raw (std::ostream& os, bool) const +{ + os << "'; +} + +// FIXME: Need routines to actually save/load xml objects through Serialize. + +bool +octave_xml_node::save_ascii (std::ostream& /* os */) +{ + warning ("save: unable to save XML objects, skipping"); + + return true; +} + +bool +octave_xml_node::load_ascii (std::istream& /* is */) +{ + // Silently skip over XML object that was not saved + return true; +} + +bool +octave_xml_node::save_binary (std::ostream& /* os */, bool /* save_as_floats */) +{ + warning ("save: unable to save XML objects, skipping"); + + return true; +} + +bool +octave_xml_node::load_binary (std::istream& /* is */, bool /* swap*/, + octave::mach_info::float_format /* fmt */) +{ + // Silently skip over XML object that was not saved + return true; +} + +bool +octave_xml_node::save_hdf5 (octave_hdf5_id /* loc_id */, const char * /* name */, + bool /* save_as_floats */) +{ + warning ("save: unable to save XML objects, skipping"); + + return true; +} + +bool +octave_xml_node::load_hdf5 (octave_hdf5_id /* loc_id */, const char * /* name */) +{ + // Silently skip object that was not saved + return true; +} + + +static octave_xml_node * +get_xml_node (const octave_value& ov) +{ + const octave_base_value& rep = ov.get_rep (); + + octave_base_value *ncrep = const_cast (&rep); + + octave_xml_node *xml = dynamic_cast (ncrep); + if (! xml) + error ("get_xml_node: dynamic_cast to octave_xml_node failed"); + + return xml; +} + + +class OCTINTERP_API octave_xml_doc : public octave_xml_node +{ +public: + + octave_xml_doc (void); + + octave_xml_doc (const std::string& xml_file); + + octave_xml_doc (const octave_xml_doc& xml_obj) + : octave_xml_node (xml_obj) + { + m_is_document = true; + } + + ~octave_xml_doc (void) { release (); } + + octave_base_value * clone (void) const { return new octave_xml_doc (*this); } + octave_base_value * empty_clone (void) const { return new octave_xml_doc (); } + + bool is_defined (void) const { return true; } + + bool is_constant (void) const { return true; } + + bool isstruct (void) const { return false; } + +private: + + void read_xml (const std::string& xml_file); + + void release (void); + + std::string m_filename; + char *m_buffer = nullptr; + rapidxml::xml_document m_xml_doc; + +public: + + int type_id (void) const { return t_id; } + std::string type_name (void) const { return t_name; } + std::string class_name (void) const { return "XML_document"; } + + const rapidxml::xml_document * document (void) const + { return &m_xml_doc; } + + static int static_type_id (void) { return t_id; } + static std::string static_type_name (void) { return t_name; } + static std::string static_class_name (void) { return ""; } + static void register_type (octave::type_info&); + +private: + + static int t_id; + static const std::string t_name; + +}; + + +int octave_xml_doc::t_id (-1); + +const std::string octave_xml_doc::t_name ("octave_xml_doc"); + +void +octave_xml_doc::register_type (octave::type_info& ti) +{ + t_id = ti.register_type (octave_xml_doc::t_name, "", + octave_value (new octave_xml_doc ())); +} + + +//! Constructor. + +octave_xml_doc::octave_xml_doc (void) + : octave_xml_node () +{ + m_is_document = true; +} + +octave_xml_doc::octave_xml_doc (const std::string& xml_file) +{ + read_xml (xml_file); +} + + +static octave_xml_doc * +get_xml_doc (const octave_value& ov) +{ + const octave_base_value& rep = ov.get_rep (); + + octave_base_value *ncrep = const_cast (&rep); + + octave_xml_doc *xml = dynamic_cast (ncrep); + if (! xml) + error ("get_xml_doc: dynamic_cast to octave_xml_doc failed"); + + return xml; +} + + +//! Read and parse XML file. + +void +octave_xml_doc::read_xml (const std::string& xml_filename) +{ + std::ifstream file + = octave::sys::ifstream (xml_filename.c_str (), + std::ios::in | std::ios::binary); + + if (! file || file.fail ()) + error ("xmlread: failed to open XML file: %s", xml_filename.c_str ()); + + file.seekg (0, std::ios::end); + size_t size = file.tellg (); + + m_buffer = reinterpret_cast (std::malloc (size + 1)); + file.seekg (0); + + file.read (m_buffer, size); + m_buffer[size] = '\0'; + + //rapidxml::xml_document xml_doc; + m_xml_doc.parse<0> (m_buffer); + + m_is_document = true; +} + + +void +octave_xml_doc::release (void) +{ + m_xml_doc.clear (); + + // buffer used to hold XML file content + if (m_buffer) + std::free (m_buffer); + m_buffer = nullptr; +} + + +octave_value +octave_xml_node::get_owner_document (void) +{ + if (is_xml_document ()) + { + octave_xml_doc *oct_doc = dynamic_cast (this); + rapidxml::xml_document *doc = oct_doc->document ()->document (); + return octave_value (new octave_xml_node (doc)); + } + else + { + if (! node ()) + return octave_value (Matrix ()); + rapidxml::xml_document *doc = this->node ()->document (); + if (doc) + return octave_value (new octave_xml_node (doc)); + } + + return octave_value (Matrix ()); +} + +#endif + + +// DEFUN blocks below must be outside of HAVE_RAPIDXML block so that +// documentation strings are always available, even when functions are not. + + +DEFUN (__xml_new_document__, args, , + doc: /* -*- texinfo -*- +@deftypefn {} {@var{doc} =} __xml_new_document__ () +Create a XML document node that can own a DOM node tree. + +@end deftypefn */) +{ +#if defined (HAVE_RAPIDXML) + + if (args.length () != 0) + print_usage (); + + return octave_value (new octave_xml_doc ()); + +#else + + octave_unused_parameter (args); + + err_disabled_feature ("xmlread", "RapidXML"); + +#endif +} + + +DEFUN (__xml_new_node__, args, , + doc: /* -*- texinfo -*- +@deftypefn {} {@var{node} =} __xml_new_node__ (@var{doc}, @var{type}, @var{name}) +Create a new XML DOM @var{node}. + +@end deftypefn */) +{ +#if defined (HAVE_RAPIDXML) + + if (args.length () < 2 || args.length () > 3) + print_usage (); + + octave_xml_doc *xml = get_xml_doc (args(0)); + const rapidxml::xml_document *doc = xml->document (); + + std::string type = args(1).xstring_value ("TYPE must be a string"); + + rapidxml::xml_node *node; + if (type.compare ("element") == 0) + { + if (args.length () < 3) + error ("createElement: NAME must be specified"); + + std::string name = args(2).xstring_value ("NAME must be a string"); + + char *node_name + = doc->document ()->allocate_string (name.c_str (), name.length ()); + node = doc->document ()->allocate_node (rapidxml::node_element, + node_name, nullptr, + name.length (), 0); + } + else if (type.compare ("text") == 0) + { + if (args.length () < 3) + error ("createTextNode: VALUE must be specified"); + + std::string value = args(2).xstring_value ("NAME must be a string"); + + char *node_value + = doc->document ()->allocate_string (value.c_str (), value.length ()); + node = doc->document ()->allocate_node (rapidxml::node_data, + nullptr, node_value, + 0, value.length ()); + } + else + error ("dom_document: Unknown TYPE '%s'", type.c_str ()); + + return octave_value (new octave_xml_node (node)); + +#else + + octave_unused_parameter (args); + + err_disabled_feature ("xmlread", "RapidXML"); + +#endif +} + + +DEFUN (__xml_open__, args, , + doc: /* -*- texinfo -*- +@deftypefn {} {@var{node} =} __xml_open__ (@var{filename}) +Open XML document, parse content and return an XML DOM @var{node}. + +@end deftypefn */) +{ +#if defined (HAVE_RAPIDXML) + + if (args.length () != 1) + print_usage (); + + std::string filename + = args(0).xstring_value ("xmlread: FILENAME must be a string"); + + return octave_value (new octave_xml_doc (filename)); + +#else + + octave_unused_parameter (args); + + err_disabled_feature ("xmlread", "RapidXML"); + +#endif +} + + +DEFUN (__xml_print_str__, args, , + doc: /* -*- texinfo -*- +@deftypefn {} {@var{str} =} __xml_print_str__ (@var{node}) +Serialize XML DOM @var{node} to string @var{str}. + +@end deftypefn */) +{ +#if defined (HAVE_RAPIDXML) + + if (args.length () != 1) + print_usage (); + + octave_xml_doc *xml = get_xml_doc (args(0)); + const rapidxml::xml_document *doc = xml->document (); + + std::string str; + rapidxml::print (std::back_inserter (str), *doc, 1); + + return octave_value (str); + +#else + + octave_unused_parameter (args); + + err_disabled_feature ("xmlread", "RapidXML"); + +#endif +} + + +DEFUN (__xml_write_file__, args, , + doc: /* -*- texinfo -*- +@deftypefn {} {} __xml_print_str__ (@var{filename}, @var{node}) +Serialize XML DOM @var{node} to string @var{str}. + +@end deftypefn */) +{ +#if defined (HAVE_RAPIDXML) + + if (args.length () != 2) + print_usage (); + + std::string filename + = args(0).xstring_value ("xmlread: FILENAME must be a string"); + octave_xml_doc *xml = get_xml_doc (args(1)); + + std::ofstream ofile + = octave::sys::ofstream (filename.c_str (), + std::ios::out | std::ios::binary); + + if (! ofile.is_open ()) + error ("xmlread: unable to open file '%s'", filename.c_str ()); + + octave::unwind_action close_file + ([] (std::ofstream *ofile_ptr) { ofile_ptr->close (); }, &ofile); + + const rapidxml::xml_document *doc = xml->document (); + + rapidxml::print (std::ostream_iterator (ofile), *doc, 1); +// ofile << *doc; + + return octave_value (Matrix ()); + +#else + + octave_unused_parameter (args); + + err_disabled_feature ("xmlread", "RapidXML"); + +#endif +} + + +DEFUN(__xml_get_name__, args, , + doc: /* -*- texinfo -*- +@deftypefn {} {@var{name} =} __xml_get_name__ (node) +Open XML document, parse content and return an XML DOM @var{node}. + +@end deftypefn */) +{ +#if defined (HAVE_RAPIDXML) + + if (args.length () != 1) + print_usage (); + + octave_xml_node *xml = get_xml_node (args(0)); + + std::string name; + if (xml->is_xml_document ()) + name = "#document"; + else + name = std::string (xml->node ()->name (), xml->node ()->name_size ()); + + return octave_value (name); + +#else + + octave_unused_parameter (args); + + err_disabled_feature ("xmlread", "RapidXML"); + +#endif +} + + +DEFUN (__xml_value__, args, , + doc: /* -*- texinfo -*- +@deftypefn {} {@var{value} =} __xml_value__ (node) +@deftypefnx {} {} __xml_value__ (node, value) +Get or set @var{value} of XML DOM @var{node}. + +@end deftypefn */) +{ +#if defined (HAVE_RAPIDXML) + + if (args.length () < 1 || args.length () > 2) + print_usage (); + + octave_xml_node *node = get_xml_node (args(0)); + + std::string value; + + if (node->is_xml_document ()) + return octave_value (Matrix ()); + + if (args.length () == 1) + { + value = std::string (node->node ()->value (), + node->node ()->value_size ()); + + return octave_value (value); + } + else + { + value = args(1).string_value (); + rapidxml::xml_document *doc = node->node ()->document (); + + if (! doc) + error ("setValue: NODE must have a parent for setting value"); + + char *val = doc->allocate_string (value.c_str (), value.length ()); + + node->node ()->value (val, value.length ()); + + return octave_value (); + } +#else + + octave_unused_parameter (args); + + err_disabled_feature ("xmlread", "RapidXML"); + +#endif +} + + +DEFUN (__xml_get_type__, args, , + doc: /* -*- texinfo -*- +@deftypefn {} {@var{type} =} __xml_get_type__ (node) +Get node @var{type} of XML DOM @var{node}. + +@end deftypefn */) +{ +#if defined (HAVE_RAPIDXML) + + if (args.length () < 1) + print_usage (); + + octave_xml_node *node = get_xml_node (args(0)); + + rapidxml::node_type type; + if (node->is_xml_document ()) + { + octave_xml_doc *doc = dynamic_cast (node); + type = doc->document ()->type (); + } + else + type = node->node ()->type (); + + return octave_value (type); + +#else + + octave_unused_parameter (args); + + err_disabled_feature ("xmlread", "RapidXML"); + +#endif +} + + +DEFUN (__xml_get_parent__, args, , + doc: /* -*- texinfo -*- +@deftypefn {} {@var{parent_node} =} __xml_get_parent__ (node) +Get @var{parent_node} of XML DOM @var{node}. + +@end deftypefn */) +{ +#if defined (HAVE_RAPIDXML) + + if (args.length () < 1) + print_usage (); + + octave_xml_node *node = get_xml_node (args(0)); + + if (! node->is_xml_document () && node->node ()) + { + rapidxml::xml_node *parent_node + = node->node ()->parent (); + return octave_value (new octave_xml_node (parent_node)); + } + + return octave_value (Matrix ()); + +#else + + octave_unused_parameter (args); + + err_disabled_feature ("xmlread", "RapidXML"); + +#endif +} + + +DEFUN (__xml_get_first_child__, args, , + doc: /* -*- texinfo -*- +@deftypefn {} {@var{child_node} =} __xml_get_first_child__ (@var{node}) +@deftypefnx {} {@var{child_node} =} __xml_get_first_child__ (@var{node}, @var{name}) +Get first @var{child_node} of XML @var{node}. + +@end deftypefn */) +{ +#if defined (HAVE_RAPIDXML) + + if (args.length () < 1 || args.length () > 2) + print_usage (); + + octave_xml_node *node = get_xml_node (args(0)); + + rapidxml::xml_node *child_node; + if (node->is_xml_document ()) + { + octave_xml_doc *doc = dynamic_cast (node); + if (! doc->document ()) + return octave_value (Matrix ()); + + if (args.length () == 1) + child_node = doc->document ()->first_node (); + else + { + std::string name = args(1).xstring_value ("NAME must be a string"); + child_node = doc->document ()->first_node (name.c_str ()); + } + } + else + { + if (! node->node ()) + return octave_value (Matrix ()); + + if (args.length () == 1) + child_node = node->node ()->first_node (); + else + { + std::string name = args(1).xstring_value ("NAME must be a string"); + child_node = node->node ()->first_node (name.c_str ()); + } + } + + if (child_node) + return octave_value (new octave_xml_node (child_node)); + else + return octave_value (Matrix ()); + +#else + + octave_unused_parameter (args); + + err_disabled_feature ("xmlread", "RapidXML"); + +#endif +} + + +DEFUN (__xml_get_last_child__, args, , + doc: /* -*- texinfo -*- +@deftypefn {} {@var{child_node} =} __xml_get_last_child__ (@var{node}) +@deftypefnx {} {@var{child_node} =} __xml_get_last_child__ (@var{node}, @var{name}) +Get last @var{child_node} of XML @var{node}. + +@end deftypefn */) +{ +#if defined (HAVE_RAPIDXML) + + if (args.length () < 1 || args.length () > 2) + print_usage (); + + octave_xml_node *node = get_xml_node (args(0)); + + rapidxml::xml_node *child_node; + if (node->is_xml_document ()) + { + octave_xml_doc *doc = dynamic_cast (node); + if (! doc->document ()) + return octave_value (Matrix ()); + + if (args.length () == 1) + child_node = doc->document ()->last_node (); + else + { + std::string name = args(1).xstring_value ("NAME must be a string"); + child_node = doc->document ()->last_node (name.c_str ()); + } + + } + else + { + if (! node->node ()) + return octave_value (Matrix ()); + + if (args.length () == 1) + child_node = node->node ()->last_node (); + else + { + std::string name = args(1).xstring_value ("NAME must be a string"); + child_node = node->node ()->last_node (name.c_str ()); + } + } + + if (child_node) + return octave_value (new octave_xml_node (child_node)); + else + return octave_value (Matrix ()); + +#else + + octave_unused_parameter (args); + + err_disabled_feature ("xmlread", "RapidXML"); + +#endif +} + + +DEFUN (__xml_get_previous_sibling__, args, , + doc: /* -*- texinfo -*- +@deftypefn {} {@var{sibling_node} =} __xml_get_previous_sibling__ (@var{node}) +@deftypefnx {} {@var{sibling_node} =} __xml_get_previous_sibling__ (@var{node}, @var{name}) +Get previous @var{sibling_node} of XML @var{node}. + +@end deftypefn */) +{ +#if defined (HAVE_RAPIDXML) + + if (args.length () < 1 || args.length () > 2) + print_usage (); + + octave_xml_node *node = get_xml_node (args(0)); + + rapidxml::xml_node *sibling_node = nullptr; + if (! node->is_xml_document () && node->node ()) + { + if (args.length () == 1) + sibling_node = node->node ()->previous_sibling (); + else + { + std::string name = args(1).xstring_value ("NAME must be a string"); + sibling_node = node->node ()->previous_sibling (name.c_str ()); + } + } + + if (sibling_node) + return octave_value (new octave_xml_node (sibling_node)); + else + return octave_value (Matrix ()); + +#else + + octave_unused_parameter (args); + + err_disabled_feature ("xmlread", "RapidXML"); + +#endif +} + + +DEFUN (__xml_get_next_sibling__, args, , + doc: /* -*- texinfo -*- +@deftypefn {} {@var{sibling_node} =} __xml_get_next_sibling__ (@var{node}) +@deftypefnx {} {@var{sibling_node} =} __xml_get_next_sibling__ (@var{node}, @var{name}) +Get next @var{sibling_node} of XML @var{node}. + +@end deftypefn */) +{ +#if defined (HAVE_RAPIDXML) + + if (args.length () < 1 || args.length () > 2) + print_usage (); + + octave_xml_node *node = get_xml_node (args(0)); + + rapidxml::xml_node *sibling_node = nullptr; + if (! node->is_xml_document () && node->node ()) + { + if (args.length () == 1) + sibling_node = node->node ()->next_sibling (); + else + { + std::string name = args(1).xstring_value ("NAME must be a string"); + sibling_node = node->node ()->next_sibling (name.c_str ()); + } + } + + if (sibling_node) + return octave_value (new octave_xml_node (sibling_node)); + else + return octave_value (Matrix ()); + +#else + + octave_unused_parameter (args); + + err_disabled_feature ("xmlread", "RapidXML"); + +#endif +} + +DEFUN (__xml_attribute_names__, args, , + doc: /* -*- texinfo -*- +@deftypefn {} {@var{attr} =} __xml_attribute_names__ (@var{node}) +Get names of attributes @var{attr} of XML @var{node}. + +@end deftypefn */) +{ +#if defined (HAVE_RAPIDXML) + + if (args.length () != 1) + print_usage (); + + octave_xml_node *node = get_xml_node (args(0)); + + rapidxml::xml_attribute *attr; + if (node->is_xml_document ()) + { + octave_xml_doc *doc = dynamic_cast (node); + if (! doc->document ()) + return octave_value (Matrix ()); + + attr = doc->document ()->first_attribute (); + } + else + attr = node->node ()->first_attribute (); + + std::list attributes; + for (; attr; attr = attr->next_attribute ()) + attributes.push_back (attr->name ()); + + return octave_value (Cell (string_vector (attributes))); + +#else + + octave_unused_parameter (args); + + err_disabled_feature ("xmlread", "RapidXML"); + +#endif +} + + +DEFUN (__xml_attribute__, args, , + doc: /* -*- texinfo -*- +@deftypefn {} {@var{val} =} __xml_attribute__ (@var{node}, @var{attr}) +@deftypefnx {} {} __xml_attribute__ (@var{node}, @var{attr}, @var{val}) +Get value @var{val} of attribute @var{attr} of XML @var{node}. + +@end deftypefn */) +{ +#if defined (HAVE_RAPIDXML) + + if (args.length () < 2 || args.length () > 3) + print_usage (); + + octave_xml_node *node = get_xml_node (args(0)); + std::string attr = args(1).string_value (); + + if (! node->is_xml_document () && node->node ()) + { + rapidxml::xml_attribute *attribute + = node->node ()->first_attribute (attr.c_str ()); + if (args.length () == 2) + { + if (attribute) + { + std::string val (attribute->value (), attribute->value_size ()); + return octave_value (val); + } + } + else + { + rapidxml::xml_document *doc = node->node ()->document (); + if (! doc) + error ("setValue: NODE must have a parent for setting attribute"); + + std::string val = args(2).string_value (); + + if (attribute) + { + char *attr_val + = doc->allocate_string (val.c_str (), val.length ()); + + attribute->value (attr_val, val.length ()); + } + else + { + char *attr_name + = doc->allocate_string (attr.c_str (), attr.length ()); + char *attr_val + = doc->allocate_string (val.c_str (), val.length ()); + + rapidxml::xml_attribute *new_attr + = doc->allocate_attribute + (attr_name, attr_val, attr.length (), val.length ()); + + node->node ()->append_attribute (new_attr); + } + } + } + + return octave_value (Matrix ()); + +#else + + octave_unused_parameter (args); + + err_disabled_feature ("xmlread", "RapidXML"); + +#endif +} + + +DEFUN (__xml_remove_attribute__, args, , + doc: /* -*- texinfo -*- +@deftypefn {} {} __xml_remove_attribute__ (@var{node}, @var{attr}) +Remove attribute @var{attr} of XML @var{node}. + +@end deftypefn */) +{ +#if defined (HAVE_RAPIDXML) + + if (args.length () < 2 || args.length () > 3) + print_usage (); + + octave_xml_node *node = get_xml_node (args(0)); + std::string attr = args(1).string_value (); + + if (! node->is_xml_document () && node->node ()) + { + rapidxml::xml_attribute *attribute + = node->node ()->first_attribute (attr.c_str ()); + if (attribute) + node->node ()->remove_attribute (attribute); + } + + return octave_value (Matrix ()); + +#else + + octave_unused_parameter (args); + + err_disabled_feature ("xmlread", "RapidXML"); + +#endif +} + + +DEFUN (__xml_get_owner_document__, args, , + doc: /* -*- texinfo -*- +@deftypefn {} {@var{owner_doc} =} __xml_get_owner_document__ (node) +Get document @var{owner_doc} of which XML @var{node} is a child. + +@end deftypefn */) +{ +#if defined (HAVE_RAPIDXML) + + if (args.length () < 1) + print_usage (); + + octave_xml_node *oct_node = get_xml_node (args(0)); + + return oct_node->get_owner_document (); + +#else + + octave_unused_parameter (args); + + err_disabled_feature ("xmlread", "RapidXML"); + +#endif +} + + +DEFUN (__xml_insert_before__, args, , + doc: /* -*- texinfo -*- +@deftypefn {} {} __xml_insert_before__ (node, new_child, ref_child) +Insert @var{new_child} in XML @var{node} before @var{ref_child}. + +@end deftypefn */) +{ +#if defined (HAVE_RAPIDXML) + + if (args.length () != 3) + print_usage (); + + octave_xml_node *node = get_xml_node (args(0)); + octave_xml_node *new_child = get_xml_node (args(1)); + octave_xml_node *ref_child = get_xml_node (args(2)); + + if (node->is_xml_document ()) + { + octave_xml_doc *doc = dynamic_cast (node); + if (! doc->document ()) + return octave_value (Matrix ()); + + doc->document ()->document ()->insert_node (ref_child->node (), + new_child->node ()); + } + else + node->node ()->insert_node (ref_child->node (), new_child->node ()); + + return octave_value (); + +#else + + octave_unused_parameter (args); + + err_disabled_feature ("xmlread", "RapidXML"); + +#endif +} + + +DEFUN (__xml_remove_child__, args, , + doc: /* -*- texinfo -*- +@deftypefn {} {} __xml_remove_child__ (node, old_child) +Remove @var{old_child} from XML @var{node}. + +@end deftypefn */) +{ +#if defined (HAVE_RAPIDXML) + + if (args.length () != 2) + print_usage (); + + octave_xml_node *node = get_xml_node (args(0)); + octave_xml_node *old_child = get_xml_node (args(1)); + + if (node->is_xml_document ()) + { + octave_xml_doc *doc = dynamic_cast (node); + if (! doc->document ()) + return octave_value (Matrix ()); + + doc->document ()->document ()->remove_node (old_child->node ()); + } + else + node->node ()->remove_node (old_child->node ()); + + return octave_value (); + +#else + + octave_unused_parameter (args); + + err_disabled_feature ("xmlread", "RapidXML"); + +#endif +} + + +DEFUN (__xml_append_child__, args, , + doc: /* -*- texinfo -*- +@deftypefn {} {} __xml_append_child__ (node, new_child) +Append @var{new_child} to XML @var{node}. + +@end deftypefn */) +{ +#if defined (HAVE_RAPIDXML) + + if (args.length () != 2) + print_usage (); + + octave_xml_node *node = get_xml_node (args(0)); + octave_xml_node *new_child = get_xml_node (args(1)); + + if (node->is_xml_document ()) + { + octave_xml_doc *doc = dynamic_cast (node); + if (! doc->document ()) + return octave_value (Matrix ()); + + doc->document ()->document ()->append_node (new_child->node ()); + } + else + node->node ()->append_node (new_child->node ()); + + return octave_value (); + +#else + + octave_unused_parameter (args); + + err_disabled_feature ("xmlread", "RapidXML"); + +#endif +} + + +DEFUN (__xml_clone_node__, args, , + doc: /* -*- texinfo -*- +@deftypefn {} {@var{clone} = } __xml_clone_node__ (node) +Return @var{clone} of XML @var{node}. + +@end deftypefn */) +{ +#if defined (HAVE_RAPIDXML) + + if (args.length () != 1) + print_usage (); + + octave_xml_node *node = get_xml_node (args(0)); + + rapidxml::xml_node *clone + = node->node ()->document ()->clone_node (node->node ()); + + return octave_value (new octave_xml_node (clone)); + +#else + + octave_unused_parameter (args); + + err_disabled_feature ("xmlread", "RapidXML"); + +#endif +} diff -r e8fa10f8920c -r 64c5e6489464 scripts/help/__unimplemented__.m --- a/scripts/help/__unimplemented__.m Wed Nov 04 17:35:02 2020 +0100 +++ b/scripts/help/__unimplemented__.m Sun Oct 04 22:35:24 2020 +0200 @@ -1316,8 +1316,6 @@ "writeKeyUnit", "writetable", "writeVideo", - "xmlread", - "xmlwrite", "xslt", "xtickangle", "xtickformat", diff -r e8fa10f8920c -r 64c5e6489464 scripts/io/dom_attribute.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/io/dom_attribute.m Sun Oct 04 22:35:24 2020 +0200 @@ -0,0 +1,68 @@ +######################################################################## +## +## Copyright (C) 2020 The Octave Project Developers +## +## See the file COPYRIGHT.md in the top-level directory of this +## distribution or . +## +## This file is part of Octave. +## +## Octave is free software: you can redistribute it and/or modify it +## under the terms of the GNU General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## Octave is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with Octave; see the file COPYING. If not, see +## . +## +######################################################################## + +classdef dom_attribute < handle + + ## -*- texinfo -*- + ## @deftypefn {} {@var{doc} =} dom_attribute (@var{xml_doc}) + ## Class for XML DOM handles. + ## + ## The class closely mimicks the DOM attribute interface used by Java for + ## hosting the owner document of the XML tree. See also: + ## @url{https://docs.oracle.com/javase/6/docs/api/org/w3c/dom/Document.html} + ## + ## @seealso{dom_node} + ## @end deftypefn + + properties (SetAccess = private, GetAccess = private, Hidden) + owner + name + value + endproperties + + + methods + + function this = dom_attribute (own, nam, val) + this.owner = own; + this.name = nam; + this.value = val; + endfunction + + function name_out = getName (this) + name_out = this.name; + endfunction + + function val = getValue (this) + val = this.value; + endfunction + + function setValue (this, val) + this.value = val; + end + + endmethods + +endclassdef diff -r e8fa10f8920c -r 64c5e6489464 scripts/io/dom_document.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/io/dom_document.m Sun Oct 04 22:35:24 2020 +0200 @@ -0,0 +1,75 @@ +######################################################################## +## +## Copyright (C) 2020 The Octave Project Developers +## +## See the file COPYRIGHT.md in the top-level directory of this +## distribution or . +## +## This file is part of Octave. +## +## Octave is free software: you can redistribute it and/or modify it +## under the terms of the GNU General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## Octave is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with Octave; see the file COPYING. If not, see +## . +## +######################################################################## + +classdef dom_document < dom_node + + ## -*- texinfo -*- + ## @deftypefn {} {@var{doc} =} dom_document (@var{xml_doc}) + ## Class for the objects returned by @code{xmlread}. + ## + ## The class closely mimicks the DOM document interface used by Java for + ## hosting the owner document of the XML tree. See also: + ## @url{https://docs.oracle.com/javase/6/docs/api/org/w3c/dom/Document.html} + ## + ## @seealso{xmlread, xmlwrite, dom_node} + ## @end deftypefn + + + methods (Hidden) + + function node = get_dom_node (this) + node = this.m_dom_node; + endfunction + + endmethods + + + methods + + function this = dom_document (doc) + + if (nargin == 1) + this.m_dom_node = doc; + else + this.m_dom_node = __xml_new_document__ (); + end + + end + + + function new_node = createElement (this, name) + new_node = dom_node (__xml_new_node__ (this.m_dom_node, + "element", name)); + endfunction + + + function new_node = createTextNode (this, value) + new_node = dom_node (__xml_new_node__ (this.m_dom_node, + "element", value)); + endfunction + + endmethods + +endclassdef \ No newline at end of file diff -r e8fa10f8920c -r 64c5e6489464 scripts/io/dom_node.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/io/dom_node.m Sun Oct 04 22:35:24 2020 +0200 @@ -0,0 +1,561 @@ +######################################################################## +## +## Copyright (C) 2020 The Octave Project Developers +## +## See the file COPYRIGHT.md in the top-level directory of this +## distribution or . +## +## This file is part of Octave. +## +## Octave is free software: you can redistribute it and/or modify it +## under the terms of the GNU General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## Octave is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with Octave; see the file COPYING. If not, see +## . +## +######################################################################## + +classdef dom_node < handle + + ## -*- texinfo -*- + ## @deftypefn {} {@var{node} =} dom_node (@var{xml_node}) + ## Class for the node objects in the XML DOM tree. + ## + ## The class closely mimicks the DOM node interface used by Java for + ## traversing XML trees. See also: + ## @url{https://docs.oracle.com/javase/6/docs/api/org/w3c/dom/Node.html} + ## + ## @seealso{xmlread, xmlwrite, dom_document} + ## @end deftypefn + + properties (GetAccess = public, SetAccess = private, Hidden) + ## FIXME: These should be enumeration values + ELEMENT_NODE = 1 + ATTRIBUTE_NODE = 2 + TEXT_NODE = 3 + CDATA_SECTION_NODE = 4 + ENTITY_REFERENCE_NODE = 5 + ENTITY_NODE = 6 + PROCESSING_INSTRUCTION_NODE = 7 + COMMENT_NODE = 8 + DOCUMENT_NODE = 9 + DOCUMENT_TYPE_NODE = 10 + DOCUMENT_FRAGMENT_NODE = 11 + NOTATION_NODE = 12 + + DOCUMENT_POSITION_DISCONNECTED = 1 + DOCUMENT_POSITION_PRECEDING = 2 + DOCUMENT_POSITION_FOLLOWING = 4 + DOCUMENT_POSITION_CONTAINS = 8 + DOCUMENT_POSITION_CONTAINED_BY = 16 + DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = 32 + endproperties + + + ## FIXME: The `SetAccess` should be `immutable` when that is implemented. + properties (GetAccess = protected, SetAccess = protected) + m_dom_node + endproperties + + + methods (Hidden) + + function node = get_dom_node (this) + node = this.m_dom_node; + endfunction + + endmethods + + + methods + + function this = dom_node (node) + + if (nargin == 0) + ## Called as super-class constructor + return; + endif + + if (nargin > 1) + print_usage (); + endif + + if (! any (strcmp (class (node), {"XML_node", "XML_document"}))) + error ("dom_node: NODE must be of class `XML_node` or `XML_document`"); + endif + + this.m_dom_node = node; + endfunction + + + function node_name = getNodeName (this) + + ## -*- texinfo -*- + ## @deftypefn {} {@var{node_name} =} @var{node}.getNodeName () + ## Get the name of @var{node}. + ## @end deftypefn + + node_name = __xml_get_name__ (this.m_dom_node); + + endfunction + + + function node_value = getNodeValue (this) + + ## -*- texinfo -*- + ## @deftypefn {} {@var{node_value} =} @var{node}.getNodeValue () + ## Get the value of @var{node}. + ## @end deftypefn + + node_value = __xml_value__ (this.m_dom_node); + + endfunction + + + function setNodeValue (this, node_value) + + ## -*- texinfo -*- + ## @deftypefn {} {} @var{node}.getNodeValue (@var{node_value}) + ## Set the value of @var{node}. + ## @end deftypefn + + if (nargin !=2) + print_usage (); + endif + if (! ischar (node_value)) + error ("setNodeValue: NODE_VALUE must be a string"); + endif + __xml_value__ (this.m_dom_node, node_value); + + endfunction + + + function node_type = getNodeType (this) + + ## -*- texinfo -*- + ## @deftypefn {} {@var{node_type} =} @var{node}.getNodeType () + ## Get the type of @var{node}. + ## @end deftypefn + + node_type = dom_node (__xml_get_type__ (this.m_dom_node)); + + endfunction + + + function parent_node = getParentNode (this) + + ## -*- texinfo -*- + ## @deftypefn {} {@var{parent_node} =} @var{node}.getParentNode () + ## Get the parent of @var{node}. + ## @end deftypefn + + parent_node = __xml_get_parent__ (this.m_dom_node); + if (isempty (parent_node)) + return; + endif + parent_node = dom_node (parent_node); + + endfunction + + + function nodes = getChildNodes (this) + + ## -*- texinfo -*- + ## @deftypefn {} {@var{nodes} =} @var{node}.getChildNodes () + ## Get array of all children @var{nodes}. + ## @end deftypefn + + ## FIXME: How to create an empty array of classdef objects? + node = __xml_get_first_child__ (this.m_dom_node); + if (isempty (node)) + nodes = []; + return; + endif + nodes = dom_node (node); + while (! isempty (node)) + node = __xml_get_next_sibling__ (node); + if (isempty (node)) + break; + endif + nodes(end+1) = dom_node (node); + endwhile + + endfunction + + + function node = getFirstChild (this, varargin) + + ## -*- texinfo -*- + ## @deftypefn {} {@var{child_node} =} @var{node}.getFirstChild () + ## @deftypefnx {} {@var{child_node} =} @var{node}.getFirstChild (@var{name}) + ## Get the first child of @var{node}. + ## + ## Optionally, get first child of @var{node} with name @var{name}. + ## + ## @end deftypefn + + if (numel (varargin) > 1) + print_usage (); + endif + + node = __xml_get_first_child__ (this.m_dom_node, varargin{:}); + if (isempty (node)) + return; + endif + node = dom_node (node); + + endfunction + + + function node = getLastChild (this, varargin) + + ## -*- texinfo -*- + ## @deftypefn {} {@var{child_node} =} @var{node}.getLastChild () + ## @deftypefnx {} {@var{child_node} =} @var{node}.getLastChild (@var{name}) + ## Get the last child of @var{node}. + ## + ## Optionally, get last child of @var{node} with name @var{name}. + ## + ## @end deftypefn + + if (numel (varargin) > 1) + print_usage (); + endif + + node = __xml_get_last_child__ (this.m_dom_node, varargin{:}); + if (isempty (node)) + return; + endif + node = dom_node (node); + + endfunction + + + function node = getPreviousSibling (this, varargin) + + ## -*- texinfo -*- + ## @deftypefn {} {@var{sibling_node} =} @var{node}.getPreviousSibling () + ## @deftypefnx {} {@var{sibling_node} =} @var{node}.getPreviousSibling (@var{name}) + ## Get the previous sibling of @var{node}. + ## + ## Optionally, get previous sibling of @var{node} with name @var{name}. + ## + ## @end deftypefn + + if (numel (varargin) > 1) + print_usage (); + endif + + node = __xml_get_previous_sibling__ (this.m_dom_node, varargin{:}); + if (isempty (node)) + return; + endif + node = dom_node (node); + + endfunction + + + function node = getNextSibling (this, varargin) + + ## -*- texinfo -*- + ## @deftypefn {} {@var{sibling_node} =} @var{node}.getNextSibling () + ## @deftypefnx {} {@var{sibling_node} =} @var{node}.getNextSibling (@var{name}) + ## Get the next sibling of @var{node}. + ## + ## Optionally, get next sibling of @var{node} with name @var{name}. + ## + ## @end deftypefn + + if (numel (varargin) > 1) + print_usage (); + endif + + node = __xml_get_next_sibling__ (this.m_dom_node, varargin{:}); + if (isempty (node)) + return; + endif + node = dom_node (node); + + endfunction + + + function val = getAttribute (this, attr) + + ## -*- texinfo -*- + ## @deftypefn {} {@var{val} =} @var{node}.getAttribute (@var{attr}) + ## Get attribute @var{attr} of @var{node}. + ## @end deftypefn + + if (nargin != 2) + print_usage (); + endif + + if (! ischar (attr)) + error ("getAttribute: ATTR must be a string"); + endif + + val = __xml_attribute__ (this.m_dom_node, attr); + + endfunction + + + function setAttribute (this, attr, val) + + ## -*- texinfo -*- + ## @deftypefn {} {} @var{node}.setAttribute (@var{attr}, @var{val}) + ## Set attribute @var{attr} of @var{node} to @var{val}. + ## @end deftypefn + + if (nargin != 3) + print_usage (); + endif + + if (! ischar (attr)) + error ("getAttribute: CHAR must be a string"); + endif + + if (! ischar (val)) + error ("getAttribute: VAL must be a string"); + endif + + __xml_attribute__ (this.m_dom_node, attr, val); + + endfunction + + + function removeAttribute (this, attr) + + ## -*- texinfo -*- + ## @deftypefn {} {} @var{node}.removeAttribute (@var{attr}) + ## Remove attribute @var{attr} of @var{node}. + ## @end deftypefn + + if (nargin != 2) + print_usage (); + endif + + if (! ischar (attr)) + error ("getAttribute: CHAR must be a string"); + endif + + __xml_remove_attribute__ (this.m_dom_node, attr); + + endfunction + + + function attributes = getAttributes (this) + + ## -*- texinfo -*- + ## @deftypefn {} {@var{attributes} =}} @var{node}.getAttributes () + ## Get @var{attributes} of @var{node}. + ## @end deftypefn + + if (nargin != 1) + print_usage (); + endif + + attr = __xml_attribute_names__ (this.m_dom_node); + if (isempty (attr)) + attributes = []; + return; + endif + attributes = dom_attribute (this.m_dom_node, attr{1}, ... + this.getAttribute (attr{1})); + for i_attr = 2:numel (attr) + attributes(i_attr) = dom_attribute (this.m_dom_node, attr{i_attr}, ... + this.getAttribute (attr{i_attr})); + end + + endfunction + + + function doc = getOwnerDocument (this) + + ## -*- texinfo -*- + ## @deftypefn {} {@var{doc} = } @var{node}.getOwnerDocument () + ## Get node of the document that own this @var{node}. + ## @end deftypefn + + doc = __xml_get_owner_document__ (this.m_dom_node); + if (isempty (doc)) + return; + endif + + doc = dom_node (doc); + + endfunction + + + function new_node = insertBefore (this, new_child, ref_child) + + ## -*- texinfo -*- + ## @deftypefn {} {@var{new_node} = } @var{node}.insertBefore (@var{new_child}, @var{ref_child}) + ## Insert node @var{new_child} as a child of @var{node} behind existing child @var{ref_child}. + ## @end deftypefn + + __xml_insert_before__ (this.m_dom_node, new_child.m_dom_node, + ref_child.m_dom_node); + new_node = new_child; + + endfunction + + + function node = replaceChild (this, new_child, old_child) + error("replaceChild: Not yet implemented"); + endfunction + + + function old_node = removeChild (this, old_child) + + ## -*- texinfo -*- + ## @deftypefn {} {@var{old_node} = } @var{node}.removeChild (@var{old_child}) + ## Remove node @var{old_child} from @var{node}. + ## @end deftypefn + + __xml_remove_child__ (this.m_dom_node, old_child.m_dom_node); + old_node = old_child; + + endfunction + + + function new_node = appendChild (this, new_child) + + ## -*- texinfo -*- + ## @deftypefn {} {@var{new_node} = } @var{node}.appendChild (@var{new_child}) + ## Append @var{new_child} at the end of children of @var{node}. + ## @end deftypefn + + __xml_append_child__ (this.m_dom_node, new_child.m_dom_node); + new_node = new_child; + + endfunction + + + function has_kids = hasChildNodes (this) + + ## -*- texinfo -*- + ## @deftypefn {} {@var{has_kids} = } @var{node}.hasChildNodes () + ## Return true if node @var{node} has children. + ## @end deftypefn + + node = __xml_get_first_child__ (this.m_dom_node); + has_kids = ! isempty (node); + + endfunction + + + function new_node = cloneNode (this, deep) + + ## -*- texinfo -*- + ## @deftypefn {} {@var{new_node} = } @var{node}.hasChildNodes () + ## @deftypefnx {} {@var{new_node} = } @var{node}.hasChildNodes (@var{deep}) + ## Clone node @var{node} including all its children. + ## + ## Shallow copies are not yet implemented. (So @var{deep} must be + ## @qcode{true}.) + ## @end deftypefn + + if (nargin < 2) + deep = true; + endif + + if (! deep) + error ("cloneNode: Shallow copies aren't implemented yet"); + endif + + new_node = __xml_clone_node__ (this.m_dom_node); + if (isempty (new_node)) + return; + endif + new_node = dom_node (new_node); + + endfunction + + + function normalize (this) + error("normalize: Not yet implemented"); + endfunction + + function is_support = isSupported (this, feature, version) + error("isSupported: Not yet implemented"); + endfunction + + function uri = getNamespaceURI (this) + error("getNamespaceURI: Not yet implemented"); + endfunction + + function prefix = getPrefix (this) + error("getPrefix: Not yet implemented"); + endfunction + + function setPrefix (this, prefix) + error("setPrefix: Not yet implemented"); + endfunction + + function local_name = getLocalName (this) + error("getLocalName: Not yet implemented"); + endfunction + + function has_attributes = hasAttributes (this) + error("hasAttributes: Not yet implemented"); + endfunction + + function base_uir = getBaseURI (this) + error("getBaseURI: Not yet implemented"); + endfunction + + function comp_pos = compareDocumentPosition (this, other) + error("compareDocumentPosition: Not yet implemented"); + endfunction + + function text_content = getTextContent (this) + error("getTextContent: Not yet implemented"); + endfunction + + function setTextContent (this, text_content) + error("setTextContent: Not yet implemented"); + endfunction + + function is_same = isSameNode (this, other) + error("isSameNode: Not yet implemented"); + endfunction + + function prefix = loopupPrefix (this, namespace_uri) + error("loopupPrefix: Not yet implemented"); + endfunction + + function is_default = isDefaultNamespace (this, namespace_uri) + error("isDefaultNamespace: Not yet implemented"); + endfunction + + function namespace_uri = lookupNamespaceURI (this, prefix) + error("lookupNamespaceURI: Not yet implemented"); + endfunction + + function is_equal = isEqualNode (this, node) + error("isEqualNode: Not yet implemented"); + endfunction + + function obj = getFeature (this, feature, version) + error("getFeature: Not yet implemented"); + endfunction + + function obj = setUserData (this, key, data, handler) + error("setUserData: Not yet implemented"); + endfunction + + function obj = getUserData (this, key) + error("getUserData: Not yet implemented"); + endfunction + + endmethods + +endclassdef diff -r e8fa10f8920c -r 64c5e6489464 scripts/io/module.mk --- a/scripts/io/module.mk Wed Nov 04 17:35:02 2020 +0100 +++ b/scripts/io/module.mk Sun Oct 04 22:35:24 2020 +0200 @@ -6,9 +6,14 @@ %reldir%/csvread.m \ %reldir%/csvwrite.m \ %reldir%/dlmwrite.m \ + %reldir%/dom_attribute.m \ + %reldir%/dom_document.m \ + %reldir%/dom_node.m \ %reldir%/fileread.m \ %reldir%/importdata.m \ - %reldir%/is_valid_file_id.m + %reldir%/is_valid_file_id.m \ + %reldir%/xmlread.m \ + %reldir%/xmlwrite.m %canon_reldir%dir = $(fcnfiledir)/io diff -r e8fa10f8920c -r 64c5e6489464 scripts/io/xmlread.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/io/xmlread.m Sun Oct 04 22:35:24 2020 +0200 @@ -0,0 +1,61 @@ +######################################################################## +## +## Copyright (C) 2020 The Octave Project Developers +## +## See the file COPYRIGHT.md in the top-level directory of this +## distribution or . +## +## This file is part of Octave. +## +## Octave is free software: you can redistribute it and/or modify it +## under the terms of the GNU General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## Octave is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with Octave; see the file COPYING. If not, see +## . +## +######################################################################## + +## -*- texinfo -*- +## @deftypefn {} {@var{dom_doc} =} xmlread (@var{filename}) +## @deftypefnx {} {@var{dom_doc} =} xmlread (@var{filename}, @var{"AllowDoctype"}, @var{doc_type}) +## Read XML from file @var{filename} and return XML DOM document @var{dom_doc}. +## +## @var{doc_type} is not yet implemented. +## +## @seealso{xmlwrite, dom_document, csvread, dlmread, csvwrite, dlmwrite} +## @end deftypefn + +function x = xmlread (filename, varargin) + + x = dom_document (__xml_open__ (filename)); + +endfunction + + +%!demo +%! ## Create an svg file, which is nothing but an xml tree +%! tk = graphics_toolkit (); +%! graphics_toolkit ("fltk"); +%! hf = figure (); +%! sombrero (); +%! fname = [tempname(), ".svg"]; +%! print (fname); +%! close (hf) +%! graphics_toolkit (tk); +%! +%! ## Read the svg file and check the root node is named "svg" +%! dom = xmlread (fname); +%! if (dom.hasChildNodes ()) +%! root_node = dom.getFirstChild (); +%! printf ("The name of the root node is \"%s\"\n", ... +%! char (root_node.getNodeName ())) +%! endif + diff -r e8fa10f8920c -r 64c5e6489464 scripts/io/xmlwrite.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/io/xmlwrite.m Sun Oct 04 22:35:24 2020 +0200 @@ -0,0 +1,79 @@ +######################################################################## +## +## Copyright (C) 2020 The Octave Project Developers +## +## See the file COPYRIGHT.md in the top-level directory of this +## distribution or . +## +## This file is part of Octave. +## +## Octave is free software: you can redistribute it and/or modify it +## under the terms of the GNU General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## Octave is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with Octave; see the file COPYING. If not, see +## . +## +######################################################################## + +## -*- texinfo -*- +## @deftypefn {} {} xmlwrite (@var{filename}, @var{dom_node}) +## @deftypefnx {} {@var{str} =} xmlwrite (@var{dom_node}) +## Write XML DOM @var{dom_node} to file @var{filename} or return as string. +## +## @var{doc_type} is not yet implemented. +## +## @seealso{xmlwrite, dom_node, csvread, dlmread, csvwrite, dlmwrite} +## @end deftypefn + +function str = xmlwrite (filename, dom_node) + + if (! ((nargin == 1) || (nargin == 2 && nargout == 0))) + print_usage (); + endif + + if (nargin == 1) + dom_node = filename; + str = __xml_print_str__ (dom_node.get_dom_node ()); + else + if (! ischar (filename)) + error ("xmlwrite: FILENAME must be a string"); + endif + if (! any (strcmp (class (dom_node), {"dom_node", "dom_document"}))) + error ("xmlwrite: DOM_NODE must be a XML DOM object"); + endif + __xml_write_file__ (filename, dom_node.get_dom_node ()); + endif + +endfunction + + +%!demo +%! doc = dom_document (); +%! +%! ## Create a root node and add it to the document +%! root = doc.createElement ("RootNode"); +%! doc.appendChild (root); +%! root.setAttribute ("created", datestr (date ())); +%! +%! ## Create a child node, append it text data and add it to the +%! ## root children +%! child = doc.createElement ("TextChild"); +%! root.appendChild (child); +%! child.setAttribute ("verbose", "yes"); +%! child.setAttribute ("useful", "no"); +%! child.setNodeValue ("This is some text data"); +%! +%! ## Serialize DOM document to string +%! str = xmlwrite (doc) + + +## FIXME: Add BISTs +%!assert (1)