1 use strict; use warnings;
2 package OpenILS::Utils::FlatXML;
5 my $_tC_mask = 1 << XML_TEXT_NODE | 1 << XML_COMMENT_NODE | 1 << XML_CDATA_SECTION_NODE | 1 << XML_DTD_NODE;
6 my $_val_mask = 1 << XML_ATTRIBUTE_NODE | 1 << XML_NAMESPACE_DECL;
9 my $parser = XML::LibXML->new();
10 $parser->keep_blanks(0);
14 $class = ref($class) || $class;
15 return bless(\%args,$class);
31 $self->{xmlfile} = $xml;
33 return $self->{xmlfile};
38 my $doc = $self->{doc};
40 $xml ||= $self->{xml};
41 $doc = $self->{doc} = $parser->parse_string( $xml );
49 $self->xml_to_doc( $xml );
50 my $nodeset = $self->_xml_to_nodeset;
56 return $self->{nodelist};
59 sub xmlfile_to_nodeset {
60 my($self, $xmlfile) = @_;
61 $self->xmlfile( $xmlfile );
62 $self->xmlfile_to_doc;
63 return $self->xml_to_nodeset;
72 my($self, $xmlfile) = @_;
73 $xmlfile ||= $self->xmlfile;
74 my $doc = $self->{doc};
76 $doc = $self->{doc} = $parser->parse_file( $xmlfile );
84 $nodeset ||= $self->nodeset;
86 my $doc = XML::LibXML::Document->new;
91 for my $node ( @$nodeset ) {
94 if ( $node->{node_type} == XML_ELEMENT_NODE ) {
96 $xml = $doc->createElement( $node->{name} );
98 $xml->setNodeName($seen_ns{$node->{namespace_uri}} . ':' . $xml->nodeName) if ($node->{namespace_uri} and $seen_ns{$node->{namespace_uri}});
100 } elsif ( $node->{node_type} == XML_TEXT_NODE ) {
101 $xml = $doc->createTextNode( $node->{value} );
103 } elsif ( $node->{node_type} == XML_COMMENT_NODE ) {
104 $xml = $doc->createComment( $node->{value} );
106 } elsif ( $node->{node_type} == XML_NAMESPACE_DECL ) {
107 if ($self->nodeset->[$node->{parent_node}]->{namespace_uri} eq $node->{value}) {
108 $_xmllist[$node->{parent_node}]->setNamespace($node->{value}, $node->{name}, 1);
110 $_xmllist[$node->{parent_node}]->setNamespace($node->{value}, $node->{name}, 0);
112 $seen_ns{$node->{value}} = $node->{name};
115 } elsif ( $node->{node_type} == XML_ATTRIBUTE_NODE ) {
117 if ($node->{namespace_uri}) {
118 $_xmllist[$node->{parent_node}]->setAttributeNS($node->{namespace_uri}, $node->{name}, $node->{value});
120 $_xmllist[$node->{parent_node}]->setAttribute($node->{name}, $node->{value});
128 $_xmllist[$node->{intra_doc_id}] = $xml;
130 if (defined $node->{parent_node}) {
131 $_xmllist[$node->{parent_node}]->addChild($xml);
135 $doc->setDocumentElement($_xmllist[0]);
140 # --------------------------------------------------------------
141 # -- Builds a list of nodes from a given xml doc
142 sub _xml_to_nodeset {
144 my($self, $doc) = @_;
147 return undef unless($doc);
149 my $node = $doc->documentElement;
150 return undef unless($node);
153 $self->{next_id} = 0;
155 push @{$self->{nodelist}}, {
157 parent_node => undef,
158 name => $node->localname,
160 node_type => $node->nodeType,
161 namespace_uri => $node->namespaceURI
164 $self->_nodeset_recurse( $node, 0);
169 sub _nodeset_recurse {
171 my( $self, $node, $parent) = @_;
172 return undef unless($node && $node->nodeType == 1);
175 for my $kid ( ($node->getNamespaces, $node->attributes, $node->childNodes) ) {
177 my $type = $kid->nodeType;
179 push @{$self->{nodelist}}, {
180 intra_doc_id => ++$self->{next_id},
181 parent_node => $parent,
182 name => $kid->localname,
183 value => _grab_content( $kid, $type ),
185 namespace_uri => ($type != 18 ? $kid->namespaceURI : undef )
188 return if ($type == 3);
189 $self->_nodeset_recurse( $kid, $self->{next_id});
195 my $type = 1 << shift();
197 return undef if ($type & 1 << XML_ELEMENT_NODE);
198 return $node->textContent if ($type & $_tC_mask);
199 return $node->value if ($type & $_val_mask);
200 return $node->getData if ($type & 1 << XML_PI_NODE);