=head1 NAME

iPE::XML::DocObject - The document object class for all XML document based objects.

=head1 DESCRIPTION

This class is a subclass of the iPE::XML::Object class.  Like XML::Objects, it has an associated node, but also has a document node which can be printed out afterward.  The aim of this class is to allow all objects which came from an XML document to be reproduced in XML format with added, removed or altered data.

=head1 FUNCTIONS

=over 8

=cut

package iPE::XML::DocObject;
use iPE::XML::Wrappers;
use XML::LibXML;
use base ("iPE::XML::Object");

=item new ()

This constructor should be called in all subclasses.  Unlike with XML objects, they are not constructed with a document, so you cannot handle the children elements.  handle_children is called automatically from the parse_file method, so there is no need to call it from here.

=cut
sub new {
    my $class = shift;
    my $this = $class->SUPER::new(@_);
    $this->{parser_} = new XML::LibXML;

    return $this;
}

=item parser ()

Returns the parser object used to parse the XML document

=cut
sub parser      { shift->{parser_} }

=item document ()

Returns the XML::LibXML::Document object representing the entire XML Document.

=cut
sub document    { shift->{xml_doc_} }

=item rootName ()

Returns the root name of this document.  Can be used for validating whether the correct type of document is being parsed.

=cut
sub rootName { shift->{xml_element_}->nodeName }

=item filename ()

Returns the name of the file from which this document object was parsed, if any exists.

=cut
sub filename { shift->{filename_} }

=item parse_file (filename[, old_root, [, new_root, new_system_id]])

Parses the file referenced by the filename passed and populates the object with the data contained in the file, after checking the file for XML errors.  Pass filename so error messages can output the filename.

Optionally, pass the name of the root node you expect to see in the document to validate that this is the correct type of file for parsing.

Optionally, pass a new root element name and system id to set this document's system id in a new file.  This is good if you are adding new elements not included in the DTD for the original file.

=cut
sub parse_file  {
    my ($this, $filename, $oldroot, $root, $system_id) = @_;
    open (FILE, "<$filename") 
        or die __PACKAGE__.": could not open file $filename for reading.\n";
    XMLCheck($filename, \*FILE);
    $this->{xml_doc_} = $this->parser->parse_file($filename);
    close FILE;

    $this->{filename_} = $filename;

    if(defined $oldroot) {
        die __PACKAGE__.": Root element for $filename should be $oldroot.\n"
            if($oldroot ne $this->{xml_doc_}->documentElement->nodeName);
        if(defined $root && defined $system_id) {
            my $old_doc = $this->{xml_doc_};
            $this->{xml_doc_} = new XML::LibXML::Document();
            $this->{xml_doc_}->createInternalSubset($root, undef, $system_id);
            $this->{xml_doc_}->setDocumentElement($old_doc->documentElement);
            $this->{xml_doc_}->documentElement->setName($root);
        }
    }

    $this->{xml_element_} = $this->{xml_doc_}->documentElement;

    $this->handle_children($this->{xml_element_});
    $this->init;
    return $this;
}

=item outputXML (out)

Prints out the entire XML document.

=cut
sub outputXML {
    my ($this, $out) = @_;
    $out->print ($this->document->toString(2)); # 1 formats the document.
}

=back

=head1 SEE ALSO

L<iPE::XML::Object>

=head1 AUTHOR

Bob Zimmermann (rpz@cse.wustl.edu)

=cut

1;
