You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@opennlp.apache.org by rz...@apache.org on 2023/01/02 19:03:49 UTC

[opennlp] branch main updated: OPENNLP-1428 - Enhance DownloadUtil to avoid the use of hard-coded model urls

This is an automated email from the ASF dual-hosted git repository.

rzo1 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/opennlp.git


The following commit(s) were added to refs/heads/main by this push:
     new c270b99b OPENNLP-1428 - Enhance DownloadUtil to avoid the use of hard-coded model urls
c270b99b is described below

commit c270b99b914baedb13045c5d5eaa9b5f11c9c999
Author: Richard Zowalla <rz...@apache.org>
AuthorDate: Fri Dec 30 13:15:31 2022 +0100

    OPENNLP-1428 - Enhance DownloadUtil to avoid the use of hard-coded model urls
---
 .../main/java/opennlp/tools/util/DownloadUtil.java | 141 ++++++++++++++-------
 .../opennlp/tools/util/DownloadParserTest.java     | 114 +++++++++++++++++
 .../test/resources/opennlp/tools/util/index.html   |  99 +++++++++++++++
 3 files changed, 307 insertions(+), 47 deletions(-)

diff --git a/opennlp-tools/src/main/java/opennlp/tools/util/DownloadUtil.java b/opennlp-tools/src/main/java/opennlp/tools/util/DownloadUtil.java
index cbc36108..41aed090 100644
--- a/opennlp-tools/src/main/java/opennlp/tools/util/DownloadUtil.java
+++ b/opennlp-tools/src/main/java/opennlp/tools/util/DownloadUtil.java
@@ -17,16 +17,26 @@
 
 package opennlp.tools.util;
 
+import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.MalformedURLException;
 import java.net.URL;
