Add French tokenizer (-l fr)

Based on French Wikipedia articles and talk pages

Change-Id: I3ee72d7d73e567638b6882a101baaf3b5ffee258
diff --git a/pom.xml b/pom.xml
index a160808..0b953a7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -63,6 +63,19 @@
                             <target>src/main/jflex/en</target>
                         </configuration>
                     </execution>
+                    <execution>
+                        <id>preprocess for fr</id>
+                        <phase>generate-sources</phase>
+                        <goals>
+                            <goal>preprocess</goal>
+                        </goals>
+                        <configuration>
+                            <vars>
+                                <target.language>fr</target.language>
+                            </vars>
+                            <target>src/main/jflex/fr</target>
+                        </configuration>
+                    </execution>
                 </executions>
                 <configuration>
                     <sources>src/main/jpc/jflex</sources>
diff --git a/src/main/java/de/ids_mannheim/korap/tokenizer/Main.java b/src/main/java/de/ids_mannheim/korap/tokenizer/Main.java
index 7509b46..c76c189 100644
--- a/src/main/java/de/ids_mannheim/korap/tokenizer/Main.java
+++ b/src/main/java/de/ids_mannheim/korap/tokenizer/Main.java
@@ -8,6 +8,7 @@
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.Callable;
 import java.util.stream.Collectors;
@@ -55,9 +56,7 @@
                     AnnotationInfo v = scanResult.getClassInfo(n).getAnnotationInfo(Languages.class.getName());
                     if(v != null)
                         for (AnnotationParameterValue i : v.getParameterValues()) {
-                            for (String lang : (String []) i.getValue()) {
-                                languages.add(lang);
-                            }
+                            languages.addAll(Arrays.asList((String[]) i.getValue()));
                         }
                 }
             }
