# # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" # # xmlHandlers -- package for generating a tree from an XML doc use XML::Parser; package xmlHandlers; $level = -1; %endCallback = (); %startCallback = (); $currentObj = 0; @objStack = (); 1; # methods # pkg reference, object name (tag), optional fileName. sub new { my $pkg = shift; my $parent = shift; # ref to parent object my $class = shift; # for debug use my @kids = (); # list of child objects push (@objStack, $parent); $currentObj = bless {'class' => $class, 'kids' => \@kids, # 'parent' => $parent, 'attributes' => 0, 'content' => ''}, $pkg; if (@_) { # if fileName passed, go! die "parent for document creation must be null" if ($parent); executeXML (shift); } return $currentObj; } # we'll call you when your object is started # class method sub registerStartCallback { my $objName = shift; # call me when you get my $callback = shift; # \&foo($objRef, $source); if ($startCallback{$objName}) { print STDERR "duplicate callback for $objName\n"; return; } $startCallback{$objName} = $callback; } # we'll call you when your object is completed # class method sub registerEndCallback { my $objName = shift; # call me when you get my $callback = shift; # \&foo($objRef); if ($endCallback{$objName}) { print STDERR "duplicate callback for $objName\n"; return; } $endCallback{$objName} = $callback; } sub start { } sub end { } sub char { my ($obj, $class, $string) = @_; } sub add { my $parent = shift; my $kid = shift; push (@{$parent->{'kids'}}, $kid); # $kid->{'parent'} = $parent; } # internal functions sub executeXML { my $file = shift; # ErrorContext - 0 don't report errors # - other = number of lines to display # ParseparamEnt - 1 allow parsing of dtd my $parser = XML::Parser->new(ErrorContext => 1, ParseParamEnt => 1); $parser->setHandlers (Char => \&charHandler, Start => \&startHandler, Default => \&defaultHandler, End => \&endHandler, Proc => \&procHandler, Comment => \&commentHandler, ExternEnt => \&externalHandler); $parser->parsefile ($file); } sub charHandler { my ($xmlObj, $string) = @_; chomp $string; $string =~ s/^\s+//; $string =~ s/\s+$//; unless ($string =~ /^\s*$/) { # print "charHandler: $currentObj->{'class'} $string\n" if $main::debug; $currentObj->{'content'} .= ' ' if ($currentObj->{'content'}); $currentObj->{'content'} .= $string; } } # create new object and attach to tree sub startHandler { my $xmlObj = shift; my $tag = shift; my $obj; my $parent = $currentObj; $obj = new xmlHandlers($currentObj, $tag); $parent->add ($obj); $obj->processAttributes ($tag, @_); my $functionRef; if ($functionRef = $startCallback{$tag}) { &$functionRef($obj, 'start'); } elsif ($main::debug) { # print "no start callback for $tag\n"; } } sub endHandler { my $xmlObj = shift; my $element = shift; # print "end tag $element\n" if $main::debug; my $functionRef; if ($functionRef = $endCallback{$element}) { &$functionRef($currentObj, 'end'); } elsif ($main::debug) { # print "no end callback for $element\n"; } # $currentObj = $currentObj->{'parent'}; $currentObj = pop (@objStack); } sub defaultHandler { my ($obj, $string) = @_; unless (!$main::debug || ($string =~ /^\s*$/)) { if ($string =~ /<\?xml/) { $string =~ s/<\?\S+\s+(.*)/$1/; my (%parameters) = parseProcInstruction ($string); print STDERR "Got call to default, guessed what to do: $string\n"; } else { print STDERR "Got call to default, didn't know what to do: $string\n"; } } } sub externalHandler { my ($obj, $base, $sysid, $pubid) = @_; $base = '' if !$base; $pubid = '' if !$pubid; print "external: base $base\nexternal: sysid $sysid\nexternal: pubid $pubid\n"; } sub commentHandler { my ($obj, $element) = @_; return unless $main::debug; unless ($element =~ /^\s*$/) { print "comment: $element\n"; } } sub procHandler { my $xmlObj = shift; my $target = shift; my $data = shift; my (%parameters) = parseProcInstruction ($data); $currentObj->processAttributes ($target, $data, @_); } # misc subs sub parseProcInstruction { my ($args) = @_; my (@outputArray) = (); while ($args =~ s/([^ =]+)=\"([^"]+)\"(.*)/$3/) { # " push (@outputArray, $1); push (@outputArray, $2); } return (@outputArray); } sub processAttributes { my $pkg = shift; my ($element, %content) = @_; # print "processAttributes: element = $element\n" if $main::debug; my $hashCount = 0; foreach $attributeName (keys %content) { if ($attributeName =~ /^\s*$/) { delete $content{$attributeName}; # remove null entries next; } $hashCount++; # print "attribute: $attributeName = $content{$attributeName}\n" # if $main::debug; } if ($hashCount && $pkg->{'attributes'}) { print STDERR "need to write attribute merge logic\n"; } else { $pkg->{'attributes'} = \%content; } } sub getKid { my $pkg = shift; my $whichKid = shift; my @kids = $pkg->getKids(); my $kid; foreach $kid (@kids) { my $class = $kid->getClass(); return $kid if $class eq $whichKid; } return undef; } sub getKids { my $pkg = shift; return @{$pkg->{'kids'}}; } sub getAttributes { my $pkg = shift; my $ref = $pkg->{'attributes'}; return %$ref; } sub getAttr { my $pkg = shift; my $attr = shift; my $ref = $pkg->{'attributes'}; return $$ref{$attr}; } sub getClass { my $pkg = shift; return $pkg->{'class'}; } sub getContent { my $pkg = shift; my $content = $pkg->{'content'}; return $content ? $content : undef; }