Module Pxp_document


module Pxp_document: sig .. end


Pxp_document:

Object model of the document/element instances

QUESTIONS:

type node_type =
| T_element of string
| T_data
| T_super_root
| T_pinstr of string
| T_comment
| T_none
| T_attribute of string
| T_namespace of string

<ID:type-node-type> <TYPE:type> <CALL> node_type <SIG> AUTO <DESCR> This type enumerates the possible node types:

About T_super_root, T_pinstr, T_comment: These types are extensions to my original design. They have mainly been added to simplify the implementation of standards (such as XPath) that require that nodes of these types are included into the main document tree. There are options (see Pxp_yacc) forcing the parser to insert such nodes; in this case, the nodes are actually element nodes serving as wrappers for the additional data structures. The options are: enable_super_root_node, enable_pinstr_nodes, enable_comment_nodes. By default, such nodes are not created.

About T_attribute, T_namespace: These types are fully virtual. This means that it is impossible to make the parser insert such nodes into the regular tree. They are normally created by special methods to allow additional views on the document tree.

type data_node_classification =
| CD_normal
| CD_other
| CD_empty
| CD_ignorable
| CD_error of exn (*<ID:type-data-node-classification> <CALL> data_node_classification <SIG> AUTO <DESCR> This type enumerates the result values of the method classify_data_node. See the description of this method. </ID>*)
The result type of the method classify_data_node:

QUESTION: Perhaps we should reexport att_value here. It is the only type from Pxp_types that is needed regularly.

Regular definition:
class type ['a] extension = object .. end
class type ['a node #extension as 'a] node = object .. end

class type namespace_info: removed
class ['a node #extension as 'a] data_impl : 'a -> ['a] node
<ID:class-data-impl> <TYPE:class> <CALL> 'ext data_impl <SIG> AUTO <DESCR> This class is an implementation of node which realizes data nodes.
class ['a node #extension as 'a] element_impl : 'a -> ['a] node
<ID:class-element-impl> <TYPE:class> <CALL> 'ext element_impl <SIG> AUTO <DESCR> This class is an implementation of node which realizes element nodes.
class ['a node #extension as 'a] comment_impl : 'a -> ['a] node
<ID:class-comment-impl> <TYPE:class> <CALL> 'ext comment_impl <SIG> AUTO <DESCR> This class is an implementation of node which realizes comment nodes.
class ['a node #extension as 'a] super_root_impl : 'a -> ['a] node
<ID:class-super-root-impl> <TYPE:class> <CALL> 'ext super_root_impl <SIG> AUTO <DESCR> This class is an implementation of node which realizes super root nodes.
class ['a node #extension as 'a] pinstr_impl : 'a -> ['a] node
<ID:class-pinstr-impl> <TYPE:class> <CALL> 'ext pinstr_impl <SIG> AUTO <DESCR> This class is an implementation of node which realizes processing instruction nodes.
val pinstr : ('a node #extension as 'a) node ->
Pxp_dtd.proc_instruction
<ID:val-pinstr> <TYPE:fun> <CALL> pinstr n <SIG> AUTO <DESCR> Returns the processing instruction contained in a processing instruction node. This function raises Invalid_argument if invoked for a different node type than T_pinstr. </ID>
class ['a node #extension as 'a] attribute_impl : element:string -> name:string -> Pxp_core_types.att_value -> Pxp_dtd.dtd -> ['a] node
Creation: new attribute_impl element_name attribute_name attribute_value dtd Note that attribute nodes do intentionally not have extensions.
val attribute_name : ('a node #extension as 'a) node ->
string
<ID:val-attribute-name> <TYPE:fun> <CALL> attribute_name n <SIG> AUTO <DESCR> Returns the name of the attribute contained in an attribute node. Raises Invalid_argument if n does not have node type T_attribute. </ID>
val attribute_value : ('a node #extension as 'a) node ->
Pxp_core_types.att_value
<ID:val-attribute-value> <TYPE:fun> <CALL> attribute_value n <SIG> AUTO <DESCR> Returns the value of the attribute contained in an attribute node. Raises Invalid_argument if n does not have node type T_attribute. </ID>
val attribute_string_value : ('a node #extension as 'a) node ->
string
<ID:val-attribute-string-value> <TYPE:fun> <CALL> attribute_string_value n <SIG> AUTO <DESCR> Returns the string value of the attribute contained in an attribute node. Raises Invalid_argument if n does not have node type T_attribute. </ID>
class ['a node #extension as 'a] namespace_element_impl : 'a -> ['a] node
<ID:class-namespace-element-impl> <TYPE:class> <CALL> 'ext namespace_element_impl <SIG> AUTO <DESCR> This class is an implementation of node which realizes element nodes.

