petitParserDom.pm

package petitParserDom;

=head1 module : petitParserDom

C'est un petit parser DOM pour XML, sommaire.

Encore en développement, créé pour intégrer un programme d'extraction/recompostion pour la gestion de traduction de document XML

La documentation sera prochainement complétée...

=head2 auteur : Raphael Schaeffer

=head1 dépendances : aucune

=head1 exemple d'utilisation

($info,$comment,$arbre)=parser(FILE);

foreach(getNoeudsParTag($arbre,"nom")){

($t)=getTexte($_);

print $$t."\n";

}

=cut

use strict;

use warnings;

use diagnostics;

use Cwd 'abs_path'; # pour reconstituer le chemin absolu d'un fichier

use utile; # pour bonneExtension

use erreur;

my %erreurLocal=( 'nonFichier' =>"erreur : l'entrée n'est pas un fichier utilisable",

'encodage' =>"erreur : l'encodage du fichier xml est inconnu",

'extension' =>"erreur : l'extension n'est pas prise en compte",

'xmlinv' =>"erreur! le fichier XLM est mal formé!",

);

erreur::maj(\%erreurLocal);

=head1 ouvre un fichier xml avec l'encodage spécifié dans l'espace de nom et renvoie un filehandle et la première ligne lue

entree : $file : string

sortie : $hdfile : référence flux

$firstLine : string

=cut

Définitions des fonctions

openXML


sub openXML{

my $file=shift;

$file = abs_path($file);

erreur::affiche('nonFichier',$file) unless -f $file;

erreur::affiche('extension',$file) unless utile::bonneExtension($file,"xml");

open IN,$file or erreur::affiche('ouvertureF',$file);

my $firstLine=<IN>;

#print $firstLine."\n";

$firstLine=~m/<\?xml.*encoding="(.*?)".*\?>/;

my $encoding=$1;

#print " $file encodage : $encoding;\n";

unless ($encoding=~/iso-8859-1/i){

($encoding=~/utf-8/i) ? binmode IN, ":utf8" : erreur::affiche('encodage',$encoding,$file);

}

return (\*IN,$firstLine);

}

=head1 parser

entrée : le fichier à analyser

sortie : $info, référence aux premières lignes d'informations

$comment, références aux commentaires en en-tête

$arbre, référence à l'arbre créé

=cut


parser


sub parser{

my $entree=shift;

my $hdfich=$entree;

my $texte="";

($hdfich,$texte)=openXML($entree) unless ref($entree);

$/=""; #slurp mode

while(<$hdfich>){$texte.=$_;}

# print "texte".$texte."\n--------------------finTexte\n";

my $fin;

my @info;

my @comment;

my $arbre;

#print "INFO\n";

while($texte=~s/^\s*(<\?[^<>]+\?>)//){

#print "info : $1\n";

#print "texte".$texte."finTexte\n";

push @info, $1;

}

#print "COMMENT\n";

while($texte=~s/^\s*<!--([^<>]+)-->\s*//){

#print "comment : $1\n";

push @comment,$1;

}

#print "PARSING\n";

($fin,$arbre)=parseNoeud($texte);

return(\@info,\@comment,$arbre);

}


parseNoeud