+import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.nio.file.StandardCopyOption;
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
+import java.util.Objects;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
+import opennlp.tools.commons.Internal;
 import opennlp.tools.util.model.BaseModel;
 
 /**
@@ -55,55 +65,14 @@ public class DownloadUtil {
   private static final String BASE_URL = "https://dlcdn.apache.org/opennlp/";
   private static final String MODELS_UD_MODELS_1_0 = "models/ud-models-1.0/";
 
-  public static final Map<String, Map<ModelType, String>> available_models = new HashMap<>();
+  public static final Map<String, Map<ModelType, String>> available_models;
 
   static {
-
-    final Map<ModelType, String> frenchModels = new HashMap<>();
-    frenchModels.put(ModelType.SENTENCE_DETECTOR,
-        BASE_URL + MODELS_UD_MODELS_1_0 + "opennlp-1.0-1.9.3fr-ud-ftb-sentence-1.0-1.9.3.bin");
-    frenchModels.put(ModelType.POS,
-        BASE_URL + MODELS_UD_MODELS_1_0 + "opennlp-fr-ud-ftb-pos-1.0-1.9.3.bin");
-    frenchModels.put(ModelType.TOKENIZER,
-        BASE_URL + MODELS_UD_MODELS_1_0 + "opennlp-fr-ud-ftb-tokens-1.0-1.9.3.bin");
-    available_models.put("fr", frenchModels);
-
-    final Map<ModelType, String> germanModels = new HashMap<>();
-    germanModels.put(ModelType.SENTENCE_DETECTOR,
-        BASE_URL + MODELS_UD_MODELS_1_0 + "opennlp-de-ud-gsd-sentence-1.0-1.9.3.bin");
-    germanModels.put(ModelType.POS,
-        BASE_URL + MODELS_UD_MODELS_1_0 + "opennlp-de-ud-gsd-pos-1.0-1.9.3.bin");
-    germanModels.put(ModelType.TOKENIZER,
-        BASE_URL + MODELS_UD_MODELS_1_0 + "opennlp-de-ud-gsd-tokens-1.0-1.9.3.bin");
-    available_models.put("de", germanModels);
-
-    final Map<ModelType, String> englishModels = new HashMap<>();
-    englishModels.put(ModelType.SENTENCE_DETECTOR,
-        BASE_URL + MODELS_UD_MODELS_1_0 + "opennlp-en-ud-ewt-sentence-1.0-1.9.3.bin");
-    englishModels.put(ModelType.POS,
-        BASE_URL + MODELS_UD_MODELS_1_0 + "opennlp-en-ud-ewt-pos-1.0-1.9.3.bin");
-    englishModels.put(ModelType.TOKENIZER,
-        BASE_URL + MODELS_UD_MODELS_1_0 + "opennlp-en-ud-ewt-tokens-1.0-1.9.3.bin");
-    available_models.put("en", englishModels);
-
-    final Map<ModelType, String> italianModels = new HashMap<>();
-    italianModels.put(ModelType.SENTENCE_DETECTOR,
-        BASE_URL + MODELS_UD_MODELS_1_0 + "opennlp-it-ud-vit-sentence-1.0-1.9.3.bin");
-    italianModels.put(ModelType.POS,
-        BASE_URL + MODELS_UD_MODELS_1_0 + "opennlp-it-ud-vit-pos-1.0-1.9.3.bin");
-    italianModels.put(ModelType.TOKENIZER,
-        BASE_URL + MODELS_UD_MODELS_1_0 + "opennlp-it-ud-vit-tokens-1.0-1.9.3.bin");
-    available_models.put("it", italianModels);
-
-    final Map<ModelType, String> dutchModels = new HashMap<>();
-    dutchModels.put(ModelType.SENTENCE_DETECTOR,
-        BASE_URL + MODELS_UD_MODELS_1_0 + "opennlp-nl-ud-alpino-sentence-1.0-1.9.3.bin");
-    dutchModels.put(ModelType.POS,
-        BASE_URL + MODELS_UD_MODELS_1_0 + "opennlp-nl-ud-alpino-pos-1.0-1.9.3.bin");
-    dutchModels.put(ModelType.TOKENIZER,
-        BASE_URL + MODELS_UD_MODELS_1_0 + "opennlp-nl-ud-alpino-tokens-1.0-1.9.3.bin");
-    available_models.put("nl", dutchModels);
-
+    try {
+      available_models = new DownloadParser(new URL(BASE_URL + MODELS_UD_MODELS_1_0)).getAvailableModels();
+    } catch (MalformedURLException e) {
+      throw new RuntimeException(e);
+    }
   }
 
   /**
@@ -174,4 +143,82 @@ public class DownloadUtil {
     }
   }
 
+  @Internal
+  static class DownloadParser {
+
+    private static final Pattern LINK_PATTERN = Pattern.compile("<a href=\\\"(.*?)\\\">(.*?)</a>", Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
+    private final URL indexUrl;
+
+    DownloadParser(URL indexUrl) {
+      Objects.requireNonNull(indexUrl);
+      this.indexUrl = indexUrl;
+    }
+
+    Map<String, Map<ModelType, String>> getAvailableModels() {
+
+      final Matcher matcher = LINK_PATTERN.matcher(fetchPageIndex());
+
+      final List<String> links = new ArrayList<>();
+      while (matcher.find()) {
+        links.add(matcher.group(1));
+      }
+
+      return toMap(links);
+    }
+
+    private Map<String, Map<ModelType, String>> toMap(List<String> links) {
+
+      final Map<String, Map<ModelType, String>> result = new HashMap<>();
+
+      for (String link : links) {
+
+        if (link.endsWith(".bin")) {
+          if (link.contains("de-ud")) {
+            addModel("de", link, result);
+          } else if (link.contains("en-ud")) {
+            addModel("en", link, result);
+          } else if (link.contains("it-ud")) {
+            addModel("it", link, result);
+          } else if (link.contains("nl-ud")) {
+            addModel("nl", link, result);
+          } else if (link.contains("fr-ud")) {
+            addModel("fr", link, result);
+          }
+        }
+
+      }
+
+      return result;
+    }
+
+    private void addModel(String locale, String link, Map<String, Map<ModelType, String>> result) {
+      final Map<ModelType, String> models = result.getOrDefault(locale, new HashMap<>());
+      final String url = (indexUrl.toString().endsWith("/") ? indexUrl : indexUrl + "/") + link;
+
+      if (link.contains("sentence")) {
+        models.put(ModelType.SENTENCE_DETECTOR, url);
+      } else if (link.contains("tokens")) {
+        models.put(ModelType.TOKENIZER, url);
+      } else if (link.contains("pos")) {
+        models.put(ModelType.POS, url);
+      }
+
+      result.putIfAbsent(locale, models);
+    }
+
+    private String fetchPageIndex() {
+      final StringBuilder html = new StringBuilder();
+      try (BufferedReader br = new BufferedReader(
+          new InputStreamReader(indexUrl.openStream(), StandardCharsets.UTF_8))) {
+        String line;
+        while ((line = br.readLine()) != null) {
+          html.append(line);
+        }
+      } catch (IOException e) {
+        System.err.println("Could not read page index from " + indexUrl);
+      }
+
+      return html.toString();
+    }
+  }
 }
diff --git a/opennlp-tools/src/test/java/opennlp/tools/util/DownloadParserTest.java b/opennlp-tools/src/test/java/opennlp/tools/util/DownloadParserTest.java
new file mode 100644
index 00000000..0d62e69b
--- /dev/null
+++ b/opennlp-tools/src/test/java/opennlp/tools/util/DownloadParserTest.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package opennlp.tools.util;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Map;
+import java.util.stream.Stream;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class DownloadParserTest {
+
+  @ParameterizedTest(name = "Verify \"{0}\" available models")
+  @MethodSource(value = "expectedModels")
+  void testAvailableModels(String language, Map<DownloadUtil.ModelType, String> expectedModels) {
+
+    final URL baseUrl = fromClasspath("opennlp/tools/util/index.html");
+    assertNotNull(baseUrl);
+
+    final DownloadUtil.DownloadParser downloadParser = new DownloadUtil.DownloadParser(baseUrl);
+
+    Map<String, Map<DownloadUtil.ModelType, String>> result = downloadParser.getAvailableModels();
+
+    assertNotNull(result);
+    assertEquals(5, result.size());
+
+    final Map<DownloadUtil.ModelType, String> availableModels = result.get(language);
+    assertNotNull(availableModels);
+
+    for (Map.Entry<DownloadUtil.ModelType, String> e : expectedModels.entrySet()) {
+      final String url = availableModels.get(e.getKey());
+      final String expectedUrl = baseUrl + "/" + e.getValue();
+
+      assertNotNull(url, "A model for the given model type is expected");
+      assertEquals(expectedUrl, url);
+    }
+  }
+
+  @Test
+  void testNullUrl() {
+    assertThrows(NullPointerException.class, () -> {
+          new DownloadUtil.DownloadParser(null);
+        }
+    );
+  }
+
+  @Test
+  void testInvalidUrl() throws MalformedURLException {
+    final DownloadUtil.DownloadParser downloadParser =
+        new DownloadUtil.DownloadParser(new URL("file:/this/does/not/exist"));
+    Map<String, Map<DownloadUtil.ModelType, String>> result = downloadParser.getAvailableModels();
+    assertNotNull(result);
+    assertEquals(0, result.size());
+  }
+
+  private URL fromClasspath(String file) {
+    return Thread.currentThread().getContextClassLoader().getResource(file);
+  }
+
+  // Note: This needs to be public as JUnit 5 requires it like this.
+  public static Stream<Arguments> expectedModels() {
+    // Data as defined in "test/resources/opennlp/tools/util/index.html"
+    return Stream.of(
+        Arguments.of("en",
+            Map.of(
+                DownloadUtil.ModelType.SENTENCE_DETECTOR, "opennlp-en-ud-ewt-sentence-1.0-1.9.3.bin",
+                DownloadUtil.ModelType.TOKENIZER, "opennlp-en-ud-ewt-tokens-1.0-1.9.3.bin",
+                DownloadUtil.ModelType.POS, "opennlp-en-ud-ewt-pos-1.0-1.9.3.bin")),
+        Arguments.of("fr",
+            Map.of(
+                DownloadUtil.ModelType.SENTENCE_DETECTOR, "opennlp-1.0-1.9.3fr-ud-ftb-sentence-1.0-1.9.3.bin",
+                DownloadUtil.ModelType.TOKENIZER, "opennlp-fr-ud-ftb-tokens-1.0-1.9.3.bin",
+                DownloadUtil.ModelType.POS, "opennlp-fr-ud-ftb-pos-1.0-1.9.3.bin")),
+        Arguments.of("de",
+            Map.of(
+                DownloadUtil.ModelType.SENTENCE_DETECTOR, "opennlp-de-ud-gsd-sentence-1.0-1.9.3.bin",
+                DownloadUtil.ModelType.TOKENIZER, "opennlp-de-ud-gsd-tokens-1.0-1.9.3.bin",
+                DownloadUtil.ModelType.POS, "opennlp-de-ud-gsd-pos-1.0-1.9.3.bin")),
+        Arguments.of("it",
+            Map.of(
+                DownloadUtil.ModelType.SENTENCE_DETECTOR, "opennlp-it-ud-vit-sentence-1.0-1.9.3.bin",
+                DownloadUtil.ModelType.TOKENIZER, "opennlp-it-ud-vit-tokens-1.0-1.9.3.bin",
+                DownloadUtil.ModelType.POS, "opennlp-it-ud-vit-pos-1.0-1.9.3.bin")),
+        Arguments.of("nl",
+            Map.of(
+                DownloadUtil.ModelType.SENTENCE_DETECTOR, "opennlp-nl-ud-alpino-sentence-1.0-1.9.3.bin",
+                DownloadUtil.ModelType.TOKENIZER, "opennlp-nl-ud-alpino-tokens-1.0-1.9.3.bin",
+                DownloadUtil.ModelType.POS, "opennlp-nl-ud-alpino-pos-1.0-1.9.3.bin"))
+    );
+  }
+}
diff --git a/opennlp-tools/src/test/resources/opennlp/tools/util/index.html b/opennlp-tools/src/test/resources/opennlp/tools/util/index.html
new file mode 100644
index 00000000..fd77cfd6
--- /dev/null
+++ b/opennlp-tools/src/test/resources/opennlp/tools/util/index.html
@@ -0,0 +1,99 @@
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one
+   or more contributor license agreements.  See the NOTICE file
+   distributed with this work for additional information
+   regarding copyright ownership.  The ASF licenses this file
+   to you under the Apache License, Version 2.0 (the
+   "License"); you may not use this file except in compliance
+   with the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing,
+   software distributed under the License is distributed on an
+   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+   KIND, either express or implied.  See the License for the
+   specific language governing permissions and limitations
+   under the License.
+-->
+<!--
+This is a copy of https://dlcdn.apache.org/opennlp/models/ud-models-1.0/ used for testing the DownloadParser
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+ <head>
+  <title>Index of /opennlp/models/ud-models-1.0</title>
+ </head>
+ <body>
+<h1>Index of /opennlp/models/ud-models-1.0</h1>
+<pre><img src="/icons/blank.gif" alt="Icon "> <a href="?C=N;O=D">Name</a>                                                     <a href="?C=M;O=A">Last modified</a>      <a href="?C=S;O=A">Size</a>  <a href="?C=D;O=A">Description</a><hr><img src="/icons/back.gif" alt="[PARENTDIR]"> <a href="/opennlp/models/">Parent Directory</a>                                                              -   
+<img src="/icons/unknown.gif" alt="[   ]"> <a href="CHANGES">CHANGES</a>                                                  2021-05-29 13:55  581   
+<img src="/icons/unknown.gif" alt="[   ]"> <a href="LICENSE">LICENSE</a>                                                  2021-05-29 13:55   13K  
+<img src="/icons/unknown.gif" alt="[   ]"> <a href="NOTICE">NOTICE</a>                                                   2021-05-29 13:55  376   
+<img src="/icons/binary.gif" alt="[   ]"> <a href="opennlp-1.0-1.9.3fr-ud-ftb-sentence-1.0-1.9.3.bin">opennlp-1.0-1.9.3fr-ud-ftb-sentence-1.0-1.9.3.bin</a>        2021-05-29 13:55  846   
+<img src="/icons/text.gif" alt="[TXT]"> <a href="opennlp-1.0-1.9.3fr-ud-ftb-sentence-1.0-1.9.3.bin.asc">opennlp-1.0-1.9.3fr-ud-ftb-sentence-1.0-1.9.3.bin.asc</a>    2021-05-29 13:55  833   
+<img src="/icons/text.gif" alt="[TXT]"> <a href="opennlp-1.0-1.9.3fr-ud-ftb-sentence-1.0-1.9.3.bin.sha256">opennlp-1.0-1.9.3fr-ud-ftb-sentence-1.0-1.9.3.bin.sha256</a> 2021-05-29 13:55  118   
+<img src="/icons/text.gif" alt="[TXT]"> <a href="opennlp-1.0-1.9.3fr-ud-ftb-sentence-1.0-1.9.3.bin.sha512">opennlp-1.0-1.9.3fr-ud-ftb-sentence-1.0-1.9.3.bin.sha512</a> 2021-05-29 13:55  182   
+<img src="/icons/binary.gif" alt="[   ]"> <a href="opennlp-de-ud-gsd-pos-1.0-1.9.3.bin">opennlp-de-ud-gsd-pos-1.0-1.9.3.bin</a>                      2021-05-29 13:55  1.2M  
+<img src="/icons/text.gif" alt="[TXT]"> <a href="opennlp-de-ud-gsd-pos-1.0-1.9.3.bin.asc">opennlp-de-ud-gsd-pos-1.0-1.9.3.bin.asc</a>                  2021-05-29 13:55  833   
+<img src="/icons/text.gif" alt="[TXT]"> <a href="opennlp-de-ud-gsd-pos-1.0-1.9.3.bin.sha256">opennlp-de-ud-gsd-pos-1.0-1.9.3.bin.sha256</a>               2021-05-29 13:55  104   
+<img src="/icons/text.gif" alt="[TXT]"> <a href="opennlp-de-ud-gsd-pos-1.0-1.9.3.bin.sha512">opennlp-de-ud-gsd-pos-1.0-1.9.3.bin.sha512</a>               2021-05-29 13:55  168   
+<img src="/icons/binary.gif" alt="[   ]"> <a href="opennlp-de-ud-gsd-sentence-1.0-1.9.3.bin">opennlp-de-ud-gsd-sentence-1.0-1.9.3.bin</a>                 2021-05-29 13:55   15K  
+<img src="/icons/text.gif" alt="[TXT]"> <a href="opennlp-de-ud-gsd-sentence-1.0-1.9.3.bin.asc">opennlp-de-ud-gsd-sentence-1.0-1.9.3.bin.asc</a>             2021-05-29 13:55  833   
+<img src="/icons/text.gif" alt="[TXT]"> <a href="opennlp-de-ud-gsd-sentence-1.0-1.9.3.bin.sha256">opennlp-de-ud-gsd-sentence-1.0-1.9.3.bin.sha256</a>          2021-05-29 13:55  109   
+<img src="/icons/text.gif" alt="[TXT]"> <a href="opennlp-de-ud-gsd-sentence-1.0-1.9.3.bin.sha512">opennlp-de-ud-gsd-sentence-1.0-1.9.3.bin.sha512</a>          2021-05-29 13:55  173   
+<img src="/icons/binary.gif" alt="[   ]"> <a href="opennlp-de-ud-gsd-tokens-1.0-1.9.3.bin">opennlp-de-ud-gsd-tokens-1.0-1.9.3.bin</a>                   2021-05-29 13:55  553K  
+<img src="/icons/text.gif" alt="[TXT]"> <a href="opennlp-de-ud-gsd-tokens-1.0-1.9.3.bin.asc">opennlp-de-ud-gsd-tokens-1.0-1.9.3.bin.asc</a>               2021-05-29 13:55  833   
+<img src="/icons/text.gif" alt="[TXT]"> <a href="opennlp-de-ud-gsd-tokens-1.0-1.9.3.bin.sha256">opennlp-de-ud-gsd-tokens-1.0-1.9.3.bin.sha256</a>            2021-05-29 13:55  107   
+<img src="/icons/text.gif" alt="[TXT]"> <a href="opennlp-de-ud-gsd-tokens-1.0-1.9.3.bin.sha512">opennlp-de-ud-gsd-tokens-1.0-1.9.3.bin.sha512</a>            2021-05-29 13:55  171   
+<img src="/icons/binary.gif" alt="[   ]"> <a href="opennlp-en-ud-ewt-pos-1.0-1.9.3.bin">opennlp-en-ud-ewt-pos-1.0-1.9.3.bin</a>                      2021-05-29 13:55  1.1M  
+<img src="/icons/text.gif" alt="[TXT]"> <a href="opennlp-en-ud-ewt-pos-1.0-1.9.3.bin.asc">opennlp-en-ud-ewt-pos-1.0-1.9.3.bin.asc</a>                  2021-05-29 13:55  833   
+<img src="/icons/text.gif" alt="[TXT]"> <a href="opennlp-en-ud-ewt-pos-1.0-1.9.3.bin.sha256">opennlp-en-ud-ewt-pos-1.0-1.9.3.bin.sha256</a>               2021-05-29 13:55  104   
+<img src="/icons/text.gif" alt="[TXT]"> <a href="opennlp-en-ud-ewt-pos-1.0-1.9.3.bin.sha512">opennlp-en-ud-ewt-pos-1.0-1.9.3.bin.sha512</a>               2021-05-29 13:55  168   
+<img src="/icons/binary.gif" alt="[   ]"> <a href="opennlp-en-ud-ewt-sentence-1.0-1.9.3.bin">opennlp-en-ud-ewt-sentence-1.0-1.9.3.bin</a>                 2021-05-29 13:55   20K  
+<img src="/icons/text.gif" alt="[TXT]"> <a href="opennlp-en-ud-ewt-sentence-1.0-1.9.3.bin.asc">opennlp-en-ud-ewt-sentence-1.0-1.9.3.bin.asc</a>             2021-05-29 13:55  833   
+<img src="/icons/text.gif" alt="[TXT]"> <a href="opennlp-en-ud-ewt-sentence-1.0-1.9.3.bin.sha256">opennlp-en-ud-ewt-sentence-1.0-1.9.3.bin.sha256</a>          2021-05-29 13:55  109   
+<img src="/icons/text.gif" alt="[TXT]"> <a href="opennlp-en-ud-ewt-sentence-1.0-1.9.3.bin.sha512">opennlp-en-ud-ewt-sentence-1.0-1.9.3.bin.sha512</a>          2021-05-29 13:55  173   
+<img src="/icons/binary.gif" alt="[   ]"> <a href="opennlp-en-ud-ewt-tokens-1.0-1.9.3.bin">opennlp-en-ud-ewt-tokens-1.0-1.9.3.bin</a>                   2021-05-29 13:55  338K  
+<img src="/icons/text.gif" alt="[TXT]"> <a href="opennlp-en-ud-ewt-tokens-1.0-1.9.3.bin.asc">opennlp-en-ud-ewt-tokens-1.0-1.9.3.bin.asc</a>               2021-05-29 13:55  833   
+<img src="/icons/text.gif" alt="[TXT]"> <a href="opennlp-en-ud-ewt-tokens-1.0-1.9.3.bin.sha256">opennlp-en-ud-ewt-tokens-1.0-1.9.3.bin.sha256</a>            2021-05-29 13:55  107   
+<img src="/icons/text.gif" alt="[TXT]"> <a href="opennlp-en-ud-ewt-tokens-1.0-1.9.3.bin.sha512">opennlp-en-ud-ewt-tokens-1.0-1.9.3.bin.sha512</a>            2021-05-29 13:55  171   
+<img src="/icons/binary.gif" alt="[   ]"> <a href="opennlp-fr-ud-ftb-pos-1.0-1.9.3.bin">opennlp-fr-ud-ftb-pos-1.0-1.9.3.bin</a>                      2021-05-29 13:55   72K  
+<img src="/icons/text.gif" alt="[TXT]"> <a href="opennlp-fr-ud-ftb-pos-1.0-1.9.3.bin.asc">opennlp-fr-ud-ftb-pos-1.0-1.9.3.bin.asc</a>                  2021-05-29 13:55  833   
+<img src="/icons/text.gif" alt="[TXT]"> <a href="opennlp-fr-ud-ftb-pos-1.0-1.9.3.bin.sha256">opennlp-fr-ud-ftb-pos-1.0-1.9.3.bin.sha256</a>               2021-05-29 13:55  104   
+<img src="/icons/text.gif" alt="[TXT]"> <a href="opennlp-fr-ud-ftb-pos-1.0-1.9.3.bin.sha512">opennlp-fr-ud-ftb-pos-1.0-1.9.3.bin.sha512</a>               2021-05-29 13:55  168   
+<img src="/icons/binary.gif" alt="[   ]"> <a href="opennlp-fr-ud-ftb-tokens-1.0-1.9.3.bin">opennlp-fr-ud-ftb-tokens-1.0-1.9.3.bin</a>                   2021-05-29 13:55   35K  
+<img src="/icons/text.gif" alt="[TXT]"> <a href="opennlp-fr-ud-ftb-tokens-1.0-1.9.3.bin.asc">opennlp-fr-ud-ftb-tokens-1.0-1.9.3.bin.asc</a>               2021-05-29 13:55  833   
+<img src="/icons/text.gif" alt="[TXT]"> <a href="opennlp-fr-ud-ftb-tokens-1.0-1.9.3.bin.sha256">opennlp-fr-ud-ftb-tokens-1.0-1.9.3.bin.sha256</a>            2021-05-29 13:55  107   
+<img src="/icons/text.gif" alt="[TXT]"> <a href="opennlp-fr-ud-ftb-tokens-1.0-1.9.3.bin.sha512">opennlp-fr-ud-ftb-tokens-1.0-1.9.3.bin.sha512</a>            2021-05-29 13:55  171   
+<img src="/icons/binary.gif" alt="[   ]"> <a href="opennlp-it-ud-vit-pos-1.0-1.9.3.bin">opennlp-it-ud-vit-pos-1.0-1.9.3.bin</a>                      2021-05-29 13:55  1.1M  
+<img src="/icons/text.gif" alt="[TXT]"> <a href="opennlp-it-ud-vit-pos-1.0-1.9.3.bin.asc">opennlp-it-ud-vit-pos-1.0-1.9.3.bin.asc</a>                  2021-05-29 13:55  833   
+<img src="/icons/text.gif" alt="[TXT]"> <a href="opennlp-it-ud-vit-pos-1.0-1.9.3.bin.sha256">opennlp-it-ud-vit-pos-1.0-1.9.3.bin.sha256</a>               2021-05-29 13:55  104   
+<img src="/icons/text.gif" alt="[TXT]"> <a href="opennlp-it-ud-vit-pos-1.0-1.9.3.bin.sha512">opennlp-it-ud-vit-pos-1.0-1.9.3.bin.sha512</a>               2021-05-29 13:55  168   
+<img src="/icons/binary.gif" alt="[   ]"> <a href="opennlp-it-ud-vit-sentence-1.0-1.9.3.bin">opennlp-it-ud-vit-sentence-1.0-1.9.3.bin</a>                 2021-05-29 13:55  9.4K  
+<img src="/icons/text.gif" alt="[TXT]"> <a href="opennlp-it-ud-vit-sentence-1.0-1.9.3.bin.asc">opennlp-it-ud-vit-sentence-1.0-1.9.3.bin.asc</a>             2021-05-29 13:55  833   
+<img src="/icons/text.gif" alt="[TXT]"> <a href="opennlp-it-ud-vit-sentence-1.0-1.9.3.bin.sha256">opennlp-it-ud-vit-sentence-1.0-1.9.3.bin.sha256</a>          2021-05-29 13:55  109   
+<img src="/icons/text.gif" alt="[TXT]"> <a href="opennlp-it-ud-vit-sentence-1.0-1.9.3.bin.sha512">opennlp-it-ud-vit-sentence-1.0-1.9.3.bin.sha512</a>          2021-05-29 13:55  173   
+<img src="/icons/binary.gif" alt="[   ]"> <a href="opennlp-it-ud-vit-tokens-1.0-1.9.3.bin">opennlp-it-ud-vit-tokens-1.0-1.9.3.bin</a>                   2021-05-29 13:55  387K  
+<img src="/icons/text.gif" alt="[TXT]"> <a href="opennlp-it-ud-vit-tokens-1.0-1.9.3.bin.asc">opennlp-it-ud-vit-tokens-1.0-1.9.3.bin.asc</a>               2021-05-29 13:55  833   
+<img src="/icons/text.gif" alt="[TXT]"> <a href="opennlp-it-ud-vit-tokens-1.0-1.9.3.bin.sha256">opennlp-it-ud-vit-tokens-1.0-1.9.3.bin.sha256</a>            2021-05-29 13:55  107   
+<img src="/icons/text.gif" alt="[TXT]"> <a href="opennlp-it-ud-vit-tokens-1.0-1.9.3.bin.sha512">opennlp-it-ud-vit-tokens-1.0-1.9.3.bin.sha512</a>            2021-05-29 13:55  171   
+<img src="/icons/binary.gif" alt="[   ]"> <a href="opennlp-nl-ud-alpino-pos-1.0-1.9.3.bin">opennlp-nl-ud-alpino-pos-1.0-1.9.3.bin</a>                   2021-05-29 13:55  897K  
+<img src="/icons/text.gif" alt="[TXT]"> <a href="opennlp-nl-ud-alpino-pos-1.0-1.9.3.bin.asc">opennlp-nl-ud-alpino-pos-1.0-1.9.3.bin.asc</a>               2021-05-29 13:55  833   
+<img src="/icons/text.gif" alt="[TXT]"> <a href="opennlp-nl-ud-alpino-pos-1.0-1.9.3.bin.sha256">opennlp-nl-ud-alpino-pos-1.0-1.9.3.bin.sha256</a>            2021-05-29 13:55  107   
+<img src="/icons/text.gif" alt="[TXT]"> <a href="opennlp-nl-ud-alpino-pos-1.0-1.9.3.bin.sha512">opennlp-nl-ud-alpino-pos-1.0-1.9.3.bin.sha512</a>            2021-05-29 13:55  171   
+<img src="/icons/binary.gif" alt="[   ]"> <a href="opennlp-nl-ud-alpino-sentence-1.0-1.9.3.bin">opennlp-nl-ud-alpino-sentence-1.0-1.9.3.bin</a>              2021-05-29 13:55   13K  
+<img src="/icons/text.gif" alt="[TXT]"> <a href="opennlp-nl-ud-alpino-sentence-1.0-1.9.3.bin.asc">opennlp-nl-ud-alpino-sentence-1.0-1.9.3.bin.asc</a>          2021-05-29 13:55  833   
+<img src="/icons/text.gif" alt="[TXT]"> <a href="opennlp-nl-ud-alpino-sentence-1.0-1.9.3.bin.sha256">opennlp-nl-ud-alpino-sentence-1.0-1.9.3.bin.sha256</a>       2021-05-29 13:55  112   
+<img src="/icons/text.gif" alt="[TXT]"> <a href="opennlp-nl-ud-alpino-sentence-1.0-1.9.3.bin.sha512">opennlp-nl-ud-alpino-sentence-1.0-1.9.3.bin.sha512</a>       2021-05-29 13:55  176   
+<img src="/icons/binary.gif" alt="[   ]"> <a href="opennlp-nl-ud-alpino-tokens-1.0-1.9.3.bin">opennlp-nl-ud-alpino-tokens-1.0-1.9.3.bin</a>                2021-05-29 13:55  340K  
+<img src="/icons/text.gif" alt="[TXT]"> <a href="opennlp-nl-ud-alpino-tokens-1.0-1.9.3.bin.asc">opennlp-nl-ud-alpino-tokens-1.0-1.9.3.bin.asc</a>            2021-05-29 13:55  833   
+<img src="/icons/text.gif" alt="[TXT]"> <a href="opennlp-nl-ud-alpino-tokens-1.0-1.9.3.bin.sha256">opennlp-nl-ud-alpino-tokens-1.0-1.9.3.bin.sha256</a>         2021-05-29 13:55  110   
+<img src="/icons/text.gif" alt="[TXT]"> <a href="opennlp-nl-ud-alpino-tokens-1.0-1.9.3.bin.sha512">opennlp-nl-ud-alpino-tokens-1.0-1.9.3.bin.sha512</a>         2021-05-29 13:55  174   
+<img src="/icons/compressed.gif" alt="[   ]"> <a href="opennlp-training-eval-logs-1.0-1.9.3.zip">opennlp-training-eval-logs-1.0-1.9.3.zip</a>                 2021-05-29 13:55   40K  
+<img src="/icons/text.gif" alt="[TXT]"> <a href="opennlp-training-eval-logs-1.0-1.9.3.zip.asc">opennlp-training-eval-logs-1.0-1.9.3.zip.asc</a>             2021-05-29 13:55  833   
+<img src="/icons/text.gif" alt="[TXT]"> <a href="opennlp-training-eval-logs-1.0-1.9.3.zip.sha256">opennlp-training-eval-logs-1.0-1.9.3.zip.sha256</a>          2021-05-29 13:55  109   
+<img src="/icons/text.gif" alt="[TXT]"> <a href="opennlp-training-eval-logs-1.0-1.9.3.zip.sha512">opennlp-training-eval-logs-1.0-1.9.3.zip.sha512</a>          2021-05-29 13:55  173   
+<hr></pre>
+</body></html>
+