(* Yaxi - Yet Another X* Implementation
 * Copyright (C) 2003 Matthieu Sozeau
 *
 * This program 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 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 * 
 * $Id: yaxi_xml.mli,v 1.21 2004/05/02 11:45:21 mat Exp $ 
 * 
 *)

(** {1 XML types and related functions} *)

(** {2 XML types} *)
(** Namespace definition URI *)
type namespace = [ `None | `URI of string]

(** NCName *)
and ncname = string

(** List of ncnames *)
and ncnames = ncname list

(** Namespace prefix *)
and prefix = ncname

(** prefix and associated namespace *)
and ns_mapping = prefix * namespace

(** List of namespace mappings *)
and ns_mappings = ns_mapping list

(** Name-mixed token *)
and nmtoken = string

(** Qualified name *)
and qname = ns_mapping * ncname

(** List of qname's *)
and qnames = qname list

(** Associative list with qnames as keys *)
and 'a qname_assoclist = (qname * 'a) list

(** Character data *)
and cdata = string
 
(** Attribute value *)
and attribute_value = cdata

(** Attribute list *)
and attributes = attribute_value qname_assoclist

(** Node type*)
type node = 
    Root of nodeset ref      (** Root node, no parent *)
  | Element of node ref      (** parent *)
      * qname                (** name *)
      * nodeset ref          (** namespaces *)
      * nodeset ref          (** attributes *)
      * nodeset ref          (** children *)
  | Attribute of node ref * qname * attribute_value     (** parent, name, value *)
  | NS of node ref * ns_mapping   	                (** parent, mapping *)
  | Text of node ref * cdata                            (** parent, contents *)
  | Comment of node ref * cdata                         (** parent, contents *)  
  | ProcessingInstruction of node ref * ncname * cdata  (** parent, name, contents *)
      
(** List of nodes *)
and nodeset = node list

(** Element quantifier *)
type dtd_quantifier = [ `Zero | `NonZero | `One | `Any ]

(** DTD children spec *)
type dtd_children_el = dtd_quantifier * dtd_children
and dtd_children_els = dtd_children_el list
and dtd_children = 
    [ `Choice of dtd_children_els 
    | `Seq of dtd_children_els 
    | `Element of string ]

(** DTD element content spec *)
type dtd_content_spec =
    [ `Any | `Empty | `PCDATA 
    | `Mixed of string list 
    | `Children of dtd_children ]

(** DTD attribute type *)
type dtd_attribute_type = 
    [ `CDATA | `ID | `IDREF | `IDREFS | `NMTOKEN | `NMTOKENS 
    | `Notation of string list 
    | `Enumeration of string list ]

type dtd_entity_value_template = [ `Text of string | `EntityRef of string ] list

(** DTD attribute default value *)    
type dtd_attribute_default =  
    [ `Fixed of string
    | `Implied 
    | `Required ]

(** DTD attribute definition *)    
type dtd_attrdef = string * dtd_attribute_type * dtd_attribute_default

(** DTD attribute definitions *)    
and dtd_attrdefs = dtd_attrdef list

(** DTD entity value *)    
type dtd_entity_value = 
    [ `ExternalID of string * string 
    | `UnparsedExternalID of string * string * string (** *)
    | `Value of dtd_entity_value_template ]

(** Parsed DTD entity value *)    
type dtd_parsed_entity_value = 
    [ `ExternalID of string * string 
    | `UnparsedExternalID of string * string * string
    | `Value of string ]

(** DTD declarations *)
and dtd_declarations = {  
  mutable dtd_elements: (string * dtd_content_spec) list;
  mutable dtd_attributes: (string * dtd_attrdefs) list;
  mutable dtd_notations: (string * (string * string)) list;
  mutable dtd_pis: (string * string) list;
  mutable dtd_comments: string list;
  mutable dtd_entities: (string * dtd_entity_value) list;
  mutable dtd_parameter_entities: (string * dtd_parsed_entity_value) list;
}

(** Document type declaration (will change when DTD parsing is implemented) *)
type dtd = {
  name: ncname;
  system_id: string option;
  public_id: string option;
  mutable internal_subset: dtd_declarations option;
  external_subset: dtd_declarations; (** Default entities here *)
  mutable fetch_remote_entities: bool; (** Fetch http:// ftp:// entities if needed ? *)
}

(** XML document *)
and document = {
  mutable root : node;                 (** Root node of document *)
  encoding : Netconversion.encoding;   (** Document encoding *)
  version : string;                    (** XML version of the document *)
  standalone: bool;
  mutable doctype: dtd;                (** Doctype of the document. Contains default entities by default *)
  base_uri: string;
} 

(** Exception sent when the document does not conform to the spec *)
exception XMLError of string

(** Use of an undefined namespace *)
exception UndefinedNamespace of string

(** The xml uri: http://www.w3.org/XML/1998/namespace *)
val xml_uri : [> `URI of string]