sub parseNoeud {

my $reste=shift;

my $balise;

my $nom; # plus pratique à utiliser dans les RI que $noeud{nom}

my %noeud;

my @champs;

$noeud{champs}=\@champs;

# cherche balise de début et la suprime du reste

erreur::affiche('xmlinv',$reste) unless ($reste=~s/^\s*<([^<>]+)>//); # s'il n'y en pas, c'est mauvais

$balise=$1;

# on en déduit le nom et les arguments

($nom,$noeud{attributs})=getArguments($balise);

$noeud{nom}=$nom;

#print "liste champs\n";

# si balise unique ex: <nom att="val"/> --> sortie

if ($balise=~m/\/$/) {

return ($reste,\%noeud);

}

# sinon on recherche tous les champs jusqu'à la balise de fin

while(1) {

# soit un commentaire

if($reste=~s/^\s*<!--([^<>]+)-->//){

my %elt=("commentaire"=>$1);

push(@champs,\%elt);

}

# soit un champs de texte

#print " reste $reste\n";

elsif ($reste=~s/^([^<>]+)//){

#print "texte $1\n";

my %elt=("texte"=>$1);

push(@champs,\%elt);

}

#(soit/puis) la balise de fin --> sortie

#print"seak : </$nom>\n";

if ($reste=~s/^\s*<\/$nom>//){

#print "fin $nom\n";

return ($reste,\%noeud);

}

# sinon c'est un noeud fils

my $fils;

($reste,$fils)=parseNoeud($reste);

my %elt=("noeud"=>$fils);

push(@champs,\%elt);

# et on recommence} } # fin newBalise

}

}

#********getArguments

# entrée : $balise : une balise de tête

# sortie : $nom : le nom de la balise

# $attributs : la référence à la table contenant les attributs


getArguments


sub getArguments {

my $balise=shift;

#print "balise : $balise\n";

my $nom;

my %attributs;

my @arguments=split(/\s+/,$balise);

# première partie : nom

$nom=shift(@arguments);

#print "nom : --$nom--\n";

# autres parties : arguments (id=>val)

#print"liste attributs\n";

foreach(@arguments){

m/^([^=]+)=['"]([^"']+)["']/;

$attributs{"$1"}="$2";

#print "attribut $1\t=\t$2\n";

}

return ($nom,\%attributs);

}


estNoeud


sub estNoeud{

my $element=shift;

return exists($element->{noeud});

}


estTexte


sub estTexte{

my $element=shift;

return exists($element->{texte});

}


estCommentaire


sub estCommentaire{

my $element=shift;

return exists($element->{commentaire});

}


getChildren


sub getChildren{

my $noeud=shift;

my @child;

my $element;

my $champs=$noeud->{champs};

foreach $element(@$champs){

if(estNoeud($element)){

push @child,$element->{noeud};

}

}

return @child;

}


getName


sub getName{

my $noeud=shift;

return \$$noeud{nom};

}


getAttributs


sub getAttributs{

my $noeud=shift;

return \$$noeud{attributs};

}


getChamps


sub getChamps{

my $noeud=shift;

return $noeud->{champs};

}

=head1 getTexte

entrée: un noeud

sortie: un tableau des références aux textes

=cut


getTexte


sub getTexte{

my $noeud=shift;

my @texte;

my $element;

foreach $element(@{$noeud->{champs}}){

if(estTexte($element)){

push @texte,\$$element{texte};

}

}

return @texte;

}


getAllName


sub getAllName{

my $noeud=shift;

my $name=getName($noeud);

my @allName;

push @allName,$name;

my @fils=getChildren($noeud);

foreach(@fils){

$name=getAllName($_);

push @allName,$name;

}

return @allName;

}


getAllAttributs


sub getAllAttributs{

my $noeud=shift;

my @att=getAttributs($noeud);

my @allAtt;

push @allAtt,@att;

my @fils=getChildren($noeud);

foreach(@fils){

@att=getAllAttributs($_);

push @allAtt,@att;

}

return @allAtt;

}


getAllTexte


sub getAllTexte{

my $noeud=shift;

my @texte=getTexte($noeud);

my @allTexte;

push @allTexte,@texte;

my @fils=getChildren($noeud);

foreach(@fils){

@texte=getAllTexte($_);

push @allTexte,@texte;

}

return @allTexte;

}

=head1 getNoeudParTag

entrée :le noeud parent

le nom des noeuds recherchés

sortie :un tableau de noeud

=cut


getNoeudsParTag


sub getNoeudsParTag{

my ($noeudParent,$tag)=@_;

my @noeuds;

my $fils;

my $nom;

# pour chaque fils

foreach $fils (getChildren($noeudParent)){

$nom=getName($fils);

# s'il correpond au tag, on l'ajoute

if($$nom eq $tag){

push @noeuds,$fils;

}

# pareil pour ses déscendants

push @noeuds,getNoeudsParTag($fils,$tag);

}

# renvoie un tableau avec tous les noeuds correpondants au tag

return @noeuds;

}

1;