diff --git a/src/main/jpc/jflex/de/ids_mannheim/korap/tokenizer/language-specific_fr.jflex-macro b/src/main/jpc/jflex/de/ids_mannheim/korap/tokenizer/language-specific_fr.jflex-macro
new file mode 100644
index 0000000..58c1333
--- /dev/null
+++ b/src/main/jpc/jflex/de/ids_mannheim/korap/tokenizer/language-specific_fr.jflex-macro
@@ -0,0 +1 @@
+SEABBR = (ALB|ART|ASBL|Abd|Abst|Abth|Adeney|Ae|Afzel|Anh|Apr|Aubl|Auk|Aver|Avg|BMJ|Backh|Bacteriol|Beibl|Benth|Bercht|Berk|Bernault|Berniker|Bif|Bj|Boiss|Bonpl|Boriss|Boud|Brabazon|Brongn|Brower|Bttr|BuNo|Burm|Cardiol|Centralbl|Ceramics|Chs|Cir|Corr|Coss|Ct|Cunn|DR|Dagbladet|Dall|Dec|Decne|Deg|Deleris|Depart|Dermatol|Deut|Disord|Dransf|Drumm|Eckl|Ehrh|Elahi|Endl|Ers|Ex|Ext|Extr|Ezek|Feb|Fisch|Fitzg|Fk|Flumin|Frapp|Gaudich|Gawl|Gesch|HZ|Handl|Hausskn|Heinem|Henn|Herrm|Hieros|Hil|Huds|Humb|Hutch|Ihlenf|Immun|Immunol|Intercep|Interp|Iss|Jer|Joba|Joc|Jum|Juss|Karst|Kfz|Kittr|Knobl|Kosterm|Kptlt|Kraenzl|KrvKpt|Kumm|Labill|Lb|Leb|Lehm|Lett|Lfg|Lond|MR|MS|Macc|Meisn|Mil|Millsp|Mirb|Moc|Monit|Movt|Mq|Muell|Nachdr|Neu|Neurophysiol|Noot|Nos|Nozaki|Np|Num|Nutr|Nyheter|Oblt|Okt|Oliv|Oncol|Orthop|Otolaryngol|Pantl|Parag|Pathol|Per|Perspect|Peruv|Poepp|Posol|Pract|Psychopharmacol|Qtd|Radlk|Rakotoarin|Rchb|Redh|Reigle|Reinw|Repr|Req|Reut|Rgt|Rheumatol|Rhop|Ridl|Rodr|Roem|SAV|Sacc|Sais|Salisb|Salvii|Salzm|Samml|Saura|Savul|Schaeff|Schellenb|Schltr|Schreb|Schuit|Schult|Schumach|Schweinf|Scribn|Seidenf|Sendtn|Sept|Serville|Settlem|Seub|Sheph|Sirvent|Sm|Speg|Spreng|Steril|Stev|Subgen|Sugim|Suguru|Summerh|Sup|Surg|Sz|Szlach|Takht|Teeuwen|Teijsm|Ther|Tiegh|Torr|Trin|Tx|VQ|Varley|Vastel|Verm|Verz|Vieill|Vieru|Virol|Vittad|Waldst|Warm|Welw|Wendl|Willd|Willk|Wiltschko|Wis|Wyl|[:letter:]|[Aa]ls|[Aa]pprox|[Aa]vr|[Cc]atal|[Cc]rim|[Dd]r|[Ff]ig|[Jj]anv|[Mm]ag|[Mm]r|[Mm]s|[Nn]o|[Nn]ov|[Nn]r|[Nn]º|[Nn]úm|[Oo]ct|[Oo]p|[Pp]ag|[Pp]g|[Pp]p|[Pp]rof|[Vv]da|[Vv]z|[Ww]z|abs|acc|alc|alt|archid|arrts|bap|barrier|blz|bret|ca|coef|cop|coul|cr|cyl|descr|diam|dim|disq|doy|emp|eq|estim|exerc|expr|fev|fl|fo|fol|fonc|frag|gest|gs|hab|hg|jan|juil|juill|kl|lleg|max|nval|orn|orth|osth|ouvr|pagg|pags|pch|perf|pgs|plur|poz|publ|pv|qu|regn|reprod|roum|sep|slo|ssp|stat|subsp|suf|tab|tch|trim|tym|ulama|uperfl|uxemb|var|vr|vv|xix|xpr|yd|zm)
diff --git a/src/test/java/de/ids_mannheim/korap/tokenizer/IPCOffsetTests.java b/src/test/java/de/ids_mannheim/korap/tokenizer/IPCOffsetTests.java
index 6a81d2f..1629d21 100644
--- a/src/test/java/de/ids_mannheim/korap/tokenizer/IPCOffsetTests.java
+++ b/src/test/java/de/ids_mannheim/korap/tokenizer/IPCOffsetTests.java
@@ -21,7 +21,7 @@
     @Parameterized.Parameters
     public static Collection<Object[]> data() {
         Collection<Object[]> testData = new ArrayList<>();
-        for (String language : new String[]{"de", "en"}) {
+        for (String language : new String[]{"de", "en", "fr"}) {
             for (String encoding : new String[]{"ascii", "latin1", "utf8"}) {
                 for (int i = 1; true; i++) {
                     URL inputUrl = IPCOffsetTests.class.getResource(String.format(testFiletemplate, language, encoding, i, "input"));
diff --git a/src/test/java/de/ids_mannheim/korap/tokenizer/TokenizerTest.java b/src/test/java/de/ids_mannheim/korap/tokenizer/TokenizerTest.java
index fc488db..8690fae 100644
--- a/src/test/java/de/ids_mannheim/korap/tokenizer/TokenizerTest.java
+++ b/src/test/java/de/ids_mannheim/korap/tokenizer/TokenizerTest.java
@@ -497,6 +497,18 @@
     }
 
     @Test
+    public void frenchTokenizerKnowsFrenchAbbreviations () {
+        DerekoDfaTokenizer_fr tok = new DerekoDfaTokenizer_fr();
+        String[] tokens = tok.tokenize("Approx. en juill. 2004 mon prof. M. Foux m'a dit qu'il faut faire exerc. no. 4, et lire pp. 27-30.");
+        assertEquals("Approx.", tokens[0]);
+        assertEquals("juill.", tokens[2]);
+        assertEquals("prof.", tokens[5]);
+        assertEquals("exerc.", tokens[13]);
+        assertEquals("no.", tokens[14]);
+        assertEquals("pp.", tokens[19]);
+    }
+
+    @Test
     public void testZipOuputArchive () {
         DerekoDfaTokenizer_de tok = new DerekoDfaTokenizer_de();
         final ByteArrayOutputStream clearOut = new ByteArrayOutputStream();
diff --git a/src/test/resources/other_test_data/test.fr.utf8.01.input.txt b/src/test/resources/other_test_data/test.fr.utf8.01.input.txt
new file mode 100644
index 0000000..b913519
--- /dev/null
+++ b/src/test/resources/other_test_data/test.fr.utf8.01.input.txt
@@ -0,0 +1 @@
+En parlant en janv. de légalité, je crois t'avoir déjà dit que je ne souhaitais plus recevoir de message de ta part, du moins tant que ces derniers ne seraient pas signés. Cela s'apparente à du harcèlement, et je te demande encore une fois d'arrêter. L'examen no. 10 de topologie est passé depuis pas mal de temps... Je ne souhaite non seulement plus recevoir de message de ta part, mais je ne souhaite d'ailleurs pas non plus que tu les envoies à qui que ce soit d'autre, que ce soit la classe, M. Festeau, le Prof. M. Dupont ou le Comité de CQFD. A bon entendeur... C'est ton choix. Il ne faudra pas venir t'en plaindre plus tard
\ No newline at end of file
diff --git a/src/test/resources/other_test_data/test.fr.utf8.01.positions.txt b/src/test/resources/other_test_data/test.fr.utf8.01.positions.txt
new file mode 100644
index 0000000..2ce7d24
--- /dev/null
+++ b/src/test/resources/other_test_data/test.fr.utf8.01.positions.txt
@@ -0,0 +1,2 @@
+0 2 3 10 11 13 14 19 20 22 23 31 31 32 33 35 36 41 42 49 50 54 55 58 59 62 63 65 66 68 69 79 80 84 85 93 94 96 97 104 105 107 108 110 111 115 115 116 117 119 120 125 126 130 131 134 135 138 139 147 148 150 151 159 160 163 164 170 170 171 172 176 177 188 189 190 191 193 194 205 205 206 207 209 210 212 213 215 216 223 224 230 231 234 235 239 240 249 249 250 251 259 260 263 264 266 267 269 270 279 280 283 284 289 290 296 297 300 301 304 305 307 308 313 313 316 317 319 320 322 323 331 332 335 336 345 346 350 351 359 360 362 363 370 371 373 374 376 377 381 381 382 383 387 388 390 391 393 394 402 403 413 414 417 418 421 422 426 427 430 431 433 434 437 438 445 446 447 448 451 452 455 456 458 459 463 464 471 471 472 473 476 477 479 480 484 485 487 488 494 494 495 496 498 499 506 506 507 508 510 511 516 517 519 520 526 527 529 530 532 533 539 540 542 543 547 547 548 549 550 551 554 555 564 564 567 568 573 574 577 578 583 583 584 585 587 588 590 591 597 598 601 602 607 608 612 613 621 622 626 627 631
+0 171 172 250 251 316 317 548 549 567 568 584 585 631
diff --git a/src/test/resources/other_test_data/test.fr.utf8.01.tokens.txt b/src/test/resources/other_test_data/test.fr.utf8.01.tokens.txt
new file mode 100644
index 0000000..10c2843
--- /dev/null
+++ b/src/test/resources/other_test_data/test.fr.utf8.01.tokens.txt
@@ -0,0 +1,132 @@
+En
+parlant
+en
+janv.
+de
+légalité
+,
+je
+crois
+t'avoir
+déjà
+dit
+que
+je
+ne
+souhaitais
+plus
+recevoir
+de
+message
+de
+ta
+part
+,
+du
+moins
+tant
+que
+ces
+derniers
+ne
+seraient
+pas
+signés
+.
+Cela
+s'apparente

+du
+harcèlement
+,
+et
+je
+te
+demande
+encore
+une
+fois
+d'arrêter
+.
+L'examen
+no.
+10
+de
+topologie
+est
+passé
+depuis
+pas
+mal
+de
+temps
+...
+Je
+ne
+souhaite
+non
+seulement
+plus
+recevoir
+de
+message
+de
+ta
+part
+,
+mais
+je
+ne
+souhaite
+d'ailleurs
+pas
+non
+plus
+que
+tu
+les
+envoies

+qui
+que
+ce
+soit
+d'autre
+,
+que
+ce
+soit
+la
+classe
+,
+M.
+Festeau
+,
+le
+Prof.
+M.
+Dupont
+ou
+le
+Comité
+de
+CQFD
+.
+A
+bon
+entendeur
+...
+C'est
+ton
+choix
+.
+Il
+ne
+faudra
+pas
+venir
+t'en
+plaindre
+plus
+tard
+