(** The xml namespace: "xml" -> xml_uri *)
val xml_ns : ncname * [> `URI of string]

(** xmlns namespace mapping *)
val xmlns_ns : ncname * [> `None]

(** The default namespace (null prefix, no namespace URI) *)
val default_ns : ncname * [> `None]

(** The null qname. It represents inexistent names, as a null name is not allowed anywhere.
  Note: one can safely use the = operator to test for equality of any qname with 
  null_name. == is still a bit dangerous, and is anyway used by =.
*)
val null_name : ((ncname * [> `None]) * ncname)

(** Test for null name *)
val is_null_name : qname -> bool

(** Create a qname from an ncname string using the default namespace *)
val unqname : ncname -> qname

(** [parse_name env str] parse a qualified name *)
val parse_name : ns_mappings -> string -> qname

(** [parse_ns_mapping env prefix value] parse a namespace mapping (xmlns:prefix="uri") *)
val parse_ns_mapping : ns_mappings -> 
  string -> string -> 
  ns_mapping

(** Create a new dtd declarations structure *)
val new_dtd_declarations : unit -> dtd_declarations

(** XML 1.0 default doctype declarations (common entities) *)
val default_dtd_declarations : unit -> dtd_declarations

(** [add_dtd_declarations to from] returns to with all dtd declarations from [from] added to it *)
val add_dtd_declarations : dtd_declarations -> dtd_declarations -> dtd_declarations

(** Get a parameter entity *)
val get_parameter_entity_value : dtd -> string -> dtd_parsed_entity_value

val parse_entity_value_template : dtd -> 
  dtd_entity_value_template ->
  string

val parse_entity_value : dtd -> dtd_entity_value -> dtd_parsed_entity_value

(** Get an entity value *)
val get_entity_value : dtd -> string -> dtd_parsed_entity_value

