Utilisation du subiterator

L'approche basée sur le subiterator ne peut fonctionner que si les types que l'on cherche à accéder sont couverts par le type couvrant au sens de UIMA, c-à-d en terme de priorité des types (cf. [la javadoc de TypePriorities] ou cet email).

Considérons une annotation A qui couvre des annotations B de la manière suivante :

Il y a du texte et les annotations sont sur ce texte ...
[-----A:1-----]   [---A:2---]  [--------A:3--------]
 [B:1] [B:2]       [B:3]        [B:4]  [B:5] [B:6]

Dans l'exemple ci-dessus, nous sommes intéressés par les annotations B couvertes par l'annotation A:3, en d'autres termes les annotations B:4, B:5 et B:6.

La méthode est la suivante :

  1. On récupère un pointeur sur l'annotation couvrante qui nous intéresse (A:3), à l'aide d'un itérateur par exemple ;
  2. On récupére l'index des annotations couvertes (les B) ;
  3. On appelle la méthode subiterator de l'index des annotations couvertes (B) en passant en paramètre l'annotation couvrante (A:3), la méthode nous retourne un itérateur sur les annotations B couvertes par A:3, soit B:4, B:5 et B:6.

Voici le code correspondant :

  1. // Récupération des index
  2. AnnotationIndex annAIdx = (AnnotationIndex) jcas.getAnnotationIndex(A.type);
  3. AnnotationIndex annBIdx = (AnnotationIndex) jcas.getAnnotationIndex(B.type);
  4. // On recherche ''A:3''
  5. FSIterator annAIt = annAIdx.iterator();
  6. while (annAIt.hasNext()) {
  7. A monA3 = (A) annAIt.next();
  8. // On récupére l'itérateur sur les annotations B couvertes par A3
  9. FSIterator annBSousA3It = annBIdx.subiterator(monA3);
  10. while (annBSousA3It.hasNext()) {
  11. // On récupére successivement B4, B5 et B6
  12. B annB = (B) annBSousA3It.next();
  13. System.out.println("Sous A3 : "+annB);
  14. }
  15. }

Utilisation des contraintes (FSMatchConstraint)

Lorsque l'on ne connaît pas les priorités des types ou bien qu'elles ne correspondent pas à ce que l'on souhaite faire, il est nécessaire de passer par un mécanisme plus complexe (mais beaucoup plus puissant) : le système de contraintes d'index.

Dans le cas présent, nous allons définir une contrainte imposant que les attributs begin et end d'une annotation d'un type donné correspondent à une certaine valeur : celle de l'annotation couvrante. Puis nous pourrons générer un itérateur qui retournera les annotations de l'index qui respectent cette contrainte.

Voici l'implémentation d'une méthode qui fait cela :

  1. /**
  2.  *
  3.  * This method provides an iterator over typed annotations that either
  4.  * have an offset embedded in that of a given annotation in a document,
  5.  * or have the same offset as these annotation.
  6.  *
  7.  * @param theDocument the document in which stand the source and
  8.  * target annotations
  9.  * @param theAnnotation the source annotation under which target
  10.  * annotations that have to be drawn out
  11.  * @param theType the type of the target annotations that have
  12.  * to be drawn out from the document under
  13.  * the source annotation
  14.  * @param isStrict the boolean that defines the offset matching,
  15.  * offsets strictly equal if isStrict is true, begin
  16.  * offsets greater or equal and end offsets less
  17.  * or equal otherwise.
  18.  * @return the iterator over the type theType annotations
  19.  * which stand under the annotation theAnnotation
  20.  * in the document theDocument
  21.  *
  22.  * @author Fabien Poulard
  23.  * @author Jérôme Rocheteau
  24.  *
  25.  * @license Apache 2.0
  26.  */
  27. public FSIterator subiterator(JCas theDocument, Annotation theAnnotation,Type theType,boolean isStrict) {
  28. // Ajout: déclaration de la variable type
  29. Type theAnnotationType = theAnnotation.getType();
  30. // On utilise le constraint factory
  31. ConstraintFactory theConstraints = theDocument.getConstraintFactory();
  32. // On définit les contraintes sur le début de l'annotation
  33. FSIntConstraint beginConstraint = theConstraints.createIntConstraint();
  34. if (isStrict) {
  35. beginConstraint.eq(theAnnotation.getBegin());
  36. } else {
  37. beginConstraint.geq(theAnnotation.getBegin());
  38. }
  39. Feature beginFeature = theAnnotationType.getFeatureByBaseName("begin");
  40. FeaturePath beginPath = theDocument.createFeaturePath();
  41. beginPath.addFeature(beginFeature);
  42. FSMatchConstraint begin = theConstraints.embedConstraint(beginPath,beginConstraint);
  43. // ... puis sur la fin de l'annotation
  44. FSIntConstraint endConstraint = theConstraints.createIntConstraint();
  45. if (isStrict) {
  46. endConstraint.eq(theAnnotation.getEnd());
  47. } else {
  48. endConstraint.leq(theAnnotation.getEnd());
  49. }
  50. Feature endFeature = theAnnotationType.getFeatureByBaseName("end");
  51. FeaturePath endPath = theDocument.createFeaturePath();
  52. endPath.addFeature(endFeature);
  53. FSMatchConstraint end = theConstraints.embedConstraint(endPath, endConstraint);
  54. // JR: on définit une contrainte sur le type d'annotation
  55. FSTypeConstraint typeConstraint = theConstraints.createTypeConstraint();
  56. typeConstraint.add(theType); //
  57. FeaturePath typePath = theDocument.createFeaturePath();
  58. FSMatchConstraint type = theConstraints.embedConstraint(typePath, typeConstraint);
  59. // On combine les contraintes
  60. FSMatchConstraint beginAndEnd = theConstraints.and(type,theConstraints.and(begin, end));
  61. // On génère un itérateur respectant ces contraintes
  62. FSIterator filteredIterator =
  63. theDocument.createFilteredIterator(theDocument.getAnnotationIndex().iterator(), beginAndEnd);
  64. return filteredIterator;
  65. }

Cette méthode prend en paramètre le JCas dans lequel travailler, l'annotation couvrante (l'annotation A3 dans l'exemple précédent), le type d'annotation qui nous intéresse (le type B pour reprendre l'exemple précédent) et un booléen qui permet de préciser si l'on souhaite une correspondance exacte ou approximative des frontières.