<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapitre SYSTEM "../ressources/chapitre22.dtd">

<chapitre typecourssiteweb="xml">
	<cours nomfichier="fonctionsxpath">Cours d'initiation à XML</cours>
	
	<entete>
		<titre>Fonctions XPath</titre>
		<auteur>G. Chagnon</auteur>
		<resume>XPath est un langage de requête permettant à une feuille XSL d'inspecter un document XML.</resume>
		<motsclefs>XPath, fonction</motsclefs>
	</entete>
	<corpus>
		<partie titre="Introduction" ancre="xpathintro">
			<paragraphe>
				<texte>Nous avons vu dans un chapitre précédent que les <reference href="recapxsl.html">transformations XSLT</reference> font appel à une syntaxe particulière, nommée XPath, pour identifier les n&#339;uds que l'on souhaite manipuler. Ce chapitre va donner quelques fonctions utiles.</texte>
			</paragraphe>
		</partie>
		<partie titre="Fonctions XPath applicables aux n&#339;uds" ancre="nœuds">
			<section titre="Fonction de comptage : la fonction count()" ancre="count">
				<paragraphe>
					<texte>La fonction <code>count(ensemble_de_n&#339;uds)</code> permet de compter le nombre de n&#339;uds référencés. Par exemple, si dans un fichier <code type='typefichier'>XML</code> on déclare la liste des 8 planètes du système solaire sous la forme de balises <code><![CDATA[planete]]></code>, l'instruction <code>count(//planete)</code> renverra la valeur 8.</texte>
				</paragraphe>
			</section>
			<section titre="Obtenir la position d'un n&#339;ud&#160;: les fonctions position() et last()" ancre="positionlast">
				<paragraphe titre="Introduction" ancre="positionintro">
					<texte>Ces fonctions permettent de connaître la position d'un n&#339;ud par rapport à ses frères.</texte>
				</paragraphe>
				<paragraphe titre="La fonction position()" ancre="position">
					<texte>La fonction <code>position()</code> retourne la position du n&#339;ud contextuel. Sur l'exemple de système solaire, si on a classé les planètes par ordre de distance croissante au Soleil dans le fichier <code type='typefichier'>XML</code>, le code <code><![CDATA[<xsl:value-of select="planete[position()=3]/nom"/>]]></code> renvoie la valeur "Terre" (la troisième planète à partir du Soleil).</texte>
				</paragraphe>
				<paragraphe titre="La fonction last()" ancre="last">
					<texte>La fonction <code>last()</code> permet de retourner le dernier n&#339;ud d'un ensemble de n&#339;uds, c'est-à-dire la position du dernier n&#339;ud. Ainsi, le code <code><![CDATA[<xsl:value-of select="planete[position()=last()]/nom"/>]]></code> retourne la valeur "Neptune" (planète la plus éloignée du Soleil).</texte>
				</paragraphe>
			</section>
		</partie>
		<partie titre="Fonctions XPath applicables aux chaînes de caractères" ancre="caracteres">
			<section titre="Concaténation de chaînes avec la fonction concat()" ancre="concat">
				<paragraphe><texte>La fonction <code>concat()</code> concatène toutes les chaînes qui lui sont passées en arguments et retourne la chaîne résultant de cette concaténation. Sa syntaxe est <code>concat(chaine1, chaine2, ...)</code>. Par exemple, <code>&lt;xsl:value-of select concat('Le livre dont le titre est', livre/titre, ' a été écrit par ', livre/auteur)/&gt;</code> permet ainsi d'afficher la chaîne "Le livre dont le titre est Les Misérables a été écrit par Victor Hugo".</texte></paragraphe>
			</section>
			<section titre="Test de présence d'une sous-chaîne&#160;: les fonctions contains() et starts-with()" ancre="containsstarts">
				<paragraphe><texte>Ces fonctions permettent de déterminer si une chaîne de caractères est incluse dans une autre, et renvoient un booléen. La fonction <code>starts-with(chaine1, chaine2)</code> renvoie la valeur <code>true</code> si <code>chaine1</code> commence par la <code>chaine2</code>, <code>false</code> sinon. La fonction <code>contains(chaine1, chaine2)</code> renvoie <code>true</code> si <code>chaine1</code> contient <code>chaine2</code>, <code>false</code> sinon.</texte></paragraphe>
			</section>
			<section titre="Extraction de sous-chaîne" ancre="extractstring">
				<paragraphe>
					<texte>Il existe trois fonctions permettant d'extraire une sous-chaîne d'une chaîne donnée&#160;: <code>substring()</code>, <code>substring-after()</code> et <code>substring-before</code>.</texte>
				</paragraphe>
				<liste type="ordonnee">
					<item><texte>La fonction <code>substring(chaine1, decalage, longueur)</code> retourne une sous-chaîne de <code>chaine1</code> contenant <code>longueur</code> caractères et commençant à <code>decalage</code>. Par exemple, <code>substring("012345", 2, 3)</code> renvoie la chaîne "123"&#160;;</texte></item>
					<item><texte>La fonction <code>substring-after(chaine1, chaine2)</code> retourne la sous-chaîne de <code>chaine1</code> qui suit la première occurence de <code>chaine2</code>. Par exemple, <code>substring-after("012345", "2")</code> renvoie la chaîne "345"&#160;;</texte></item>
					<item><texte>La fonction <code>substring-before(chaine1, chaine2)</code> retourne la sous-chaîne de <code>chaine1</code> qui précède la première occurence de <code>chaine2</code>. Par exemple, <code>substring-before("012345", "2")</code> renvoie la chaîne "01".</texte></item>
				</liste>
			</section>
			<section titre="Transformation des caractères d'une chaîne&#160;: la fonction translate()" ancre="translate">
				<paragraphe>
					<texte>La fonction <code>translate(chaine1, chaine2, chaine3)</code> retourne <code>chaine1</code> après y avoir remplacé chaque occurence des caractères de <code>chaine2</code> par le caractère correspondant (c'est-à-dire occupant la même position) de <code>chaine3</code>. Par exemple, pour transformer une lettre quelconque et la mettre en majuscule, on utilise l'appel suivant de la fonction&#160;: <code>translate(chaine, "abcdefghijklmnopqrstuvwxyz", "ABCDEFGHIJKLMNOPQRSTUVWXYZ")</code>.</texte>
				</paragraphe>
			</section>
			<section titre="Suppression des espaces en surnombre&#160;: la fonction normalize-space()" ancre="normalizespace">
				<paragraphe>
					<texte>Il est parfois nécessaire de supprimer des espaces en surnombre dans une chaîne. La fonction <code>normalize-space(chaine)</code> retourne la chaîne de caractères qu'elle reçoit en argument après en avoir supprimé les espaces situés au début et à la fin, et y avoir remplacé chaque séquence d'espaces consécutifs par un espace unique.</texte>
				</paragraphe>
			</section>
			<section titre="Longueur d'une chaîne: la fonction string-length()" ancre="stringlength">
				<paragraphe>
					<texte>Cette fonction retourne "classiquement" la longueur de la chaîne qu'elle reçoit en argument.</texte>
				</paragraphe>
			</section>
		</partie>

		<partie titre="Fonctions XPath applicables aux nombres" ancre="xpathnombres">
			<section titre="Rappel&#160;: opérateurs disponibles" ancre="operateurs">
				<paragraphe>
					<texte>Les opérateurs suivants sont disponibles en XSLT&#160;:</texte>
					<liste>
						<item><texte><code>+</code> pour l'addition&#160;;</texte></item>
						<item><texte><code>-</code> pour la soustraction&#160;;</texte></item>
						<item><texte><code>*</code> pour la multiplication&#160;;</texte></item>
						<item><texte><code>div</code> pour la division&#160;;</texte></item>
						<item><texte><code>nombre1 mod nombre2</code> retourne le reste de la division euclidienne du premier nombre par le second.</texte></item>
					</liste>
				</paragraphe>
			</section>
			<section ancre="nombresfonctions" titre="Fonctions de manipulation">
				<paragraphe>
					<texte>Ces fonctions sont peu nombreuses, et nous les avons déjà rencontrées dans le <reference href="recapxsl.html">chapitre sur XSLT</reference>&#160;:</texte>
					<liste>
						<item><texte>La fonction <code>ceiling(nombre)</code> retourne le plus petit entier égal ou supérieur au nombre qu'elle reçoit en argument&#160;;</texte></item>
						<item><texte>La fonction <code>floor(nombre)</code> retourne le plus grand entier égal ou inférieur au nombre qu'elle reçoit en argument&#160;;</texte></item>
						<item><texte>La fonction <code>round(nombre)</code> arrondit le nombre qu'elle reçoit à l'entier le plus proche.</texte></item>
						<item><texte>La fonction <code>sum(nombre1, nombre2, ...)</code> retourne la somme des nombres reçus en argument. Cette fonction sert aussi à calculer la somme des éléments spécifiés par une expression <code type="langage">XPAth</code>&#160;: par exemple, <code>sum(//prix)</code> calcule la somme des contenus de tous les éléments <code>prix</code> du document <code type='typefichier'>XML</code>.</texte></item>
					</liste>
				</paragraphe>
			</section>
		</partie>
		
		<partie titre="Fonctions booléennes" ancre="booleens">
			<section titre="Fonctions booléennes constantes&#160;: true() et false()" ancre="truefalse">
				<paragraphe>
					<texte>Il est parfois nécessaire de créer une constante booléenne initialisée à une valeur <code>true</code> ou <code>false</code>. Cette opération est réalisable par un appel respectivement aux fonctions <code>true()</code> et <code>false()</code>.</texte>
				</paragraphe>
			</section>
			<section titre="La fonction not()" ancre="not">
				<paragraphe>
					<texte>La fonction <code>not(variable)</code> retourne l'inverse de la valeur logique de son argument.</texte>
				</paragraphe>
			</section>
			<section titre="Test de la langue&#160;: la fonction lang()" ancre="lang">
				<paragraphe>
					<texte>La fonction <code>lang(chaine)</code> vérifie que la langue dans laquelle est écrit le n&#339;ud courant (telle qu'elle est définie par l'attribut <code>xml:lang</code>) est la même que le langage qu'elle reçoit en argument. Cette fonction reçoit une chaîne correspondant à l'un des codes de langage définis dans la spécification XML&#160;: <code>en</code> pour l'anglais, <code>jp</code> pour le japonais, <code>fr</code> pour le français, <autrelangue>etc</autrelangue>. Cela permet, avec une même feuille de style, de gérer des fichiers <code type='typefichier'>XML</code> écrits dans des langues différentes.</texte>
				</paragraphe>
			</section>
			<section titre="Conversion à un booléen&#160;: la fonction boolean()" ancre="boolean">
				<paragraphe>
					<texte>La fonction <code>boolean(objet)</code> convertit son argument en valeur booléenne. Le comportement de cette fonction dépend du type XPath de son argument&#160;:</texte>
					<liste>
						<item><texte>Si l'argument est un nombre non nul et différent de <code>NaN</code>, la fonction retourne <code>true</code>&#160;; si l'argument est égal à zéro ou <code>NaN</code>, la fonction retourne <code>false</code>&#160;;</texte></item>
						<item><texte>Si l'argument est une chaîne non vide, le résultat est <code>true</code>&#160;; sinon, le résultat est <code>false</code>&#160;;</texte></item>
						<item><texte>Si l'argument est de type booléen, sa valeur reste identique&#160;;</texte></item>
						<item><texte>Si l'argument est un ensemble de n&#339;uds, le résultat est <code>true</code> sauf si l'ensemble de n&#339;uds est vide (dans ce cas, la valeur est <code>false)</code>&#160;</texte></item>
					</liste>
				</paragraphe>
			</section>
			
			<exercice titre="Utilisation des fonctions XPath" ancre="exoxpath">
				<enonce href="exercices/exo10.html"/>
				<correction href="exercices/exo10cor.html"/>
			</exercice>
		</partie>
		
		<partie titre="Autres fonctions" ancre="xpathautres">
			<section titre="Accéder à un autre document avec document()" ancre="document">
				<paragraphe titre="Introduction" ancre="documentintro">
					<texte>Il est parfois nécessaire d'accéder à un document extérieur au fichier <code type="typefichier">XML</code> en cours de transformation, pour y récupérer une information particulière. XPath le permet grâce à la fonction <code>document(chaine)</code>. Cette fonction prend comme argument une chaîne de caractères donnant le chemin vers le fichier à ouvrir. Il faut ensuite spécifier le chemin vers l'élément cherché, à partir de la racine.</texte>
				</paragraphe>
				<paragraphe titre="Exemple" ancre="exemplexpath">
					<texte>Supposons que nous soyons en train de transformer un fichier <code type="typefichier">biblio.xml</code>, contenant une liste de livres, pour lesquels on donne un auteur&#160;:</texte>
					<exemple type="XML">
						<element nom="biblio" indent="oui">
							<element nom="livre" niveau="1">(...)</element>
							<element nom="livre" niveau="1">(...)</element>
							<element nom="livre" niveau="1" indent="oui">
								<element nom="titre" niveau="2">Les Misérables</element>
								<element nom="auteur" vide="oui" niveau="2"><attribut nom="prénom">Victor</attribut><attribut nom="nom">Hugo</attribut></element>
							</element>
						</element>
					</exemple>
					<texte>En parallèle, on dispose d'un fichier <code type="typefichier">auteurs.xml</code> possédant des références biographiques sur des auteurs, dont Victor Hugo&#160;:</texte>
					<exemple type="XML">
						<element nom="biographies" indent="oui">
							<element nom="auteur" niveau="1">(...)</element>
							<element nom="auteur" niveau="1">(...)</element>
							<element nom="auteur" niveau="1" indent="oui" enligne="non">
								<attribut nom="prénom">Victor</attribut>
								<attribut nom="nomFamille">Hugo</attribut>
								<element nom="naissance" niveau="2" indent="non"><element nom="date" enligne="oui">1802</element>(...)</element>
								<element nom="décès" niveau="2" indent="non"><element nom="date" enligne="oui">1885</element>(...)</element>
								<element nom="biographie" niveau="2">(...)</element>
							</element>
						</element>
					</exemple>
					<texte>Lors de la transformation du fichier <code>biblio.xml</code>, on peut accéder à ces informations avec la commande suivante&#160;:</texte>
					<exemple type="XML">
						<element nom="xsl:variable" vide="oui"><attribut nom="name">nomAuteur</attribut><attribut nom="select">@nom</attribut></element>
						<element nom="xsl:variable" vide="oui"><attribut nom="name">prénomAuteur</attribut><attribut nom="select">@prénom</attribut></element>
						<element nom="xsl:value-of" vide="oui"><attribut nom="select">document('auteurs.xml')//auteur[@prénom=$prénomAuteur and @nomFamille=$nomAuteur]/naissance/date</attribut></element>
						<element nom="xsl:value-of" vide="oui"><attribut nom="select">document('auteurs.xml')//auteur[@prénom=$prénomAuteur and @nomFamille=$nomAuteur]/décès/date</attribut></element>
					</exemple>
				</paragraphe>
			</section>
		</partie>
	
	</corpus>
</chapitre>
