Ce chapitre présente le processus de transformation automatique que nous avons développé dans l'éditeur Thot. Ce système de transformation se base sur la recherche des relations de massif et d'absorption entre le type des éléments à transformer et le type dans lequel ils doivent être transformés.
Le système de transformation présenté ici s'inscrit dans la continuité des travaux présentés dans [Claves 95].
L'objectif de la transformation est de trouver une relation entre les types source et cible préservant le plus possible la structure du type source.
Le système de transformation de Thot est composé de deux modules (cf. figure 37) : le premier intègre l'algorithme de recherche des relations entre les structures génériques source et cible et produit un ensemble de relations entre types d'éléments ; le second exploite ces relations pour transformer les éléments source de façon à produire l'instance cible de la transformation.
[Here is a Drawing]Figure 37 - Transformation automatique de document
Les algorithmes de comparaison d'arbres ont une complexité élevée et nécessitent une place en mémoire importante. Ils font d'autre part appel à des primitives d'accès dans les arbres entraînant un surcoût de temps d'exécution. C'est pour ces raisons que nous représentons les arbres sous forme de chaînes parenthésées qui permettent une comparaison plus efficaces par l'application d'algorithmes de pattern matching éprouvés.
La première section de ce chapitre donne quelques préliminaires à la recherche de massifs. Nous donnons ensuite l'algorithme de comparaison de types utilisé pour la recherche de relations. Nous présentons ensuite le module de transformation. Ces deux sections se basent sur une définition arborescente des types d'éléments. Pour prendre en compte la récursivité dans les définitions de types d'éléments, nous proposons dans la section 5.4.4 une extension de l'algorithme de comparaison. Une extension pour prendre en compte les absorptions spécifiques aux éléments transformés est également présentée. Nous présentons ensuite l'intégration du système de transformations dans l'éditeur Thot et les commandes d'édition qu'il permet d'implémenter ou d'améliorer. Nous terminons ce chapitre sur une discussion de la pertinence des transformations réalisées avec ce système et sur les moyens d'améliorer ce mode de transformation.
Nous présentons ici l'algorithme donné par Frilley dans [Frilley 89]. Cet algorithme implémente un automate de reconnaissance de massifs entre deux arborescences. Il se base sur une forme linéaire des arborescences étiquetées : les mots de Dyck. Le langage des mots de Dyck sur un alphabet S, noté D(S) est un langage algébrique sur l'alphabet S = S È S, où S est l'ensemble des inverses des éléments de S. Notons que chaque élément de S à un élémnt inverse unique dams S. Ce langage est défini récursivement de la façon suivante :
Frilley montre qu'il existe une bijection entre les arbres ordonnés d'une forêt d'arbres étiquetés et le mot de Dyck correspondant.
Le langage des mots de Dyck est un langage parenthésé donc un langage algébrique. Par conséquent, il est reconnu par un automate à pile. Le problème de reconnaissance de massifs peut s'écrire comme un problème d'intersection de langages algébriques.
L'automate proposé par Frilley permet la reconnaissance d'un massif (U) dans une forêt d'arbres ordonnés (V). Les données de l'automate sont deux mots de Dyck u et v, représentant respectivement les forêts U et V. Il est démontré que le langage reconnu par cet automate est non vide si et seulement si la forêt U est isomorphe à un massif de V.
L'automate de Frilley ne permet pas de reconnaître tous les massifs d'arborescence. Ainsi si l'on considère l'arborescence ordonnée représentée par le mot de dyck aabbaaccaa, l'automate ne permet pas la reconnaissance de abbaacca qui est de toute évidence un massif de l'arborescence précédente. Plus généralement l'automate proposé par Frilley ne reconnaît pas les massifs de la forme adaad'a dans une arborescence a..adaad'a..a où d et d' sont des mots de Dyck. Ceci est dû au fait que le retour sur une erreur partielle ne remet pas en cause la progression de l'indice dans la chaîne cible définissant les états de l'automate.
Pour résoudre ce problème nous proposons une modification de l'automate de Frilley permettant de rechercher une solution dans la descendance d'un noeud non reconnu en cas d'erreur partielle de l'algorithme (voir section 5.2.2).
Nous adaptons ce principe de reconnaissance à la comparaison des structures génériques des documents. Nous proposons une forme linéaire des structures génériques appelées empreintes de types. Les empreintes appartiennent au langage de Dyck sur un alphabet représentant les constructeurs de types (section 5.2.1).
L'algorithme de recherche de relation entre types d'éléments prend en entrée un type d'élément source et un type cible.
Nous commençons par définir la représentation linéaire sous forme d'empreinte des types d'éléments. Nous donnons ensuite l'automate puis l'algorithme de comparaison d'empreintes pour la recherche de relations de massif.
Nous adaptons l'algorithme de Frilley [Frilley 89] aux spécificités des arbres de types d'éléments de documents. Une spécificité de ces arbres est que les noeuds terminaux portent un ensemble d'étiquetés (les types de base) différent de celui associé aux noeuds intermédiaires (les types construits).
De plus, nous proposons une extension de l'automate pour prendre en compte la recherche de solutions dans un sous- arbre lorsque le couplage d'un noeud de l'arbre cible à été tenté mais n'a pas pu être réalisé.
Les étapes nécessaires à la génération de l'empreinte d'un type d'élément sont :
Le type d'élément tel qu'il est défini dans la DTD est d'abord mis sous forme canonique. La mise sous forme canonique d'un type se fonde sur la représentation sous forme de graphe de type et produit un arbre de types canonique.
Les opérations nécessaires à la mise sous forme canonique d'un graphe de types sont :
Les noeuds non terminaux des arbres canoniques sont étiquetés par les constructeurs choix, liste et agrégat. Les feuilles de ces arbres sont étiquetées par les types de base.
L'algorithme de recherche de massifs nécessite la définition d'un ordre sur les arbres de types sans quoi le problème de la détermination de l'existence d'une relation de massif entre deux types est indécidable [Kilpeläinen 92].
Plusieurs ordres peuvent être proposés pour prendre en compte :
La relation d'ordre sur les constructeurs est notée <c.
Le choix de la relation d'ordre associée à l'arbre de types est déterminant pour la pertinence des relations de massif trouvées par l'algorithme de comparaison d'empreintes (voir section 5.6). Pour présenter l'algorithme, nous choisissons la relation d'ordre suivante sur les types canoniques d'une structure générique S :
t < t' si et seulement si :
ou
c = c', m < n et " i Î [1,.., m], ti = t'i.
ou
c = c', $i Î [1,.., min (m, n)] tel que "j Î [1,.., i-1], tj = t'j et ti < t'i.
Cette définition implique le choix d'un ordre sur les types de base et sur les constructeurs. Pour les types de base, nous choisissons arbitrairement T (texte) < G(graphique) < S (symbole) < P (image), l'ordre des feuilles n'ayant pas de conséquence pour la comparaison. Les constructeurs sont ordonnés selon deux critères :
liste <c choix, agrégat.
choix <c agrégat.
Cet ordre a pour but de comparer prioritairement les sous-arbres comprenant le plus petit nombre de noeuds, ceci pour trouver les relations de massifs les plus « compactes » possible.
Une empreinte de type est ensuite construite à partir de l'arbre de types produit par la canonisation. Une empreinte d'un type est une chaîne de caractères contenant des parenthèses pour représenter les constructeurs de type (différentes sortes de parenthèses sont utilisées pour représenter les différents constructeurs) et des lettres pour représenter les types de base. Le parenthésage des empreintes reflète la structure générique du type. La figure 38 représente la DTD définissant le type exercice, l'arbre de type canonique correspondant et l'empreinte du type.
Dans les empreintes, le constructeur choix est représenté par
des crochets, le constructeur liste par des parenthèses, le
constructeur agrégat par des accolades. Les types de base sont
identifiés par les lettres T
pour le type texte,
G
pour le type graphique, S
pour le type
symbole mathématique, P
pour le type
image et R pour le type référence. Les autres
types de base sont représentés par des lettres minuscules pour
les types définis comme vides et par le caractère @ pour les
pseudo-types de base récurrence.
Constructeur | Liste | Choix | Agrégat | ||
---|---|---|---|---|---|
Caractère | { | [ | [ | ( | { |
Type de base | texte | graphique | symbole | image | |||
---|---|---|---|---|---|---|---|
Caractère | T | G | S | P | a, b, c... | @ |
Type de base | référence | autres | récurrence | |||||
---|---|---|---|---|---|---|---|---|
Caractère | R | T | G | S | P | a, b, c... | @ |
Un alphabet des constructeurs et des types de base est ainsi établi
: S = {'{
', '[
', '(
',
'T
', 'G
', 'S
',
'P
','a',...,'z'}, L'ensemble S des inverses est
{'}
', ']
', ')
', 'T
',
'G
', 'S
', 'P
','a',..., 'z'}. Les
applications associant un noeud de l'arbre de types au caractère
représentant son constructeur dans S sont notées
e pour les types construits et b pour les types base.
Dans les empreintes, nous confondons les caractères représentant les types de base et leur inverses. En effet, les types de base étant des feuilles des arbres de types, l'opération de réduction effectuée par les transitions M1 et M1' de l'automate présenté dans la section 5.2.2 est unifiée pour être réalisée par la transition Mt.
<!ELEMENT exercice (titre, énoncé, solution) > <!ELEMENT énoncé (p | question)+ > <!ELEMENT solution (pa)+ > <!ELEMENT titre (#PCDATA) > <!ELEMENT p (#PCDATA) > <!ELEMENT question (#PCDATA) > <!ELEMENT pa (#PCDATA | réponse) > <!ELEMENT réponse (#PCDATA) > <!ATTRLIST question ident ID #REQUIRED > <!ATTRLIST réponse ident ID #REQUIRED > <!ATTRLIST exercice niveau CDATA "1" >
{T([TT])([TT])}
Figure 38 - DTD, arbre de types et empreinte du type exercice
La définition donnée des empreintes de types est proche de celle des mots de Dyck. La différence réside dans la représentation des noeuds terminaux par un caractère unique.
L'empreinte générique d'une forêt d'arbres de types est un mot défini par les règles suivantes :
Comme pour les mots de Dyck, une bijection f
est
définie entre les arbres canoniques et les empreintes. Remarquons que
:
(si x est une feuille : F0(x) = b(x)).
Un tableau de références vers les noeuds de l'arbre canonique de types est construit simultanément à l'empreinte. Ce tableau est utilisé lors de la génération des éléments transformés pour lier un indice de l'empreinte au type canonique qu'il représente. Il implémente[Here is a formula] (voir section 5.3).
La comparaison des empreintes de types pour la recherche de massif se fait à l'aide d'un l'automate à pile.
À chaque couple d'indices (i, j) des empreintes source et destination est associé un état de l'automate. À chaque état, l'automate compare les caractères ui et vj et effectue une transition vers un nouvel état. Une pile permet de mémoriser les caractères de S rencontrés dans l'empreinte cible alors que leur image par a n'a pas encore été rencontrée.
L'état initial (1, 1) est celui associant les indices représentant deux types de plus haut niveau (deux premiers caractères des empreintes). Les états terminaux sont ceux associant l'indice de la parenthèse fermante du type source de plus haut niveau (|u|+1) avec un indice quelconque de l'empreinte destination (l'ensemble des indices de l'empreinte source à été parcouru si et seulement un massif à été trouvé). La pile contient des triplets (caractère, état de retour, compteur) définis comme suit :
La comparaison des empreintes de types pour la recherche de massif se fait à l'aide de l'automate à pile {Q, A, Y, qinit, Qfin, M} :
Y = S(v) ´ ([0, |u| + 1]N ´ [0, |v| + 1]N) ´ [0, |v| + 1]N
La transition M1 est activée lorsque les symboles source et cible coïncident entre eux et avec l'inverse du symbole en sommet de pile, le compteur étant égal a 0. Cette transition est activé lorsqu'un type et sa descendance a été reconnu. L'automate passe alors de l'état (i, j) à l'état (i+1, j+1) en dépilant. On progresse sur u et v.
M1 = {(i, j), vj, ([Here is a formula], s, 0), (i+1, j+1), e}
condition : ui = vj
La transition M'1 est déclenchée lorsque les symboles source et cible coïncident entre eux et sont des parenthèses ouvrantes. Cette transition est activée lors de la reconnaissance du constructeur d'un type, avant d'inspecter la descendance. On passe alors dans l'état (i+1, j+1) en empilant le symbole reconnu. On progresse sur u et v.
M'1 = {(i, j), vj, (y, s, k), (i+1, j+1), (y, s, k)(vj, (i, j+1), 0)}
conditions : ui = vj, y ¹ vj-1
La transition M2 est activée lorsque les symboles source et cible coïncident entre eux et sont des parenthèses fermantes. Le compteur de pile n'étant pas nul, la parenthèse ouvrante correspondante dans la cible n'a pas été reconnue (transition M'1). On passe dans l'état (i, j+1) en décrémentant le compteur de pile. On progresse sur v.
M2={(i, j), vj, ([Here is a formula], s, k), (i, j+1), ([Here is a formula], s, k-1)}
condition : k ¹ 0
Les transitions M3 et M'3 sont activées lors que le symbole cible est l'inverse du symbole de sommet de pile, les symboles source et cible étant différents. Ces transitions sont déclenchées lorsque l'algorithme n'a pas pu trouver de massif dans une sous-arborescence d'un type.
La transition M'3 mène dans l'état défini par l'indice source de l'état empilé et dont les deux indices ont été incrémentés. L'indice cible de l'état empilé est incrémenté. On progresse sur u par rapport à l'état dans lequel on a engagé la comparaison (état empilé). Cet état différencie notre automate de celui proposé par Frilley. Nous verrons dans la démonstration du théorème 1 comment cet état permet de reconnaître le massif que l'automate de Frilley ne reconnaît pas (cf. section 5.1).
M'3={(i, j), vj, ([Here is a formula], (i', j'), 0), (i'+1, j'), ([Here is a formula], (i', j'+1), 0)}
condition : ui ¹ vj
Le transition M3 est activée lorsque les transitions M'3 successives n'ont pas pu mener à une solution (l'indice j est égal à l'indice de l'état de retour empilé). Cette transition mène à l'état suivant (en j) l'état empilé. Si (i, j) est l'état courant et (i', j') l'état de retour, le nouvel état est (i, a-1(j'+1)+1). Le sommet de pile est dépilé. On progresse sur v par rapport à l'état dans lequel on a engagé la comparaison (état empilé).
M3={(i, j), vj, ([Here is a formula], (i', j'), 0), (i', a-1(j'+1)+1), e }
La transition M4 est activée lorsque les symboles source et cible ne coïncident pas et que le symbole cible est le même que le symbole stocké sur le sommet de la pile (ouvrant). Le compteur de pile est alors incrémenté et on passe à l'état (i, j+1). On progresse sur v.
M4={(i, j), vj, ([Here is a formula], s, k), (i, j+1), ([Here is a formula], s, k+1)}
condition : ui ¹ vj
La transition M5 est activée dans les autre cas, le symbole source ne correspond ni au symbole cible, ni au sommet de pile, ni à son inverse. On progresse sur v.
M5={(i, j), vj, ([Here is a formula], s, k), (i, j+1), ([Here is a formula], s, k)}
condition : ui ¹ vj, y ¹ vj-1, y ¹ vj
Pour illustrer les transitions de l'automate prenons l'exemple de la comparaison de l'empreinte du type exercice avec celle du type paragraphe donnée dans la figure 39.
<!ELEMENT paragraphe (simple|cite|groupe|liste|titré)> <!ELEMENT simple (#PCDATA) > <!ELEMENT cite (#PCDATA) > <!ELEMENT groupe (paragraphe)+ > <!ELEMENT liste (item)+ > <!ELEMENT item (paragraphe)+ > <!ELEMENT titré (titre, corps)> <!ELEMENT titre (#PCDATA) > <!ELEMENT corps (paragraph)+ >
[TT(@)((@)){T(@)}]
Figure 39 - DTD et empreinte du type Paragraphe
L'empreinte du type paragraphe contient des symboles '@' marquant les définitions récursives de types. Dans un premier temps nous considérerons des développements récursifs nécessaires à la correspondance, la section 5.4.4 présente une méthode pour traiter les types récursifs. En développant les récursions nécessaires, l'empreinte du type paragraphe devient :
[TT(@)((@)){T([TT([TT(@)((@)){T(@)}])(([TT(@)((@)){T(@)}])){T(@)}])}]
Le déroulement pas à pas de cet exemple est présenté sous la forme des 2 chaînes placées l'une sous l'autre de telle sorte que les symboles reconnus de la source soient alignés verticalement avec le symbole correspondant de la cible. Les symboles * représentent les indices de progression dans chaque empreinte. À chaque étape nous donnons également l'état de la pile.
La comparaison commence sur l'indice 1 des deux empreintes avec une pile vide :
* {T([TT])([TT])} [TT(@)((@)){T([TT([TT(@)((@)){T(@)}])(([TT(@)((@)){T(@)}])){T(@)}])}] * Pile : e
Une suite de transitions M5 mène à l'état (1,12).
* {T([TT])([TT])} [TT(@)((@)){T([TT([TT(@)((@)){T(@)}])(([TT(@)((@)){T(@)}])){T(@)}])}] * Pile : e
Ensuite, la suite de transitions M'1, Mt, M'1, M'1, Mt, Mt conduit à l'état (7, 18) en empilant les parenthèses ouvrantes rencontrées avec les états correspondant :
* {T([TT])([TT])} [TT(@)((@)){T([TT([TT(@)((@)){T(@)}])(([TT(@)((@)){T(@)}])){T(@)}])}] * Pile : ('{',2,13,0) ('(',4,15,0) ('[',5,16,0)
Une suite de transitions M5 et une transition M4 (état 7, 19) mènent à l'état (7,35), le compteur du sommet de pile n'étant pas nul, il est décrémenté (transition M2).
* {T([TT ])([TT])} [TT(@)((@)){T([TT([TT(@)((@)){T(@)}])(([TT(@)((@)){T(@)}])){T(@)}])}] * Pile : ('{',2,13,0) ('(',4,15,0) ('[',5,16,1)
Une suite de transitions M5, M4 et M2 mènent à l'état (7, 66).
* ...{T([TT ])([TT])} ...{T([TT([TT(@)((@)){T(@)}])(([TT(@)((@)){T(@)}])){T(@)}])}] * Pile : ('{',2,13,0) ('(',4,15,0) ('[',5,16,0)
Dans l'état (7, 68) le symbole cible est identique à l'inverse du symbole de sommet de pile et le compteur de pile est nul, la concordance démarrée à l'état (1, 12) est complète. Mais la transition M3 déclenchée dans l'état suivant provoque un retour à l'état (2, 13)
* ...{T([TT ])([TT])} ...{T([TT([TT(@)((@)){T(@)}])(([TT(@)((@)){T(@)}])){T(@)}])}] * Pile : ('{',2,13,0)
L'état de retour est alors changé en (2, 14) et la comparaison se poursuit.
* { T( [TT])([TT])} [TT(@)((@)){T([TT([TT(@)((@)){T(@)}])(([TT(@)((@)){T(@)}])){T(@)}])}] * Pile : ('{',2,14,0)
Après avoir reconnu une autre parenthèse et un autre crochet, la transition M1 valide la reconnaissance du type choix.
* { T ([TT ])([TT])} [TT(@)((@)){T([TT([TT(@)((@)){T(@)}])(([TT(@)((@)){T(@)}])){T(@)}])}] * Pile : ('{',2,14,0) ('(',4,20,0) ('[',5,21,0)
L'algorithme se poursuit jusqu'a ce que l'intégralité de l'empreinte source ait été parcourue (succès de la reconnaissance) ou que la pile soit vide alors que l'instance source n'a pas été entièrement parcourue (échec de la reconnaissance).
* { T ([TT ])( [TT ] ) } [TT(@)((@)){T([TT([TT(@)((@)){T(@)}])(([TT(@)((@)){T(@)}])){T(@)}])}] *
Ici la comparaison s'est terminée avec succès et une relation de massif à été trouvée entre les structures des deux types d'éléments. La figure 40 montre la mise en correspondance entre les noeuds de l'arbre de types source et l'arbre de types cible. Les noeuds de l'arbre cible présentés en rouge sont les noeuds image de la relation de massif.
[Here is a Drawing]Figure 40 - Types mis en correpondance par la transformation automatique
Nous montrons maintenant la consistance et la complétude de l'automate de comparaison d'empreintes de types non récursifs.
Désignons par L(u, v) le langage reconnu par l'automate à pile précédemment défini, Alors L(u, v) ¹ Æ si et seulement si U Í V
Nous allons faire la démonstration par récurrence sur le cardinal |v| à u fixé.
Remarquons qu'à cause des mouvements de M5, L(u,v) est non vide si et seulement s'il contient le mot v lui-même.
Remarquons également qu'en tout état (i, j) atteignable de l'automate, nécessairement i £ j. Ceci entraîne d'une part que les mouvements de M3 mènent toujours en état (h, k) tel que h < i (l'état précédent étant (i, j)) et d'autre part que si |v| < |u|, il vient |v| < |u| - 1 et par suite aucun état terminal ne peut être atteint (puisque l'automate se bloque sur un état (i, j) et que, dans ce cas, i £ j £ |v| + 1 £ |u| + 1).
Nous supposerons donc que |v| ³ |u|, ce qui permet de commencer la récurrence en |u| = |v|.
¨ Si |u| = |v|, Supposons d'abord que U Í V. Dans ce cas U º V, ce qui entraîne la succession de mouvements suivants (qui sont tous dans M1 È M1' È Mt) :
(vi R|v|-i+1(v),(i, i),( y (c, s, 0)))® (R|v|-i+1(i+1, i+1), (y Q ((c, s, 0)(vi ,(i, j) ,0)))
Les empreintes étant des chaînes parenthésées et par la relation d'absorption Q définie par les transitions M1 , M1' et Mt , l'automate arrive donc après |u| mouvements, en la configuration :
(e, (|u|+1, |u|+1), e)
Il s'agit d'un état terminal et le mot v a été reconnu donc L(u,v) ¹ Æ.
Réciproquement, si l'automate reconnaît au moins un mot w, alors il reconnaît le mot v et il est parvenu en un état terminal, celui-ci ne pouvant être que de la forme : (|u|+1, |u|+1) puisque |u| = |v|. Nous en déduisons aussi que |v| = |u|.
D'après le fonctionnement de l'automate ceci ne peut se produire que si l'on a exécuté une suite de mouvements élémentaires de la forme :
(vi R|v|-i+1(v),(i, i), (y (c, s, 0)))® (R|v|-i+1(v), (i+1, i+1), (y Q ((c, s, 0)(vi ,(i, j) ,0))
Rn(v) étant le suffixe d'ordre n de l'empreinte v.
Cette suite n'est réalisable que si, pour tout indice i, ui = vj. Ce qui permet de conclure que u = v = w et par suite que U º V (seuls les mouvements élémentaires de M1ÈM1'ÈMt font avancer simultanément i et j) et donc U Í V .
¨ Supposons le résultat établi à un rang |v| = k ³ |u| et montrons-le au rang supérieur :
Remarquons d'abord que si L(u, v) ¹ Æ, alors l'automate a réussi à atteindre un état terminal par une suite d'états q1 = (i1, j1),...,qk = (ik, jk) en reconnaissant le mot v qui est aussi dans le langage reconnu.
On construit alors l'ensemble E :
E = {qh = (ih, jh) ½ ih+1 = ih + 1 et " h' ¹ h, ih' = ih Þ jh' < jh}
Supposons qu'il existe deux couples (ih, jh) et (ih', jh') dans E tels que ih = ih' et jh ¹ jh'. Alors, par définition de E jh' < jh et par croissance des indices jh < jh' ce qui est absurde. D'autre part, pour tout indice i Î [1, |u|], il est clair qu'il existe un indice j tel que (i, j) Î E puisque l'on atteint un état terminal.
Donc E définit une application f de {1,..., |u|} dans {1,..., |v|} telle que vf(i) = ui pour tout i (en effet le mouvement effectué en l'état (i, f(i)) ne peut conduire qu'en l'état (i+1, f(i)+1), par définition de E, ce qui implique que ce mouvement est dans M1 È M1' È Mt ).
Remarquons que la suite j1,..., jk est strictement croissante par construction de l'automate, en effet le retour sur j occasionné par la transition M3' implique qu'aucun élément de E ne comprenne un indice j qui soit compris entre l'indice de départ et l'indice résultant de la transition. Donc la sous-suite des indices jh pour lesquels il existe ih tel que (ih, jh) Î E est elle-même croissante (strictement).
Supposons alors qu'il existe dans E deux couples (ih, jh) et (ih', jh') tels que ih > ih' et h < h'. Alors comme l'automate parvient en état terminal (l'état qk) il existe nécessairement un indice l > h' tel que ih = il. Comme l > h', l > h et donc nécessairement jl > jh, la suite (j1, j2,..., jk) étant croissante. Mais ceci est contradictoire avec le fait que (ih, jh) Î E. Donc la sous-suite des indices ih pour lesquels il existe jh tel que (ih, jh) Î E est croissante. Ce qui nous permet de conclure que l'application f est strictement croissante (ce qui implique d'ailleurs qu'elle est bijective sur son image).
Posons m = jV-1o
f o
jU. Par
construction de f, ui = vf(i) pour tout
i, donc, par définition des applications jU et jV,
e'(m(x)) = e(x) pour tout
xÎU.
Soit y = jV o
jV-1o
f o
jU et m =
jV-1o
y . Par construction m = m.
Analysons maintenant les mouvements de M1, M2 et M4 :
Si (ih, jh) Î E, avec uih Î C, ce couple est associé à un autre couple (ih', jh') Î E dans un mouvement de M1 (l'appartenance de ce couple à E résulte de celle de (ih, jh)). Par construction de l'automate, il vient : ih' = a-1(ih). Si le compteur de pile est nul, on a aussi exactement jh' = a-1(jh). Si le compteur n'est pas nul, cette dernière égalité n'est pas vérifié et on a seulement : jh' £ a-1(jh). Mais, si ql = (il, jl) désigne l'état qui suit exactement qh' dans la liste d'états de E, il vient aussi : jh' £ a-1(jh) £ jl (sinon il y aurait eu empilement et non incrémentation du compteur).
Or, par définition de f et de y, jh' = f(ih') et
a-1(jh) = jVo
jV-1(jh) = y (ih)
Donc comme f est croissante, y l'est aussi. Et, par suite m est aussi croissante,
Si x et y sont deux noeuds de l'arbre U comparables pour l'ordre partiel de cet arbre, alors (avec x £ y) :
jU(x) £ jU(y) £ jU(y) £ jU(x)
ce qui implique, par croissance de l'application y, que :
y(jU (x)) £ y(jU (y)) £ y(jU(y)) £ y(jU(x))
que l'on peut écrire sous la forme
jV(m(x)) £ jV(m(y)) £ jV(m(y)) £ jV(m(x))
et, comme m = m :
jV(m(x)) £ jV (m(y)) £ jV (m(y)) £ jV (m(x))
Donc m(x) et m(y) sont aussi comparables dans V et se comparent dans le même ordre.
On vérifie de même que si x et y sont incomparables dans U, alors ils restent incomparables dans V, ceci résultant de l'inégalité :
jU (x) £ jU (y) £ jU (y) £ jU (x)
Donc m est bien un isomorphisme de U sur m(U) et donc U Í V ·
Réciproquement montrons que si U Í V alors langage L (u, v) n'est pas vide.
Il eiste au moins un noeud x0 Î V tel que U Í V-{x0}. On choisit alors un tel noeud maximisant la fonction j(x0) et l'on considère l'automate construit sur les deu arbres U et V' = V - {x0}. La maimisation de la fonction j(x0) peut conduire à le détermination de x0 comme racine de l'arbre V. C'est précisément le cas dans le contre exemple de l'automate de Frilley donné en 5.1. Nous expliquons dans la suite de cette démonstration comment la transition M'3 introduite permet de traiter le cas où le 0 déterminé par maximisation est égal à la racine de V.
Nous considérons donc dans un premier temps que l'ensemble V' est bien un massif de V puisque x0 est différent de la racine.
Comme U Í V', le langage reconnu par cet automate contient au moins F (V'), celui-ci étant reconnu par une suite d'états [Here is a formula]), 1 £ h £ k. Nous considérons alors, dans l'automate construit sur U et V, la suite d'états q1 = (i1, j1), ... , qk = (ik, jk) définie par :
Nous ajoutons les deux états q et q' définis par : si h est l'indice tel que [Here is a formula]) alors [Here is a formula]) et de même, si h' est l'indice tel que [Here is a formula] alors [Here is a formula].
Il ne reste alors plus qu'à trier cette suite d'états sur l'ordre des jh.
Remarquons maintenant que, jusqu'en l'état q, tous les mouvements sont possibles car ils se produisent dans l'automate reconnaissant L (U, V'). Désignons par (i, j) l'état qui précède immédiatement l'état q dans notre liste. Le mouvement exécuté à partir de cet état ne peut pas être dans M3. S'il était dans M1 È M'1È Mt, cela impliquerait que x0 est « indispensable » pour la reconnaissance de U dans V', i.e. il n'existe pas d'autre noeud x'0 pouvant jouer le rôle avant x0 lui-même au sens de l'ordre préfixe.
Mais comme on a choisi x0 en maximisant la valeur de j (x0), cela signifierait exactement que U Ë V', ce qui est contradictoire avec l'hypothèse ou encore que x0 est racine de l'arbre V.
Dans ce dernier cas, la transition M'3 joue le rôle de « maximisation » de la fonction j(x0). Lorsque l'automate ne permet pas de trouver une solution pour un sous-arbre (vj Î S, le compteur du sommet de pile état nul et i ¹ j(qt), qt étant l'état placé en sommet de pile) la transition M'3 simule le fait que j-1(vj) soit racine de l'arbre en provoquant la comparaison de ui avec chacun de ses fils.
On parvient donc bien en l'état q et ce, par un mouvement de M4 È M5 È M'3 qui ne change pas le caractère de dessus de pile. Tout au plus, ce mouvement incrémente le compteur associé à ce caractère.
Par rapport à l'automate reconnaissant L(U, V') seule la valeur associée au caractère de sommet de pile est donc susceptible d'avoir changé. Mais ceci n'aucune influence sur les mouvements suivants car V est un arbre : le « compteur » sera nécessairement décrémenté en q'. On vérifie alors sans difficulté que l'ensemble des états que nous avons construits sont effectivement atteints. Par suite L (U, V) ¹ Æ, ce qui assure le résultat. ¨
L'automate de reconnaissance de massif est implémenté par l'algorithme donné dans la figure 41.
Cet algorithme prend en entrée deux chaînes bien parenthésées appelées respectivement u pour la chaîne représentant le type de l'élément source et v pour la chaîne représentant le type dans lequel l'élément doit être converti. L'algorithme utilise une pile pour mémoriser les parenthèses ouvrantes rencontrées au cours du déroulement de l'algorithme. Chaque élément de la pile contient trois informations : pile.symb est le symbole représentant le constructeur du type de l'élément comparé ('{', '[' ou '(') ; pile.état est l'état de retour sur erreur correspondant à l'état courant quand le type à été empilé ; pile.compte est le nombre de parenthèses identiques à pile.symb rencontrées depuis que le type a été empilé.
L'algorithme utilise également les fonctions Inverse
,
AjoutCouple
, SupprCouple
et Alpha
.
Inverse
renvoie la parenthèse fermante opposée
à celle qui lui est passée en paramètre (c'est à
dire ')' pour '(', ']' pour '[' et '}' pour '{'). La fonction
AjoutCouple(i,j)
enregistre une relation entre le type d'indice
i
dans la source et le type d'indice j
dans la
cible. La fonction SupprCouple(i1, i2)
efface les relations
concernant l'ensemble des types dont les indices dans l'empreinte source sont
compris entre i1
et i2
. La fonction
Alpha
implémente a et a-1. Elle retourne l'indice de la
parenthèse opposée au caractère dont l'indice est
passé en paramètre.
Le résultat est un ensemble de couples (type source, type cible), appelé couplage de u dans v, qui sont utilisés pour la conversion de l'instance source u.
i := 1; /* indice source */ j := 1; /* indice cible */ empiler (pile, e); /* initialisation de la pile */ tantque i <= |u| et j <= |v| faire /*boucle principale*/ si u[i] = v[j] alors si v[j] = 'T' alors /* cas 1 transition Mt : deux terminaux égaux */ AjoutCouple (i,j); i := i + 1; sinon /* v[j] != 'T'*/ si v[j] = Inverse (pile.symb) alors /* v[j] est une parenthese fermante inverse du symbole en sommet de pile */ si pile.compte = 0 alors /* cas 2, transition M1 : compte = 0 signifie que v(j) est la parenthèse fermante associée a celle du sommet de pile : le type u[i] est en relation avec v[j] transition M1 */ AjoutCouple (i, j); i := i + 1; dépiler (pile); sinon /* pile.compte != 0 */ /* cas 3, transition M2 : v[j] n'est pas au même niveau de parenthésage que le sommet de pile */ pile.compte := pile.compte - 1; finsi sinon /* v[j] != inverse (pile.symb) */ /* v[j] n'est pas l'inverse du somment de pile */ si v[j] Î ('{','[','(') alors /* cas 4, transition M1' : v[j] est une parenthèse ouvrante, on empile le type correspondant */ empiler (pile, (v[j],(i, j),0)); i := i + 1; finsi /* l'autre cas ne peut arriver car les chaînes sont bien parenthésées */ finsi finsi i := i + 1; sinon /* u[i] != v[j] */ si v[j] = Inverse (pile.symb) alors /* v[j] est une parenthese fermante inverse du symbole en sommet de pile */ si pile.compte > 0 alors /* cas 5, transition M2: v[j] correspond à un autre niveau de parenthésage que le type en sommet de pile */ pile.compte := pile.compte - 1; sinon /* pile.compte = 0 */ /* cas 6 : le compteur ne peut plus être décrémenté : la correspondance de types echoue partiellement. les couplages de tous les types de l'instance source compris entre pile.ind et i sont effacées */ si (pile.état.j < j - 1) alors /* transition M3 */ SupprCouple (pile.état.i + 1, i); i := pile.état.i + 1; /* j est incrémenté en fin de boucle */ j := pile.état.j - 1; pile.état.j := pile.état.j + 1; sinon /* transition M3' */ SupprCouple (pile.état.i, i); j := Alpha ( pile.état.j + 1); i := pile.état.i; dépiler (pile); finsi finsi finsi si v[j] = pile.symb et u[i] Ï ('}',']',')') alors /* cas 7, transition M4: v[j] est une parenthèse identique à la derniere empilée */ pile.compte := pile.compte + 1; finsi /* cas 8, transition M5: v[j] n'est ni identique ni inverse au symbole de sommet de pile, on ne fait qu'avancer dans la chaîne cible */ finsi j := j + 1; /* progression dans la chaîne cible */ fintantque si i > ½u½ alors retourner succes; sinon retourner echec; finsi
Figure 41 - Algorithme de comparaison d'empreintes.
La complexité de l'algorithme est polynômiale en O(|v|2).
Si l'on ne considère que les états Mt È M1
È M1' È M2 È M3 È M4 È M5, l
'indice j est systématiquement incrémenté par
ces état, ils peuvent donc engendrer au plus |v
|
itérations de la boucle de l'algorithme.
Considérons maintenant la transition M'3 cette transition
ne peut entraîner le parcours supplémentaire que des fils du
noeud de V correspondant à l'état (i, j) dans
lequel est déclenchée la transition. Par conséquent, au
plus |v| transitions M'3 sont déclenchée lors
de la comparaison. La comparaison des fils entraînant au plus jj - jj £
|v| transitions. Le surcoût dû à la transition
M'3 est donc |v|2. Le nombre d'itérations de la
boucle est donc limité par |v|+|v
|2. Par
conséquent la complexité de l'algorithme est O(|v|2).
Cet algorithme calcule un massif de l'arbre de types source U dans l'arbre de types cible V s'il en existe au moins un. Dans le cas de la comparaison de types de documents structurés, il peut exister un grand nombre de solutions - ceci est dû au nombre limité de constructeurs du système de types et à la forte composante textuelle des documents traités, qui implique de nombreuses correspondances possibles entre les types de base intervenant dans les structures génériques des documents.
Les relations trouvées par cet algorithme sont des relations de massif entre types non définis récursivement. Aussi, pour prendre en compte les relations d'absorption et trouver des relations entre types récursifs, qui sont fréquents dans les structures considérées (les paragraphes, les sections d'articles, les arbres sont définis récursivement), nous étendons la méthode de comparaison de types effectuée par l'algorithme de recherche de massif.
Pour optimiser le processus de transformation, nous évitons certaines comparaisons superflues. Lorsque la comparaison est effectuée entre deux types appartenant à une même structure générique, les éléments comparés peuvent être du même type. Il est, dans ce cas superflu de comparer les images de ces descendants dans les empreintes. Grâce à la table associative construite lors de la génération de l'empreinte, l'utilisation de la fonction fu-1 qui associe le noeud de l'arbre de type U à l'indice d'un symbole de l'empreinte appartenant à l'ensemble S est immédiate, on ajoute l'état M''1 à l'automate de comparaison :
M''1={((i, j), vj, (y, h, k), ((fu o fu-1(i)) + 1, j + 1), (y, h, k))}
avec la condition ui = vj, y ¹ vj-1
Cet état permet d'éviter la comparaison de la descendance de deux types égaux.
S'il existe une relation de massif M entre l'arbre canonique représentant le type d'un élément et un type cible, il est possible de convertir l'élément dans le nouveau type.
La relation de massif étant une relation injective, tous les noeuds de l'arbre de types source ont une image dans l'arbre de type cible. D'autre part, l'ascendance des éléments et les constructeurs étant préservés par cette relation, si le type t est un fils du type t' alors M(t) est un descendant de M(t').
Par conséquent, lors d'un parcours dans l'ordre préfixe de l'élément à transformer, l'image de chaque élément rencontré peut être inséré comme un descendant de l'image du parent de l'élément original.
L'algorithme de génération de l'élément cible
présenté dans la figure 43
utilise les fonctions CoupleAvec(t)
qui retourne le type
couplé avec t
, TypeElement(elem)
qui
retourne le type canonique l'élément elem
,
NouvelElem(t)
qui crée un élément du type
t
, PremierFils(elem)
qui renvoie le premier fils de
l'élément elem
, Suivant (elem)
qui
revoie l'élément suivant elem
,
InsérerFils(e1, e2)
qui insère
l'élément e1
comme fils de e2
et
InsérerDernier(e1, e2)
qui insère
l'élément e1
comme dernier fils de
e2
.
La fonction récursive Générer
parcourt
les fils de l'élément source. Le type cible couplé avec
le type de chacun des éléments source est recherché et la
descendance d'éléments depuis l'élément
parentSource
jusqu'à un élément du type
couplé. La fonction Générer
est ainsi
appliquée récursivement sur tous les éléments fils
et leurs images.
Nous illustrons ce processus dans la figure 42 qui montre comment est réalisée la transformation d'un élément répertoire en annuaire. Le type répertoire est couplé avec le type annuaire et le type entrée est couplé avec le type abonné (en vert). Lors de la transformation de l'élément de type entrée, l'élément de type annuaire a déjà été créé. L'algorithme parcourt en remontant les noeuds de l'arbre de types annuaire depuis le noeud couplé avec entrée (abonné) et crée successivement les éléments correspondants, soit abonné, commune puis départ. Le dernier noeud créé est finalement rattaché au parent qui existait préalablement (ici annuaire).
[Here is a Drawing]Figure 42 - Transformation d'un élément répertoire en annuaire.
fonction Générer (élément parentSource, élément parentCible) { typeCibleParent := TypeElement (parentCible); elemSource := PremierFils (parentSource); /* traite successivement chacun des fils du parent source */ tantque (elemSource) typeSource := TypeElement (elemSource); /* recherche le noeud couplé avec le fils courant */ typeCible = CoupleAvec (typeSource); type := typeCible; precElem := Æ; /* génère les ascendants jusqu'au type de parentCible */ tantque (type != typeCibleParent) faire elem := NouveauElem (type); si (precElem) alors InsérerFils (precElem, elem); sinon elemCible := elem; finsi precElem := elem; fintantque /* insère la descandance comme dernier fils de parentCible */ InsérerDernier (elem, parentCible); Générer (elemSource, elemCible); elemSource := Suivant (elemSource); fintantque }
Figure 43 - Algorithme de génération des éléments cible
Cette méthode de génération s'appuie sur le fait que, par construction des arbres de types, il n'existe qu'une branche entre un type canonique et un de ses ancêtres à quelque niveau que ce soit. Cependant, à cause des constructeurs liste, un élément n'est pas associé de façon unique à un type canonique. Par conséquent, la descendance entre un élément et l'un de ses descendants peut être crée de plusieurs manières, comme le montre la figure 44.
[Here is a Drawing]Figure 44 - Trois transformations possibles de répertoire en annuaire
Ces possibilités multiples de génération des éléments issus de la transformation démontre les limites de l'approche de la transformation automatique. Lorsque plusieurs solutions sont possibles, seule une connaissance additionnelle portant sur la sémantique de chaque type d'élément peut permettre un choix pertinent entre les différentes solutions. L'exemple ci-dessus montre également que les noms de types d'éléments ne permettent pas de lever l'ambiguïté qui apparaît lors de la comparaison des structures. Dans le chapitre suivant nous proposons d'intégrer un minimum de spécifications de l'utilisateur pour trancher entre les différentes possibilités qui se présentent lors de la transformation.
L'algorithme de comparaison présenté dans la section 5.2.2 est étendu pour prendre en compte les relations d'absorption et pour pouvoir traiter les types récursifs. Les deux premières extensions utilisent des formes réduites de l'empreinte source, permettant de combiner recherche de massif et recherche d'absorption. La troisième est une extension de l'ensemble des transitions de l'automate pour la prise en compte des définitions récursives des types.
La réduction de l'empreinte qui consiste à ignorer les noeuds qui ont un constructeur identique à celui de leur père ne préserve pas l'isomorphisme d'un arbre de type ni l'empreinte qui le représente. La question est de savoir si cet isomorphisme est essentiel pour pouvoir effectuer une transformation entre le type source et le type cible avec perte de structure.
Pour satisfaire la clause 2 de la définition de la relation d'absorption énoncée en 4.3.2, les noeuds de l'arbre de type source ayant un constructeur identique à celui de leur parent ne sont pas représentés dans l'empreinte. Ainsi, l'empreinte réduite du type message (figure 45) ne représente pas le type contenu car ce type à le même constructeur que son parent.
<!ELEMENT message (référence, expéditeur, destinataire, contenu) > <!ELEMENT référence (#PCDATA) > <!ELEMENT expéditeur (#PCDATA) > <!ELEMENT destinataire (#PCDATA) > <!ELEMENT contenu (titre, corps) > <!ELEMENT titre (#PCDATA) > <!ELEMENT corps (para)+ > <!ELEMENT para (#PCDATA) > <!ATTLIST message date CDATA #REQUIRED >
{TTT{T(T)}}
{TTTT(T)}
Figure 45 - DTD, arbre de types et empreintes du type message
La recherche des relations d'absorption utilise la réduction de l'empreinte source pour la comparaison. Ce procédé consiste à construire une empreinte par linéarisation de l'arbre de types en ignorant certains noeuds susceptibles d'être absorbés.
La transformation devant préserver le plus possible la structure du document source, la relation de massif est d'abord recherchée en utilisant l'empreinte générique du type source. En cas d'échec, l'empreinte réduite est utilisée.
La comparaison utilisant la forme réduite de l'empreinte source est effectuée avec le même algorithme que la comparaison des empreintes génériques présenté en 5.2.2.5.
Si l'algorithme de comparaison conclut à une relation d'équivalence entre l'empreinte réduite d'un type source t et l'empreinte d'un type cible t', alors il existe une relation d'absorption A telle que t'= A(t)
La preuve est une conséquence immédiate de l'existence d'un isomorphisme entre l'arbre de types source dans lequel les noeuds absorbables ont été supprimés (tr) et l'arbre de types cible (t'). cet isomorphisme est la conséquence de l'équivalence calculée par l'automate.
Par définition de l'empreinte réduite, tr est équivalent à t aux noeuds absorbés près, par conséquent t' est également équivalent à t aux noeuds absorbés près et les fils des parents des noeuds absorbés dans tr sont tous équivalents à leur image dans t'. Donc la condition 2 de la définition de la relation d'absorption est vérifiée.
Une spécificité des applications permettant l'édition de documents structurés est la possibilité de manipuler des documents dont la structure n'est pas entièrement instanciée (c.f. section 4.1.1). Ainsi, il est possible de définir une relation partielle de l'arbre de types canonique vers l'arbre de structure du document. La relation inverse, qui associe chaque élément avec un type canonique, est une application injective associant à chaque élément son type canonique.
Cette relation inverse est suffisante pour être utilisée par le processus de génération décrit précédemment (section 5.3). Utilisée transitivement avec la relation définie par le couplage, elle permet d'associer un type canonique de l'arbre de types cible à chaque élément de l'instance source. Nous illustrons cette idée dans la figure 46, ou les types d'éléments instanciés sont représentés en rouge dans l'arbre de types source et leurs types associés par couplage sont également représentés en rouge dans l'arbre de types cible.
[Here is a Drawing]Figure 46 - Principe de comparaison utilisant l'empreinte spécifique
Par transitivité, nous définissons une application injective de l'ensemble des éléments de l'instance source dans l'arbre des types cible. La transformation peut alors avoir lieu. On constate qu'aucun couplage n'est possible en utilisant simplement les empreintes générique et réduite du type source.
La figure 47 montre une instance de document message et l'empreinte spécifique relative à cette instance. Les types expéditeur et titre ne sont pas instanciés dans ce document, cela peut se produit lorsque au cours de l'édition l'utilisateur n'a pas encore inséré ces éléments dans la structure. Une autre situation dans laquelle certains types de la structure générique ne sont pas instanciés est la création d'un élément dont le constructeur de type est le choix. Dans ce cas, une alternative du choix est instanciée et les types définissant les autres alternatives peuvent ne pas être représentées.
<message date="30-07-1998"> <référence>sb332</référence> <destinataire> Cecile.Roisin@inrialpes.fr </destinataire> <contenu> <corps> <para>Bonjour Cécile,</para> </corps> </contenu> </message> |
|
Figure 47 - Instance et empreinte spécifique du type message
Comme l'empreinte générique, l'empreinte spécifique peut être simplifiée pour la recherche de relations d'absorption en cas d'échec de l'algorithme de comparaison.
Les types d'éléments définis de manière récursive ne peuvent pas être pris en compte par l'algorithme de comparaison défini précédemment. Ces types sont très répandus, les définitions des paragraphes et des sections d'article en sont un exemple, et un système de transformation doit permettre la transformation d'éléments entre types définis récursivement.
Dans [Claves 95] il est proposé de considérer les éléments récursifs comme des types de base, ne permettant le couplage d'un tel type qu'avec un autre type récursif.
Cette proposition a l'inconvénient de ne permettre le couplage de types définis récursivement qu'avec des types également définis récursivement. Avec cette approche, dans l'exemple de la section 5.2 les types énoncé#1 et pa n'auraient pas pu être couplés au type paragraphe, qui est défini de manière récursive (voir figure 40).
Pour résoudre cette limitation, nous proposons de développer les types récursifs lors de la génération des empreintes. Le problème de cette approche est la détermination de nombre de récursions à développer. Ce nombre peut être déterminé pour le type source en se basant sur l'instanciation des types canoniques. Par contre, la profondeur du développement de l'instance cible ne peut être déterminé facilement car la relation de massif peut impliquer des noeuds de l'arbre de types cible de profondeur supérieure à la structure source.
Nous utilisons donc ces développements pour supprimer la récursion lors de la génération de l'empreinte source : les types canoniques définis récursivement sont développés tant qu'ils sont instanciés dans la structure source du document.
En ce qui concerne l'empreinte cible, il n'est pas possible de décider a priori quelle la profondeur maximale des développements récursifs, en effet, cette profondeur dépend du massif trouvé. Pour résoudre ce problème nous décidons de représenter une occurrence de type récursif comme un type de base et nous l'associons au caractère @ dans les empreintes. Lorsque l'automate rencontre ce caractère dans l'empreinte cible, des transitions particulières lui permettent de rechercher un massif dans le développement de la récursion.
L'automate de reconnaissance est modifié pour prendre en compte ce nouveau type de base. Nous étendons la relation a qui associe à chaque valeur de l'intervalle [1, |v|] une valeur dans ce même intervalle. Soit i Î [1, |v|], et e(i) = @, alors a(i) est égal à l'indice du symbole ouvrant correspondant au type semblable dans les ascendants du type récursif :
Les transitions suivantes sont ajoutées à l'automate :
M6 = { ((i, j), @, (@, s, 0), (i, j+1), (@, s, 0))}
M6' = { ((i, j), @, e, (i, j+1), e)}
M7 = { ((i, j), @, (y, s, k), (i, a-1(j)), (y, s, k)(@, (i, j), a(j)) ) | y != @ }
M8 = { ((i, j), vj, (@, (i', j'), k), (i, j'+1), e)½j > k }
Il reste à montrer la validité de cet automate, c'est-à-dire que le langage qu'il reconnaît est non vide, ce qui implique qu'il existe une relation de massif entre le type source et le type récursif cible.
Le langage L (u, v) reconnu par l'automate à pile {Q, A, Y, qinit, Qfin, M} avec M = Mt È M1 È M1' È M2 È M3 È M'3 È M4 È M5 È M6 È M'6 È M7 È M8 est non vide si et seulement si U Í V.
Nous montrons que les états M6 È M'6 È M7 È M8 conserve la validité de l'automate de comparaison en montrant que ces transitions permettent de construire un arbre V'' par développement d'un nombre limité de récursions tel qu'il existe une relation de massif entre U et V'' reconnue par l'automate de la section 5.2.2.
Les transitions M6 et M6' impliquent que le nombre de
développements de chaque type récursif est limité
à |u|. Si R est le nombre de symboles récursifs
présents dans l'empreinte cible, la complexité de l'algorithme
est donc O(|v
|2´R|u
|) soit
O(|u
|´|v
|2).
D'un point de vue global, le processus de transformation automatique permet de trouver une correspondance entre les éléments d'un type source et les types composant un type cible. Cette comparaison se fonde sur les structures génériques et ne réclame aucune intervention de l'utilisateur.
Ces caractéristiques font que la transformation automatique de types est adaptée pour la réalisation de certaines fonctions d'édition qui nécessitent la transformation des éléments manipulés. Ces opérations peuvent ainsi être effectuées d'une façon simple nécessitant une interaction minimale avec l'utilisateur. Ainsi, dans l'éditeur Thot, la transformation automatique intervient lorsque les commandes suivantes sont effectuées :
La transformation se déroule en deux phases : l'élément source est d'abord confronté à l'ensemble des autres options proposées par le type choix. Les types dans lesquels cet élément peut être converti sont proposés à l'utilisateur sous forme de pop-up menu. Ainsi, seuls les types vers lesquels le système peut effectuer la transformation sont proposés à l'utilisateur. La transformation correspondant au choix de l'utilisateur est alors effectuée.
L'application du processus de transformation automatique à la conversion et la structuration de documents dans leur intégralité est plus délicate. La transformation automatique est d'autant plus pertinente que les structures des types d'éléments mises en jeu sont proches ce qui est souvent le cas pour les commandes mentionnées ci-dessus. La comparaison de structures génériques de documents entiers pose des problèmes d'un autre ordre : les structures de document au niveau global sont généralement très différentes et les relations de massif et d'absorption ne permettent pas de représenter les correspondances adaptées à la transformation.
Le processus de transformation automatique présenté ici permet de réaliser des restructurations d'éléments de documents sans intervention de l'utilisateur ni de spécification préalable de la méthode de transformation. Contrairement aux transformations explicites, cette méthode s'appuie sur le système de types des documents structurés et non sur la structure particulière d'un élément. Cette méthode est donc intéressante pour la manipulation de documents structurés quelle que soit leur structure générique.
Ce jugement doit cependant être nuancé car la méthode transformation automatique présente un nombre non négligeable d'inconvénients et n'est pas assez complète :
L'ambiguïté impliquée par la transformation automatique est due au mode de génération de l'instance cible et au choix de la relation d'ordre définissant le placement des fils d'un noeud dans les arbres de types préalablement à la transformation.
Une relation d'ordre sera valide pour un ensemble de transformations mais ne le sera pas pour un autre. Le résultat attendu d'une transformation dépend de la sémantique attachée à la structure et de l'utilisation que font les applications de celle-ci, plutôt que de la structure elle-même.
Le seul critère absolu qui puisse être retenu pour évaluer les transformations automatiques est la similarité entre la structure cible et le massif trouvé qui peut se mesurer par le nombre de noeuds « sautés » par cette relation. C'est pour maximiser cette similarité que nous avons choisi un ordre mettant en priorité les constructeurs des types susceptibles d'avoir une descendance importante (constructeurs liste, choix, puis agrégat).
D'autres solutions auraient été de choisir comme critère d'ordonnancement la profondeur ou le nombre de noeuds du sous-arbre de types canonique. Ces solutions auraient conduit à augmenter le nombre de types non reconnus dans les fils d'un noeud donné et, ainsi à trouver un massif plus « éparpillé » que celui retrouvé avec la relation d'ordre sur les constructeurs.
Le problème de la pertinence se pose également pour la relation d'absorption ; l'élimination de noeuds dans l'arbre de types canonique source conduit à de nombreuses combinaisons de relations de massif entre un arbre source et un arbre cible. En conséquence, la pertinence des relations trouvées souffrent de l'« aplatissement » de la structure qui introduit de nombreuses combinaisons de couplages possibles.
Cependant cette méthode de transformation est adaptée au contexte de l'édition interactive et répond aux besoins exprimés pour les commandes d'édition nécessitant des transformations, car ces commandes portent généralement sur un nombre réduit d'éléments.