Ce billet explique comment sélectionner des annotations en appliquant des filtres sur les indexes des CASes. On prend l'exemple d'un Analysis Engine qui filtre le mot « saperlipopette » dans des textes.
La plate-forme UIMA offre des facilités pour filtrer les annotations parmi l'index d'un CAS. J'explique rapidement comment faire fonctionner ce mécanisme de filtrage en trois temps :
La partie générique du code ci-dessous correspond à la façon de créer des contraintes qui permettront d'appliquer des filtres sur les annotations. Il s'agit de créer des contraintes sur la valeur de certains traits d'annotations. Une contrainte peut-être vue avec UIMA comme la donnée d'un chemin de traits (feature path) et d'une contrainte de base (contrainte numérique entière, contrainte numérique flottante, contrainte textuelle, etc). Le mécanisme interne à UIMA consiste vérifier si la valeur du chemin de traits de chaque annotation sélectionnée satisfait effectivement la relation définie par la contrainte de base. C'est ce que fait la méthode getConstraint dans la classe ci-dessous.
La méthode getBasicConstraint renvoie la contrainte de base en fonction de la valeur et plus précisément de son type. Cette méthode s'appuie alors sur deux méthodes getIntContraint et getStringConstraint qui renvoie la contrainte de base en fonction de la relation voulue entre le chemin de traits et la valeur précédente, à savoir une relation d'égalité ou d'inégalité.
En outre, l'Analysis Engine ci-dessous définit une méthode getType qui renvoie le type à partir de son nom et une méthode getFeaturePath qui renvoie le chemin de traits correspondant au type d'annotation et au nom de ce trait.
L'Analysis Engine Filtering suivant réalise les opérations décrites ci-dessus et les met en œuvre afin d'afficher les mots « saperlipopette ». On suppose ainsi que le Type System de cet Analysis Engine contient un type d'annotation fr.free.rocheteau.jerome.Mot qui possède un trait value correspondant au texte couvert par ces mots.
public class Filtering extends JCasAnnotator_ImplBase {
private Type getType (JCas cas,String name) {
Type type = cas.getTypeSystem().getType(name);
if (type == null) {
type = cas.getTypeSystem().getType(name);
if (type == null) {
type = cas.getTypeSystem().getType(name);
if (type == null) {
String msg = "Unknown type " + name);
System.out.println(msg);
}
}
}
return type;
}
private FeaturePath getFeaturePath(JCas cas,Type type,String feat) {
Feature feature = type.getFeatureByBaseName(feat);
FeaturePath path = cas.createFeaturePath();
path.addFeature(feature);
return path;
}
private int EQ = 0;
private int LT = 1;
private int LEQ = 2;
private int GEQ = 3;
private int GT = 4;
private FSConstraint getStringConstraint(ConstraintFactory factory,int type,String string) {
FSStringConstraint constraint = factory.createStringConstraint();
if (type == this.EQ) {
constraint.equals(string);
} else {
return null;
}
return constraint;
}
private FSConstraint getIntConstraint(ConstraintFactory factory,int type,Integer integer) {
FSIntConstraint constraint = factory.createIntConstraint();
if (type == this.EQ) {
constraint.eq(integer);
} else if (type == this.LT) {
constraint.lt(integer);
} else if (type == this.LEQ) {
constraint.leq(integer);
} else if (type == this.GT) {
constraint.gt(integer);
} else if (type == this.GEQ) {
constraint.geq(integer);
} else {
return null;
}
return constraint;
}
private FSConstraint getBasicConstraint(ConstraintFactory factory,int type,Object value) {
if (value instanceof java.lang.String) {
return this.getStringConstraint(factory, type,(String) value);
} else if (value instanceof java.lang.Integer) {
return this.getIntConstraint(factory,type,(Integer) value);
} else {
return null;
}
}
private FSMatchConstraint getConstraint(JCas cas,Type type,String feat,int typ,Object value) {
ConstraintFactory factory = cas.getConstraintFactory();
FSConstraint constraint = this.getBasicConstraint(factory,typ,value);
FeaturePath path = this.getFeaturePath(cas,type,feat);
FSMatchConstraint filter = factory.embedConstraint(path,constraint);
return filter;
}
public void initialize(UimaContext context) throws ResourceInitializationException {
}
public void process(JCas cas) throws AnalysisEngineProcessException {
Type type = this.getType(cas,"fr.free.rocheteau.jerome.Mot");
FSMatchConstraint filter = this.getConstraint(cas,type,"value",this.EQ,"saperlipopette");
FSIterator iter = cas.getAnnotationIndex(type).iterator();
FSIterator res = cas.createFilteredIterator(iter,filter);
while (res.hasNext()) {
Annotation a = res.next();
System.out.println(a.toString(2));
}
}
}
Ce billet explique comment récupérer les descripteurs XML de Type Systems, Collection Readers, d'Analysis Engines, de CAS Consumers ou d'autres descripteurs XML pour des composants UIMA à partir de répertoires ou d'archives JAR contenus dans le CLASSPATH d'une application Java.
Les outils pour UIMA requiert pour l'instant que les descripteurs XML des composants se trouvent sur le système de fichiers local. Dans la terminologie UIMA, cela s'appelle « importer un composant par son emplacement » (import by location). Or, la plate-forme UIMA permet également de récupérer des composants par leur nom s'ils sont contenus dans le CLASSPATH (import by name). Malheureusement les outils UIMA ne fournissent pas (encore) cette fonctionnalité. Je propose une solution qui s'effectue en deux temps.
Tout d'abord, il s'agit récupérer la liste des éléments contenus dans le CLASSPATH, à parcourir ces éléments et à sélectionner les fichiers XML qu'ils contiennent. Seuls deux types d'élements sont traités : les archives JAR et les répertoires sur le système de fichiers local. C'est la fonction de la méthode setFiles dans l'exemple ci-dessous. Cette méthode fait appel aux deux méthodes addFile pour construire la liste des fichiers XML contenus dans les éléments du CLASSPATH.
Ensuite, il s'agit de parcourir cette liste de fichiers XML et de construire un descripteur XML d'un Analysis Engine à partir des méthodes fournies par la plate-forme UIMA. Si on réussit à obtenir un tel descripteur, on l'ajoute à la liste des Analysis Engines disponibles dans le CLASSPATH. Sinon on tente de construire un descripteur XML d'un Collection Reader qui est lui-même ajouté à la liste des Collection readers disponibles dans le CLASSPATH. C'est le rôle de la méthode setComponents dans l'exemple ci-dessous qui fait appel aux méthodes addAnalysisEngine et addCollectionReader. On a commencé par les Analysis Engines étant donné qu'il représente la majorité des descripteurs XML pour UIMA. On pourrait imaginer étendre la méthode setComponents pour détecter les Type Systems, les CAS Consumers et les autres types de composants UIMA.
public class ComponentDetection {
private List collectionReaders;
private void setCollectionReaders() {
this.collectionReaders = new ArrayList();
}
private List getCollectionReaders() {
return this.collectionReaders;
}
private void addCollectionReader(String name) throws IOException, InvalidXMLException {
XMLInputSource source = new XMLInputSource(name);
XMLParser parser = UIMAFramework.getXMLParser();
CollectionReaderDescription desc = parser.parseCollectionReaderDescription(source);
this.getCollectionReaders().add(desc);
}
private List analysisEngines;
private void setAnalysisEngines() {
this.analysisEngines = new ArrayList();
}
private List getAnalysisEngines() {
return this.analysisEngines;
}
private void addAnalysisEngine(String name) throws IOException, InvalidXMLException {
XMLInputSource source = new XMLInputSource(name);
XMLParser parser = UIMAFramework.getXMLParser();
AnalysisEngineDescription desc = parser.parseAnalysisEngineDescription(source);
this.getAnalysisEngines().add(desc);
}
private List files;
private void setFiles() {
this.files = new ArrayList();
String classPath = System.getProperty("java.class.path");
String pathSeparator = System.getProperty("path.separator");
String[] pathElements = classPath.split(pathSeparator);
for (String pathElement : pathElements) {
try {
JarFile jarFile = new JarFile(pathElement);
this.addFile(jarFile);
} catch (IOException e) {
File file = new File(pathElement);
if (file.exists()) {
if (file.isDirectory()) {
addFile(file);
}
}
} catch (SecurityException e) {
System.err.println(e.getMessage());
}
}
}
private List getFiles() {
return this.files;
}
private void addFile(JarFile jar) {
Enumeration entries = jar.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if (!entry.isDirectory()) {
String name = entry.getName();
if (name.endsWith(".xml")) {
try {
ClassLoader cl = ClassLoader.getSystemClassLoader();
URL url = cl.getResource(name);
URI uri = url.toURI();
files.add(uri);
} catch (URISyntaxException e) {
System.err.println(e.getMessage());
}
}
}
}
}
private void addFile(File file) {
if (file.isDirectory()) {
File[] files = file.listFiles();
for (File f : files) {
addFile(f);
}
} else {
String name = file.getName();
if (name.endsWith(".xml")) {
try {
URI uri = new URI(file.getAbsolutePath());
files.add(uri);
} catch (URISyntaxException e) {
System.err.println(e.getMessage());
}
}
}
}
private void setComponents() {
this.setAnalysisEngines();
this.setCollectionReaders();
for (URI file : this.getFiles()) {
String name = file.toString();
try {
this.addAnalysisEngine(name);
} catch (Exception e) {
try {
this.addCollectionReader(name);
} catch (Exception ee) { }
}
}
}
public ComponentDetection() {
this.setFiles();
this.setComponents();
}
}
<:pre> Ce billet explique comment réaliser un Analysis Engine qui calcule les occurrences des termes dans des documents avec UIMA et qui les affiche à la fin du traitement de la collection.
Nous définissons tout d'abord ce qu'est une occurrence avant de réaliser le composant qui calcule l'occurrence des termes dans un document.
La classe Java qui correspond à cette définition se nomme Occurence. Elle possède :
Le code de cette classe Java est le suivant :
private class Occurence implements Comparable{
private int id;
private String term;
private int occ;
public Occurence(int id,String term){
this.id = id;
this.term = term;
this.occ = 1;
}
public boolean incr(int id,String term) {
if (this.id == id && this.term.equals(term)) {
this.occ++;
return true;
} else {
return false;
}
}
public String toString() {
String string = "The term '" + this.term + "'";
string += " has " + this.occ + " occurences";
string += " in the " + this.id + "-th document.";
return string;
}
public int compareTo(Occurence occ) {
int diff = 0;
String fst, snd;
Integer i,j;
i = this.id;
j = occ.id;
diff = i.compareTo(j);
if (diff == 0) {
fst = this.term;
snd = occ.term;
diff = fst.compareTo(snd);
if (diff == 0) {
i = this.occ;
j = occ.occ;
diff = i.compareTo(j);
return -diff;
} else { return diff; }
} else { return diff; }
}
}
L'Analysis Engine qui calcule les occurrences de termes dans un document possède deux attributs :
Elle possède une méthode add qui incrémente le nombre d'occurrences d'un terme dans un document si celui-ci existe ou qui ajoute une occurrence de ce terme dans ce document à la liste globale.
L'initialisation de l'Analysis Engine, à l'aide de la méthode initialize, consiste à définir le nombre de document traités à 0 et de créer une liste vide pour les occurrences de termes dans les documents. Cette liste sera persistante au cours de l'analyse complète de la collection !
Le cœur de l'analyse, à l'aide de la méthode process, consiste, pour chaque document, à parcourir les annotations dont le type correspond aux termes souhaités (dans l'exemple ci-dessous il s'agit de mots caractérisés par le type d'annotation Mot) et à ajouter cette occurrence avec le texte couvert par cette annotation et l'identifiant du document en cours d'analyse.
Lorsque l'ensemble des documents de la collection est traitée, cet Analysis Engine affiche alors, à l'aide de la méthode collectionProcessComplete, les occurrences des termes dans les documents (d'où l'utilité de la méthode toString de la class Occurence) à partir de la liste globale triée (d'où la nécessité de la méthode compareTo de la classe Occurence).
La classe Java de cet Analysis Engine est le suivant :
public class OccurenceAnalyser extends JCasAnnotator_ImplBase {
private int num;
private List occurences;
private void add(int num,String term) {
boolean done = false;
for (Occurence occ : this.occurences) {
done = done || occ.incr(num,term);
}
if (!done) {
Occurence occ = new Occurence(num,term);
this.occurences.add(occ);
}
}
public void initialize(UimaContext context) throws ResourceInitializationException {
this.num = 0;
this.occurences = new ArrayList();
}
public void process(JCas cas) throws AnalysisEngineProcessException {
this.num++;
Type type = cas.getTypeSystem().getType("fr.free.rocheteau.jerome.Mot");
FSIterator iter = cas.getAnnotationIndex(type).iterator();
while (iter.hasNext()) {
Annotation annotation = iter.next();
String term = annotation.getCoveredText();
this.add(num,term);
}
}
public void collectionProcessComplete() throws AnalysisEngineProcessException {
Collections.sort(this.occurences);
for (Occurence occ : this.occurences) {
System.out.println(occ);
}
}
}
Ce billet présente deux manières d'utiliser les paramètres des composants UIMA. Ces deux types d'utilisation sont illustrés avec l'exemple d'un composant qui extrait le contenu textuel d'une ressource en fonction de son type de contenu.
Il existe principalement deux manières d'utiliser les paramètres d'un composant UIMA. Un même paramètre peut être utilisé de ces deux manières. En effet, ou bien le paramètre est utilisé par son composant « en interne » ou bien si il est utilisé « en externe » par un composant agrégé qui contient le composant précédent.
Le 1er type d'utilisation correspond au paramétrage « classique » d'un composant UIMA. Dans ce cas, le descripteur XML du composant UIMA contient, d'une part, la déclaration du paramètre et de sont type de valeur et, d'autre part, de la définition de ce paramètre à l'aide d'une paire composée du nom de ce paramètre et de sa valeur. La classe Java du composant UIMA utilise alors les informations fournies par son descripteur XML :sa méthode initialize récupère la valeur de ce paramètre tandis que la méthode process effectue des traitements en fontion de la valeur de ce paramètre.
Le second type d'utilisation consiste à déclarer et définir, dans le descripteur XML du composant UIMA, un paramètre dont la valeur ne va pas servir à la classe Java de ce composant. En revanche, ce paramètre va être accessible comme une méta-donnée de ce composant UIMA par tout composant qui l'utilisera dans une chaîne de traitement.
Le composant agrégé TextExtractor permet d'illustrer les deux utilisations possibles d'un paramètre de composants UIMA. Il s'agit d'un composant qui extrait le contenu textuel d'une vue d'un CAS et qui la sauvegarde dans une autre vue. Ce composant contient donc respectivement les deux paramètres inputView et outputView.
C'est un composant agrégé contenant quatre composants primitifs qui extrait respectivement le contenu textuel de la vue d'un CAS de type "text/plain", "application/msword", "application/pdf" et "application/rtf". Le texte extrait est alors sauvegardé dans une nouvelle vue du CAS. Ces composants primitifs contiennent donc tous trois paramètres inputView, outputView et contentTypes. Ce dernier paramètre définit la liste de type de contenu traités par chacun de ces composants primitifs et il n'est pas utilisé par les classes Java de ces composants. Le paramètre contentTypes va servir au composant agrégé TextExtractor de sélectionner le composant primitif adéquat lors du traitement d'un CAS.
Le composant TextExtractor utilise un Flow Controller qui récupère des méta-données des quatre composants primitifs le type de contenu à partir duquel le texte est extrait. Ce Flow Controller construit alors une table associant l'identifiant de ces composants primitifs et leur type de contenu traité ; cette table permet alors de sélectionner le composants primitif adéquat. La classe ContentTypeFlowController construit cette table d'association :
public class ContentTypeFlowController extends JCasFlowController_ImplBase {
private Map contentTypes = new HashMap();
private void build(Map map) {
Iterator iter = map.keySet().iterator();
while (iter.hasNext()) {
String id = (String) iter.next();
AnalysisEngineMetaData md = map.get(id);
ConfigurationParameterSettings config = md.getConfigurationParameterSettings();
String[] types = (String[]) config.getParameterValue("contentTypes");
for (String type : types) {
this.contentTypes.put(type,id);
}
}
}
public void initialize(FlowControllerContext context) throws ResourceInitializationException {
super.initialize(context);
this.build((Map) context.getAnalysisEngineMetaDataMap());
}
public Flow computeFlow(JCas cas) throws AnalysisEngineProcessException {
ContentTypeFlow flow = new ContentTypeFlow(this);
flow.setJCas(cas);
return flow;
}
}
La sélection prprement dite est effectuée par la classe ContentTypeFlow :
public class ContentTypeFlow extends JCasFlow_ImplBase {
private ContentTypeFlowController controller;
private boolean done = false;
public ContentTypeFlow(ContentTypeFlowController controller) {
this.controller = controller;
}
public Step next() throws AnalysisEngineProcessException {
if (done) {
return new FinalStep();
} else {
JCas cas = this.getJCas();
String uri = cas.getSofaDataURI();
String type = cas.getSofaMimeType();
String key = this.controller.contentTypes.get(type);
this.done = true;
return new SimpleStep(key);
}
}
}
Il suffit alors de définir le descripteur XML du composant agrégé TextExtractor ainsi :
Il s'agit de l'onglet Parameters de ce composant. À gauche sont définis les deux paramètres inputView et outputView de telle sorte que leur valeur respective est transmise à ses composants primitifs. À droite figurent les différents paramètres des composants primitifs.
Ce billet explique ce qu'est un Flow Controller et illustre son utilité à travers l'exemple d'un Flow Controller qui se base sur le langage des CASes.
Un Flow Controller est un composant UIMA qui s'intègre dans un Aggregate Analysis Engine et qui, étant donné un CAS, définit quels sont ses sous Analysis Engines qui vont s'appliquer dessus et dans quel ordre.
En effet, un Aggregate Analysis Engine se définit uniquement par un descripteur XML. d'une part, en renseignant les sous Analysis Engines qui le composent et, d'autre part, en renseignant son Flow Controller. Par défaut, le Flow Controller utilisé se nomme Fixed Flow
et consiste simplement à appliquer chaque Analysis Engine dans l'ordre spécifié par l'utilisateur dans le descripteur XML sur chaque CAS.
Un Flow Controller consiste, d'une part, à récupérer l'information sur les Analysis Engines qui compose l'Aggregate Analysis Engine et, d'autre part, à construire un Flow pour chaque CAS à traiter. Un Flow est un objet Java qui définit, à partir des informations collectées par le Flow Controller, quels sont les Analysis Engines qui vont traiter son CAS et ans quel ordre les traitements vont avoir lieu.Je prends l'exemple d'un Aggregate Analysis Engine dont les sous Analysis Engines effectuent une même sorte de traitement mais pour des langues différentes. Il peut s'agir de la traduction de textes en français. Cela suppose trois choses :
Le Flow Controller LanguageFlowController construit une liste d'association entre, d'une part, des langages et, d'autre part, l'identifiant du sous Analysis Engine qui traite ce langage. Pour cela, ce Flow Controller récupère de son contexte la liste des identifiants de chaque sous Analysis Engine. Pour chacun de ces sous Analysis Engines, il récupère de leus paramètres de configuration la valeur du paramètre Language qu'il ajoute alors dans la liste d'association des langages et des identifiant d'Analysis Engines. Enfin, ce Flow Controller construit un Flow par CAS en lui fournissant cette liste d'association des langages. En Java, cela donne :
public class LanguageFlowController extends JCasFlowController_ImplBase {
private Map<String,String> languages = new HashMa<String,String>();
public void initialize(FlowControllerContext context) throws ResourceInitializationException {
super.initialize(context);
Map<String,AnalysisEngineMetadata> map = context.getAnalysisEngineMetaDataMap();
Iterator<String> iter = map.keySet().iterator();
while (iter.hasNext()) {
String id = (String) iter.next();
AnalysisEngineMetaData md = map.get(id);
ConfigurationParameterSettings config = md.getConfigurationParameterSettings();
String language = (String) config.getParameterValue("Language");
this.languages.put(language,id);
}
}
public Flow computeFlow(JCas cas) throws AnalysisEngineProcessException {
LanguageFlow flow = new LanguageFlow(this.map);
flow.setJCas(cas);
return flow;
}
}
Le Flow construit se nomme LanguageFlow. Il consiste simplement à récupérer l'information du langage de chaque CAS et à rechercher dans la liste d'association qui lu a été fournie quel Analysis Engine traite cette langue. Si cette information existe, le Flow renvoie que le prochain traitement est effectué par le sous Analysis Engine qui correspond à l'identifiant associé au langage du CAS. Si cette information n'existe pas ou que le CAS a déjà été traduit, alors le Flow indique qu'il n'y a plus de traitement à effectuer sur son CAS. En Java, cela donne :
public class LanguageFlow extends JCasFlow_ImplBase {
private Map<String,String> languages;
private boolean done = false;
public LanguageFlow(Map<String,String> lang) {
this.languages = lang;;
}
public Step next() throws AnalysisEngineProcessException {
if (done) {
return new FinalStep();
} else {
JCas cas = this.getJCas();
String lang = cas.getDocumentLanguage();
String id = this.languages.get(lang);
if (id == null || id.isEmpty()) {
return new FinalStep();
} else {
done = true;
return new SimpleStep(id);
}
}
}
}
Il suffit alors de créer un descripteur XML de Flow Controller et de l'associer à la classe LanguageFlowController pour disposer d'un composant UIMA prêt à être intégré dans un Aggregate Analysis Engine qui puisse sélectionner le sous Analysis Engine qui correspond à la langue des CASes.
Ce billet explique comment construire un projet sous Eclipse pour développer un composant UIMA avec Maven.
Il suffit de trois étapes pour créer un projet sous Eclipse pour un composant UIMA dont la gestion des dépendances et des phases de construction est déléguée à Maven.
Il faut tout d'abord créer un projet Java : File > New > Java Project. Il faut alors indiquer le nom du projet sous Eclipse. Cette opération créée :
Il faut ensuite spécifier que le projet correspond à un composant UIMA : Project Right Click > Add UIMA Nature. Cette opération crée les répertoires conf, data, desc, doc, lib, metadata et resources.
Il faut finalement indiquer que Maven constitue le système de gestion de ce projet : Project Right Click > Maven > Enable Dependency Managment. Il faut alors indiquer :
Une telle opération crée un fichier pom.xml qui contient la configuration de Maven pour la gestion de ce projet et retire le répertoire src des répertoires source du projet. Pour que ce projet soit prêt pour développer un composant UIMA, il convient alors de modifier les fichier pom.xml :
Il faut alors spécifier que le répertoire src est un répertoire source du projet et que le répertoire bin en est le répertoire binaire étant donné que l'activation Maven les a respectivement remplacé par src/main/java et target/classes. Il faut également ajouter les répertoires desc et resources aux répertoires source du projet afin de rendre les descripteurs XML accessibles par nom. Cela s'effectue par le chemin Project > Properties > Java Build Path > Source :
Le billet « Comment configurer votre POM pour UIMA ? » explique comment configurer le fichier pom.xml à la main.
Ce billet explique comment installer et configurer Eclipse pour utiliser et développer des composants UIMA.
Eclipse est un IDE (Integrated Development Environment) qui sert à la fois :
Je conseille d'utiliser Eclipse Galileo 3.5 avec les greffons ci-dessous :
Il suffit d'installer ces greffons dans Eclipse en indiquant leurs URLs via Help > Install New Software... > Add
Attention : il est nécessaire d'avoir installé au préalable la plate-forme Apache UIMA à partir des sources de la version 2.2.2-incubating. La procédure à suivre est expliquée dans le billet « Comment installer UIMA ? ».
L'archive d'Eclipse Galileo 3.5.1 pour les développeurs Java avec les trois greffons ci-dessus est disponible dans la rubrique public/applications de ce site sous le nom « Eclipse pour UIMA » ou dans les annexes de ce billet.
Ce billet explique comment installer TextMarker un outil pour l'extraction d'information et l'analyse de textes. Cet outil s'intègre dans Eclipse et utilise notamment UIMA.
Pour installer TextMarker, il faut tout d'abord disposer :
Il faut ensuite installer automatiquement sous Eclipse les plugins suivants à l'aide du chemin Help / Install Sew Software .../ Add :
Il faut enfin installer manuellement les plugins et les features distribués par TextMarker en trois étapes :
./eclipse -clean
Ce billet explique comment récupérer les composants officiels UIMA de la Sandbox pour la version 2.2.2-incubating.
Pour récupérer les composants UIMA de la Sandbox pour la version 2.2.2-incubating, il faut disposer, d'une part, de Subversion et, d'autre part, de Maven 2. Il n'est normalement pas nécessaire que les archives Java pour la plate-forme UIMA soient installées dans le dépôt Maven local étant donné qu'elles sont récupérées lors de la compilation des composants de la Sandbox à partir d'un dépôt Maven distant.
Il suffit alors d'exécuter les commandes qui suivent :
svn checkout http://svn.apache.org/repos/asf/incubator/uima/sandbox/tags/sandbox-2.2.2/
cd sandbox-2.2.2/sandbox-2.2.2-incubating/SandboxDistr/
mvn compile
Les composants UIMA ci-dessous sont alors prêts à être utilisés dans vos chaînes de traitement. Il s'agit essentiellement de composants UIMA dédiés à l'analyse de textes :
Ce billet explique comment configurer le Project Model Object (POM) pour votre composant UIMA. Il s'agit du descripteur XML pour les projets gérés avec Maven.
Les fichiers pom.xml fournissent à Maven les informations nécessaires pour la compilation, l'installation et le déploiement de composants Java, mais aussi de composants UIMA. C'est d'ailleurs cet outil qui est utilisé pour gérer les différents composants essentiels de la plate-forme UIMA ainsi que ceux du bac-à-sable.
La structure globale d'un fichier POM correspond à ceci :
<project
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"
>
<modelVersion>4.0.0</modelVersion>
...
</project>
Il faut tout d'abord que vous renseigner l'identifiant de votre composant (ou artifact pour Maven), sa version, etc.
<groupId>fr.uima</groupId>
<artifactId>your-uima-processing-engine-id</artifactId>
<packaging>jar</packaging>
<version>1.0</version>
...
Il faut ensuite déclarer les dépendances de votre composant UIMA. Notamment, il y a de grandes chances pour qu'elles aient les dépendances suivantes afin :
<dependencies>
<!-- Required to generate the Java classes -->
<dependency>
<groupId>org.apache.uima</groupId>
<artifactId>uimaj-core</artifactId>
<version>2.2.2-incubating</version>
<scope>compile</scope>
</dependency>
<!-- Required to generate the Java sources from the XML descriptors -->
<dependency>
<groupId>org.apache.uima</groupId>
<artifactId>uimaj-tools</artifactId>
<version>2.2.2-incubating</version>
<scope>compile</scope>
</dependency>
<dependencies>
...
Il faut alors spécifier que les descripteurs XML sont des ressources du projet :
<build>
<!-- The source directory is set to src/ instead of src/java/main/ -->
<sourceDirectory>src</sourceDirectory>
<!-- The Java class output directory is set to bin/ instead of taget/classes/ -->
<outputDirectory>bin</outputDirectory>
<!-- The desc/ directoriy is added to the project resources -->
<resources>
<resource>
<directory>desc</directory>
</resource>
</resources>
<plugins>
...
</plugins>
</build>
Il faut ensuite activer la génération des classes Java à partir ces descripteurs :
<!-- The Java sources are generated from the XML desriptors in desc/ -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.1</version>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>java</goal>
</goals>
</execution>
</executions>
<configuration>
<mainClass>org.apache.uima.tools.jcasgen.Jg</mainClass>
<arguments>
<argument>-jcasgeninput</argument>
<argument>${where.your.descriptor.is}</argument>
<argument>-jcasgenoutput</argument>
<argument>${project.build.sourceDirectory}</argument>
</arguments>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.0.2</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
...
Il faut enfin éliminer les classes Java générées à partir des descripteurs XML lors du nettoyage du projet :
<!-- Clean the Java files generated by JCasGen -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-clean-plugin</artifactId>
<version>2.2</version>
<configuration>
<filesets>
<fileset>
<directory>${project.build.sourceDirectory}/${path.of.your.types}</directory>
<includes>
<include>*.class</include>
</includes>
<excludes>
<exclude>*.java</exclude>
</excludes>
<followSymlinks>false</followSymlinks>
</fileset>
</filesets>
</configuration>
</plugin>