namespace_element_impl: the namespace-aware implementation of element nodes.

This class has an extended definition of the create_element method. It accepts element names of the form "normprefix:localname" where normprefix must be a prefix managed by the namespace_manager. Note that create_element does not itself normalize prefixes; it is expected that the prefixes are already normalized.

In addition to calling create_element, one can set the namespace scope after creation (set_namespace_scope) to save the mapping of unprocessed namespace prefixes to normalized prefixes. This is voluntary.

Such nodes have the node type T_element "normprefix:localname".

Furthermore, this class implements the methods:


class ['a node #extension as 'a] namespace_attribute_impl : element:string -> name:string -> Pxp_core_types.att_value -> Pxp_dtd.dtd -> ['a] node

namespace_attribute_impl: the namespace-aware implementation of attribute nodes.
class ['a node #extension as 'a] namespace_impl : string -> string -> Pxp_dtd.dtd -> ['a] node
Namespace objects are only used to represent the namespace declarations occurring in the attribute lists of elements.
val namespace_normprefix : ('a node #extension as 'a) node ->
string
val namespace_display_prefix : ('a node #extension as 'a) node ->
string
val namespace_uri : ('a node #extension as 'a) node ->
string
These functions return the normprefix, the display prefix, and the URI stored in a namespace object. If invoked for a different node type, the functions raise Invalid_argument.
type 'a node #extension as 'a spec 
<ID:type-spec> <TYPE:type> <CALL> 'ext spec <SIG> AUTO <DESCR> The abstract data type specifying which objects are actually created by the parser. </ID>
val make_spec_from_mapping : ?super_root_exemplar:('a node #extension as 'a)
node ->
?comment_exemplar:'a node ->
?default_pinstr_exemplar:'a node ->
?pinstr_mapping:(string, 'a node) Hashtbl.t ->
data_exemplar:'a node ->
default_element_exemplar:'a node ->
element_mapping:(string, 'a node) Hashtbl.t ->
unit -> 'a spec
<ID:val-make-spec-from-mapping> <TYPE:fun> <CALL> make_spec_from_mapping ~super_root_exemplar ~comment_exemplar ~default_pinstr_exemplar ~pinstr_mapping ~data_exemplar ~default_element_exemplar ~element_mapping () <SIG> AUTO <DESCR> Creates a spec from the arguments. Some arguments are optional, some arguments are mandatory.
val make_spec_from_alist : ?super_root_exemplar:('a node #extension as 'a)
node ->
?comment_exemplar:'a node ->
?default_pinstr_exemplar:'a node ->
?pinstr_alist:(string * 'a node) list ->
data_exemplar:'a node ->
default_element_exemplar:'a node ->
element_alist:(string * 'a node) list ->
unit -> 'a spec
<ID:val-make-spec-from-alist> <TYPE:fun> <CALL> make_spec_from_alist ~super_root_exemplar ~comment_exemplar ~default_pinstr_exemplar ~pinstr_alist ~data_exemplar ~default_element_exemplar ~element_alist () <SIG> AUTO <DESCR> Creates a spec from the arguments. This is a convenience function for make_spec_from_mapping; instead of requiring hashtables the function allows it to pass associative lists. </ID>
val create_data_node : ('a node #extension as 'a) spec ->
Pxp_dtd.dtd -> string -> 'a node
<ID:val-create-data-node> <TYPE:fun> <CALL> create_data_node spec dtd datastring <SIG> AUTO <DESCR> Creates a new data node from the exemplar contained in spec. The new node contains datastring and is connected with the dtd. </ID>
val create_element_node : ?name_pool_for_attribute_values:Pxp_core_types.pool ->
?position:string * int * int ->
?valcheck:bool ->
?att_values:(string * Pxp_core_types.att_value) list ->
('a node #extension as 'a) spec ->
Pxp_dtd.dtd -> string -> (string * string) list -> 'a node
<ID:val-create-element-node> <CALL> create_element_node ~name_pool_for_attribute_values ~position ~valcheck ~att_values spec dtd eltype att_list <SIG> AUTO <DESCR> Creates a new element node from the exemplar(s) contained in spec:
val create_super_root_node : ?position:string * int * int ->
('a node #extension as 'a) spec ->
Pxp_dtd.dtd -> 'a node
<ID:val-create-super-root-node> <CALL> create_super_root_node ~position spec dtd <SIG> AUTO <DESCR> Creates a new super root node from the exemplar contained in spec. The new node is connected to dtd, and the position triple is set to ~position.

The function fails if there is no super root exemplar in spec. </ID>

val create_comment_node : ?position:string * int * int ->
('a node #extension as 'a) spec ->
Pxp_dtd.dtd -> string -> 'a node
<ID:val-create-comment-node> <CALL> create_comment_node ~position spec dtd commentstring <SIG> AUTO <DESCR> Creates a new comment node from the exemplar contained in spec. The new node is connected to dtd, and the position triple is set to ~position. The contents of the node are set to commentstring.

The function fails if there is no comment exemplar in spec. </ID>

val create_pinstr_node : ?position:string * int * int ->
('a node #extension as 'a) spec ->
Pxp_dtd.dtd -> Pxp_dtd.proc_instruction -> 'a node
<ID:val-create-pinstr-node> <CALL> create_pinstr_node ~position spec dtd pi <SIG> AUTO <DESCR> Creates a new processing instruction node from the exemplar contained in spec. The new node is connected to dtd, and the position triple is set to ~position. The contents of the node are set to pi.

The function fails if there is no processing instruction exemplar in spec. </ID>

val create_no_node : ?position:string * int * int ->
('a node #extension as 'a) spec ->
Pxp_dtd.dtd -> 'a node
Creates a T_none node with limited functionality NOTE: This function is conceptually broken and may be dropped in the future.
val get_data_exemplar : ('a node #extension as 'a) spec ->
'a node
val get_element_exemplar : ('a node #extension as 'a) spec ->
string -> (string * string) list -> 'a node
val get_super_root_exemplar : ('a node #extension as 'a) spec ->
'a node
val get_comment_exemplar : ('a node #extension as 'a) spec ->
'a node
val get_pinstr_exemplar : ('a node #extension as 'a) spec ->
Pxp_dtd.proc_instruction -> 'a node
These functions just return the exemplars (or raise Not_found). Notes: (1) In future versions, it may be possible that the element exemplar depends on attributes, too, so the attlist must be passed to get_element_exemplar (2) In future versions, it may be possible that the pinstr exemplar depends on the full value of the processing instruction and not only on the target, so the full proc_instruction must be passed to get_pinstr_exemplar.

The functions compare and ord_compare implement the so-called "document order". The basic principle is that the nodes are linearly ordered by their occurence in the textual XML representation of the tree. While this is clear for element nodes, data nodes, comments, and processing instructions, a more detailed definition is necessary for the other node types. In particular, attribute nodes of an element node occur before any regular subnode of the element, and namespace nodes of that element occur even before the attribute nodes. So the order of nodes of <sample a1="5" a2="6"><subnode/></sample> is 1. element "sample" 2. attribute "a1" 3. attribute "a2" 4. element "subnode" Note that the order of the attributes of the same element is unspecified, so "a2" may alternatively be ordered before "a1". If there were namespace nodes, they would occur between 1 and 2. If there is a super root node, it will be handled as the very first node.
val compare : ('a node #extension as 'a) node ->
'a node -> int
<ID:val-compare> <TYPE:fun> <CALL> compare n1 n2 <SIG> AUTO <DESCR> Returns -1 if n1 occurs before n2, or +1 if n1 occurs after n2, or 0 if both nodes are identical. If the nodes are unrelated (do not have a common ancestor), the result is undefined (Note: this case is different from ord_compare). This test is rather slow, but it works even if the XML tree changes dynamically (in contrast to ord_compare below). </ID>
type 'a node #extension as 'a ord_index 
<ID:type-ord-index> <TYPE:type> <CALL> 'ext ord_index <SIG> AUTO <DESCR> The type of ordinal indexes. </ID>
val create_ord_index : ('a node #extension as 'a) node ->
'a ord_index
<ID:val-create-ord-index> <TYPE:fun> <CALL> create_ord_index startnode <SIG> AUTO <DESCR> Creates an ordinal index for the subtree starting at startnode. This index assigns to every node an ordinal number (beginning with 0) such that nodes are numbered upon the order of the first character in the XML representation (document order). Note that the index is not automatically updated when the tree is modified. </ID>
val ord_number : ('a node #extension as 'a) ord_index ->
'a node -> int
Returns the ordinal number of the node, or raises Not_found. Note that attribute nodes and namespace nodes are treated specially: All attribute nodes for a certain element node have the _same_ ordinal index. All namespace nodes for a certain element node have the _same_ ordinal index. (So ord_number x = ord_number y does not imply x == y for these nodes. However, this is true for the other node types.) It is not recommended to work with the ordinal number directly but to call ord_compare which already handles the special cases.
val ord_compare : ('a node #extension as 'a) ord_index ->
'a node -> 'a node -> int
<ID:val-ord-compare> <TYPE:fun> <CALL> ord_compare idx n1 n2 <SIG> AUTO <DESCR> Compares two nodes like compare: Returns -1 if n1 occurs before n2, or +1 if n1 occurs after n2, or 0 if both nodes are identical. If one of the nodes does not occur in the ordinal index, Not_found is raised. (Note that this is a different behaviour than what compare would do.)

This test is much faster than compare. </ID>


General note: The iterators ignore attribute and namespace nodes
val find : ?deeply:bool ->
(('a node #extension as 'a) node ->
bool) ->
'a node -> 'a node
<ID:val-find> <TYPE:fun> <CALL> find ~deeply f startnode <SIG> AUTO <DESCR> Searches the first node in the tree below startnode for which the predicate f is true, and returns it. Raises Not_found if there is no such node.

By default, ~deeply=false. In this case, only the children of startnode are searched.

If passing ~deeply=true, the children are searched recursively (depth-first search). Note that even in this case startnode itself is not checked.

Attribute and namespace nodes are ignored. </ID>

val find_all : ?deeply:bool ->
(('a node #extension as 'a) node ->
bool) ->
'a node -> 'a node list
<ID:val-find-all> <CALL> find_all ~deeply f startnode <SIG> AUTO <DESCR> Searches all nodes in the tree below startnode for which the predicate f is true, and returns them.

By default, ~deeply=false. In this case, only the children of startnode are searched.

If passing ~deeply=true, the children are searched recursively (depth-first search). Note that even in this case startnode itself is not checked.

Attribute and namespace nodes are ignored. </ID>

val find_element : ?deeply:bool ->
string ->
('a node #extension as 'a) node ->
'a node
<ID:val-find-element> <TYPE:fun> <CALL> find_element ~deeply eltype startnode <SIG> AUTO <DESCR> Searches the first element in the tree below startnode that has the element type eltype, and returns it. Raises Not_found if there is no such node.

By default, ~deeply=false. In this case, only the children of startnode are searched.

If passing ~deeply=true, the children are searched recursively (depth-first search). Note that even in this case startnode itself is not checked. </ID>

val find_all_elements : ?deeply:bool ->
string ->
('a node #extension as 'a) node ->
'a node list
<ID:val-find-all-elements> <TYPE:fun> <CALL> find_all_elements ~deeply eltype startnode <SIG> AUTO <DESCR> Searches all elements in the tree below startnode having the element type eltype, and returns them.

By default, ~deeply=false. In this case, only the children of startnode are searched.

If passing ~deeply=true, the children are searched recursively (depth-first search). Note that even in this case startnode itself is not checked. </ID>

exception Skip
<ID:exc-skip> <TYPE:exception> <CALL> Skip <SIG> AUTO <DESCR> This exception can be used in the functions passed to map_tree, map_tree_sibl, iter_tree, and iter_tree_sibl to skip the current node, and to proceed with the next node. See these function for details. </ID>
val map_tree : pre:(('a node #extension as 'a) node ->
('b node #extension as 'b) node) ->
?post:('b node -> 'b node) ->
'a node -> 'b node
<ID:val-map-tree> <TYPE:fun> <CALL> map_tree ~pre ~post startnode <SIG> AUTO <DESCR> Maps the tree beginning at startnode to a second tree using the following algorithm.

startnode and the whole tree below it are recursively traversed. After entering a node, the function ~pre is called. The result of this function must be a new node; it must not have children nor a parent. For example, you can pass ~pre:(fun n -> n # orphaned_flat_clone) to copy the original node. After that, the children are processed in the same way (from left to right) resulting in a list of mapped children. These are added to the mapped node as its children.

Now, the ~post function is invoked with the mapped node as argument, and the result is the result of the function (~post should return a root node, too; if not specified, the identity is the ~post function).

Both ~pre and ~post may raise Skip which causes that the node is left out (i.e. the mapped tree does neither contain the node nor any children of the node). If the top node is skipped, the exception Not_found is raised.

For example, the following piece of code duplicates a tree, but removes all comment nodes:

map_tree ~pre:(fun n -> if n # node_type = T_comment then raise Skip else n # orphaned_flat_clone) startnode

Attribute and namespace nodes are ignored. </ID>

val map_tree_sibl : pre:(('a node #extension as 'a) node
option ->
'a node ->
'a node option ->
('b node #extension as 'b) node) ->
?post:('b node option ->
'b node ->
'b node option -> 'b node) ->
'a node -> 'b node
<ID:val-map-tree-sibl> <TYPE:fun> <CALL> map_tree_sibl ~pre ~post startnode <SIG> AUTO <DESCR> Maps the tree beginning at startnode to a second tree using the following algorithm.

startnode and the whole tree below it are recursively traversed. After entering a node, the function ~pre is called with three arguments: some previous node, the current node, and some next node. The previous and the next node may not exist because the current node is the first or the last in the current list of nodes. In this case, None is passed as previous or next node, resp. The result of this function invocation must be a new node; it must not have children nor a parent. For example, you can pass ~pre:(fun prev n next -> n # orphaned_flat_clone) to copy the original node. After that, the children are processed in the same way (from left to right) resulting in a list of mapped children.

Now, the ~post function is applied to the list of mapped children resulting in a list of postprocessed children. (Note: this part works rather differently than map_tree.) ~post has three arguments: some previous child, the current child, and some next child. The previous and the next child are None if non-existing. The postprocessed children are appended to the mapped node resulting in the mapped tree.

Both ~pre and ~post may raise Skip which causes that the node is left out (i.e. the mapped tree does neither contain the node nor any children of the node). If the top node is skipped, the exception Not_found is raised.

Attribute and namespace nodes are ignored. </ID>

val iter_tree : ?pre:(('a node #extension as 'a) node ->
unit) ->
?post:('a node -> unit) -> 'a node -> unit
<ID:val-iter-tree> <TYPE:fun> <CALL> iter_tree ~pre ~post startnode <SIG> AUTO <DESCR> Iterates over the tree beginning at startnode using the following algorithm.

startnode and the whole tree below it are recursively traversed. After entering a node, the function ~pre is called. Now, the children are processed recursively. Finally, the ~post function is invoked.

The ~pre function may raise Skip causing that the children and the invocation of the ~post function are skipped. If the ~post function raises Skip nothing special happens.

Attribute and namespace nodes are ignored. </ID>

val iter_tree_sibl : ?pre:(('a node #extension as 'a) node
option -> 'a node -> 'a node option -> unit) ->
?post:('a node option ->
'a node -> 'a node option -> unit) ->
'a node -> unit
<ID:val-iter-tree-sibl> <TYPE:fun> <CALL> iter_tree_sibl ~pre ~post startnode <SIG> AUTO <DESCR> Iterates over the tree beginning at startnode using the following algorithm.

startnode and the whole tree below it are recursively traversed. After entering a node, the function ~pre is called with three arguments: some previous node, the current node, and some next node. The previous and the next node may be None if non-existing. Now, the children are processed recursively. Finally, the ~post function is invoked with the same three arguments.

The ~pre function may raise Skip causing that the children and the invocation of the ~post function are skipped. If the ~post function raises Skip nothing special happens.

Attribute and namespace nodes are ignored. </ID>

type stripping_mode = [ `Disabled | `Strip_one | `Strip_one_lf | `Strip_seq ] 
<ID:type-stripping-mode> <TYPE:type> <CALL> stripping_mode <SIG> AUTO <DESCR> The different ways how to strip whitespace from a single data node:
val strip_whitespace : ?force:bool ->
?left:stripping_mode ->
?right:stripping_mode ->
?delete_empty_nodes:bool ->
('a node #extension as 'a) node ->
unit
<ID:val-strip-whitespace> <TYPE:fun> <CALL> strip_whitespace ~force ~left ~right ~delete_empty_nodes startnode <SIG> AUTO <DESCR> Modifies the passed tree in-place by the following rules: Defaults:
val normalize : ('a node #extension as 'a) node ->
unit
<ID:val-normalize> <TYPE:fun> <CALL> normalize startnode <SIG> AUTO <DESCR> Normalizes the tree denoted by startnode such that neither empty data nodes nor adjacent data nodes exist. Normalization works in-place. </ID>
val validate : ('a node #extension as 'a) node ->
unit
<ID:val-validate> <TYPE:fun> <CALL> validate startnode <SIG> AUTO <DESCR> Validates the tree denoted by startnode. In contrast to startnode # validate() this function validates recursively. </ID>
class ['a node #extension as 'a] document : ?swarner:Pxp_core_types.symbolic_warnings -> Pxp_core_types.collect_warnings -> Pxp_core_types.rep_encoding -> object .. end

Printers for toploop:
val print_node : ('a node #extension as 'a) node ->
unit
val print_doc : ('a node #extension as 'a) document ->
unit

Experimental: event streams and node trees
exception Error_event of exn
The event stream contains an E_error event
type 'a node #extension as 'a solid_xml = [ `Document of 'a document | `Node of 'a node ] 
val solidify : ?dtd:Pxp_dtd.dtd ->
Pxp_types.config ->
('a node #extension as 'a) spec ->
(unit -> Pxp_types.event option) -> 'a solid_xml

Reads the event stream by calling the unit->event function, and creates a node tree according to config, dtd, spec.

The event stream may be either:

Document streams contain a DTD. The found DTD is used for the node tree. Content streams, on the contrary, do not contain DTDs. In this case, an empty DTD is created (in well-formedness mode).

The dtd argument overrides any DTD, no matter whether found in the stream or freshly created.

If the DTD allows validation, the returned tree is validated.

The data nodes are not normalized unless the arriving data events are already normalized. To get this effect, filter the stream with Pxp_ev_parser.norm_cdata_filter before calling solidify.

Ignorable whitespace is not automatically removed. To get this effect, filter the stream with Pxp_ev_parser.drop_ignorable_whitespace_filter before calling solidify.

The uniqueness of ID attributes is not checked.

val liquefy : ?omit_end:bool ->
?omit_positions:bool ->
('a node #extension as 'a) solid_xml ->
'b -> Pxp_types.event option
The converse of solidify: The passed node or document is transformed into an event stream.

omit_end: If true, the E_end_of_stream event is omitted at the end. Useful to concatenate several streams. Default: false. omit_positions: If true, no E_position events are generated. Default:false.