(** Get an element's definition from the dtd *)
val get_element_def : dtd -> string -> dtd_content_spec

(** Get an attribute's definition from the dtd *)
val get_attributes_def : dtd -> string -> dtd_attrdefs

(** Creates a new document type declaration. 
  It uses the default doctype declarations for XML plus the dtd declarations given *)
val new_dtd : string -> 
  ?public_id:string -> 
  ?system_id:string -> 
  ?internal_subset:dtd_declarations -> 
  ?external_subset:dtd_declarations -> 
  unit -> dtd

(** Creates a new document *)
val new_document : 
  string ->                        (** Base uri *)
  ?version:string ->
  ?encoding:Netconversion.encoding ->
  ?standalone:bool ->
  ?doctype:dtd ->
  unit -> document

(** Set the document type of the document *)
val set_doctype : document -> dtd -> unit

(** Creates a new element node *)
val new_element :
  qname ->
  ?namespaces:nodeset ->
  ?attributes:nodeset -> ?children:nodeset -> unit -> node

(** Creates a new attribute node *)
val new_attribute : qname -> attribute_value -> node

(** Creates a new text node *)
val new_text : cdata -> node

(** Creates a new comment node *)
val new_comment : cdata -> node

(** Creates a new processing instruction node *)
val new_pi : ncname -> cdata -> node

(** Creates a new namespace node *)
val new_namespace : ns_mapping -> node

(** Copy a node and its children *)
val copy_node : node -> node

(** bind_copy_children parent children 
  Copy and bind the nodes to some parent *)
val bind_copy_children : node -> nodeset -> nodeset
  
(** reparent parent child
  Rebinds the node to some parent *)
val reparent : node -> node -> unit

(** reparent_nodes parent children
  Rebinds some child node to a common parent *)
val reparent_nodes : node -> nodeset -> unit

(** remove_child parent child
  Removes a node from its parent child list (any child type) *)
val remove_child : node -> node -> unit
  
(** replace node newnode
  Replaces a node with another, at the same place in document order *)
val replace : node -> node -> unit

(** replace_with_nodes node nodes
  Replaces a node with a list of nodes, at the same place in document order *)
val replace_with_nodes : node -> nodeset -> unit

(** unlink the node from it's potential parent and returns it *)
val unlink : node -> node

(** Compares two qualified names *)
val qname_eq : qname -> qname -> bool

(** 1 Associative lists on qnames *)

(** Member *)
val qname_mem_assoc : qname -> 'a qname_assoclist -> bool

(** Association, raise Not_found if no value is bound to the name *)
val qname_assoc : qname -> 'a qname_assoclist -> 'a

(** Same as qname_assoc, but the name is constructed from a nc name *)
val qname_assoc_nc : ncname -> 'a qname_assoclist -> 'a

(** Association, with a default value if unbound *)
val qname_try_assoc : qname -> 'a qname_assoclist -> 'a -> 'a
  
(**  Same as qname_try_assoc, but the name is constructed from a nc name *)
val qname_try_assoc_nc : ncname -> 'a qname_assoclist -> 'a -> 'a


(** Attributes accessor functions *)

(** Attribute's name *)
val name_of_attr : node -> qname

(** Attribute's value *)
val value_of_attr : node -> attribute_value

(** Set the attributes of a node *)
val set_element_attrs : node -> nodeset -> unit

(** Attributes finding functions *)

(** Test the presence of an attribute on a node
  @param ncname name of the attribute
  @param node the node
*)
val has_attribute : ncname -> node -> bool

(** Get an attribute's node
  @param ncname the attribute's name
  @param node the node
*)
val get_attribute : ncname -> node -> node

(** Test the presence of an attribute (with namespace) on a node
  @param namespace namespace of the attribute
  @param ncname name of the attribute
  @param node the node
*) 
val has_attribute_ns : ( namespace * ncname ) -> node -> bool

(** Get an attribute's node (with namespace)
  @param namespace namespace of the attribute
  @param ncname name of the attribute
  @param node the node
*) 
val get_attribute_ns : ( namespace * ncname ) -> node -> node

(** Tests the presence of an attribute in an attributes list *)
val attr_mem_assoc : ncname -> node list -> bool

(** Tests the presence of an attribute with a specific namespace in an attributes list *)
val attrns_mem_assoc : namespace -> ncname -> node list -> bool

(** Get an attributes value in an attributes list *)
val attr_assoc : ncname -> node list -> attribute_value

(** Get an attributes value in an attributes list. Namespace version *)
val attrns_assoc : namespace -> ncname -> node list -> attribute_value

val ns_assoc : qname -> (qname * 'a) list -> 'a

(** Get an attribute, calling a function if it is not present *)
val get_attr_required :
  ncname -> node list -> (unit -> attribute_value) -> attribute_value

(** Try getting an attribute, returning a default value if the attribute wasn't found *)
val try_get_attr : ncname -> node list -> attribute_value -> attribute_value
val try_get_int_attr : ncname -> node list -> int -> int
val try_get_bool_attr : ncname -> node list -> bool -> bool

(** Raise an XMLError informing that a required attribute was missing for a node *)
val required_attr : string -> string -> unit -> 'a

(** Raise an XMLError informing that a required child node was missing for a node *)
val required_elem : string -> string -> unit -> 'a

(** {2 Type predicates} *)
  
(** Node is an element ? *)
val elementp : node -> bool

(** Node is an attribute ? *)
val attributep : node -> bool

(** Node is a namespace node ? *)
val namespacep : node -> bool

(** Any node type *)
val nodep : node -> bool
  
(** Node is a root node ? *)
val rootp : node -> bool
  
(** Node is a root or element node ? *)
val root_or_elemp : node -> bool
  
(** Node is a comment ? *)
val commentp : node -> bool

(** Node is text ? *)
val textp : node -> bool

(** Node is a processing instruction ? *)
val processing_instructionp : node -> bool
  
(** Return the children of the node, if any *)
val children : node -> nodeset

(** Return the parent of a node, or None if the node has no parent *)
val parent : node -> node option

(** Return the namespace node children of a node *)
val namespace_nodes : node -> nodeset

(** Return the namespace mappings of a node *)
val namespaces : node -> ns_mappings

(** Attributes of a node *)
val attributes : node -> attributes

(** Return the attribute children nodes of a node *)
val attribute_nodes : node -> nodeset

(** Return the name of a node, or null_name if the node has no name *)
val name : node -> qname

(** Return Some name of a node, or None if the node has no name *)
val name_opt : node -> qname option

(** find_ns_mapping prefix node
  Finds the namespace mapping of prefix in the parent nodes of node *)
val find_ns_mapping : string -> node -> ns_mapping 

(** find_ns_mappings uri node
  Finds the namespace mappings of uri in the doc or raise Not_found*)
val find_ns_mappings : string -> node -> ns_mapping list

(** Return the name of a node, or raise Not_found if the node has no name *)
val name_exc : node -> qname

(** Return the root node of the document *)
val root_node : document -> node

(** Return the document element of a document (the first element children of its root) *)
val document_element : document -> node option

(** Serialize the document.
  serialize_doc write doc serialize the document using function write
*)
val serialize_doc :
  (string -> unit) ->
  ?indent:bool ->
  ?omit_decl:bool ->
  ?omit_doctype:bool ->
  document -> unit

val serialize_node :
  (string -> unit) ->
  node -> unit  

(** String representation of a qualified name *)
val string_of_name : qname -> string

(** String representation of an attribute node inside an element start tag *)
val string_of_attribute : node -> string

(** String representation of a namespace mapping *)
val string_of_ns_mapping : ns_mapping -> string

(** String representation of a namespace node inside an element start tag *)
val string_of_namespace : node -> string

(** String representation of a node *)
val string_of_node : node -> string

(** String representation of a node list *)
val string_of_nodes : node list -> string

(** Indented versions *)
val pretty_string_of_node : string -> node -> string
val pretty_string_of_nodes : nodeset -> string
val pretty_string_of_doc : document -> string
val pretty_print_doc : document -> unit

(** Find the root node which is parent of a node *)
val find_root : node -> node 
