3232import opennlp .tools .formats .ad .ADSentenceStream .SentenceParser .Node ;
3333import opennlp .tools .formats .ad .ADSentenceStream .SentenceParser .TreeElement ;
3434import opennlp .tools .namefind .NameSample ;
35- import opennlp .tools .tokenize .WhitespaceTokenizer ;
3635import opennlp .tools .util .InputStreamFactory ;
3736import opennlp .tools .util .ObjectStream ;
3837import opennlp .tools .util .PlainTextByLineStream ;
3938import opennlp .tools .util .Span ;
39+ import opennlp .tools .util .StringUtil ;
4040
4141/**
4242 * Parser for Floresta Sita(c)tica Arvores Deitadas corpus, output to for the
5959 * Detailed info about the
6060 * <a href="http://beta.visl.sdu.dk/visl/pt/info/portsymbol.html#semtags_names">NER tagset</a>.
6161 * <p>
62+ * Whitespace inside the tags of a leaf and in a contraction is the Unicode White_Space property,
63+ * see {@link StringUtil#isUnicodeWhitespace(char)}, independent of the whitespace mode.
64+ * <p>
6265 * <b>Note:</b>
6366 * Do not use this class, internal use only!
6467 */
@@ -70,6 +73,16 @@ public class ADNameSampleStream implements ObjectStream<NameSample> {
7073 */
7174 private static final Map <String , String > HAREM ;
7275
76+ private static final String NER_PREFIX = "NER:" ;
77+ private static final String HYPHEN = "-" ;
78+ private static final char HYPHEN_CHAR = '-' ;
79+ private static final char UNDERSCORE = '_' ;
80+ private static final char TAG_OPEN = '<' ;
81+ private static final char TAG_CLOSE = '>' ;
82+ private static final String LITERARY_PREFIX = "LIT" ;
83+ private static final String SCIENTIFIC_PREFIX = "CIE" ;
84+ private static final String INVALID_METADATA = "Invalid metadata: " ;
85+
7386 static {
7487 Map <String , String > harem = new HashMap <>();
7588
@@ -243,7 +256,7 @@ private void processLeaf(Leaf leaf, List<String> sentence, List<Span> names) {
243256 String c = PortugueseContractionUtility .toContraction (
244257 leftContractionPart , right );
245258 if (c != null ) {
246- String [] parts = WhitespaceTokenizer . INSTANCE . tokenize (c );
259+ String [] parts = StringUtil . splitOnUnicodeWhitespace (c );
247260 sentence .addAll (Arrays .asList (parts ));
248261 alreadyAdded = true ;
249262 } else {
@@ -266,7 +279,7 @@ private void processLeaf(Leaf leaf, List<String> sentence, List<Span> names) {
266279 if (lexemes .length > 1 ) {
267280 sentence .addAll (Arrays .asList (lexemes ).subList (0 , lexemes .length - 1 ));
268281 }
269- leftContractionPart = lexemes [lexemes .length - 1 ];
282+ leftContractionPart = lexemes . length == 0 ? null : lexemes [lexemes .length - 1 ];
270283 return ;
271284 }
272285 if (leafTag .contains ("<NER2>" )) {
@@ -335,12 +348,12 @@ private List<String> processTok(String tok) {
335348 }
336349
337350 // lets split all hyphens
338- if (this .splitHyphenatedTokens && tok .contains ("-" ) && tok .length () > 1 ) {
351+ if (this .splitHyphenatedTokens && tok .contains (HYPHEN ) && tok .length () > 1 ) {
339352 String [] parts = matchHyphenatedToken (tok );
340353
341354 if (parts != null ) {
342355 addIfNotEmpty (parts [0 ], out );
343- addIfNotEmpty ("-" , out );
356+ addIfNotEmpty (HYPHEN , out );
344357 addIfNotEmpty (parts [1 ], out );
345358 addIfNotEmpty (parts [2 ], out );
346359 tokAdded = true ;
@@ -372,11 +385,17 @@ private void addIfNotEmpty(String firstTok, List<String> out) {
372385 * @param s The lexeme.
373386 * @return The parts in order; empty when the lexeme has no character other than underscores.
374387 */
375- static String [] splitOnUnderscores (String s ) {
388+ String [] splitOnUnderscores (String s ) {
389+ if (s .isEmpty ()) {
390+ return new String [0 ];
391+ }
392+ if (s .indexOf (UNDERSCORE ) == -1 ) {
393+ return new String [] {s };
394+ }
376395 List <String > tokens = new ArrayList <>();
377396 int start = -1 ;
378397 for (int i = 0 ; i < s .length (); i ++) {
379- if (s .charAt (i ) == '_' ) {
398+ if (s .charAt (i ) == UNDERSCORE ) {
380399 if (start >= 0 ) {
381400 tokens .add (s .substring (start , i ));
382401 start = -1 ;
@@ -398,7 +417,7 @@ static String[] splitOnUnderscores(String s) {
398417 * @return {@code true} if the token is non-empty and every code point is a letter or a
399418 * decimal digit.
400419 */
401- static boolean isAlphaNumeric (String tok ) {
420+ boolean isAlphaNumeric (String tok ) {
402421 if (tok .isEmpty ()) {
403422 return false ;
404423 }
@@ -422,23 +441,20 @@ static boolean isAlphaNumeric(String tok) {
422441 * @return The first token, second token, and rest, each {@code null} when absent, or
423442 * {@code null} if the token has none of the three shapes.
424443 */
425- static String [] matchHyphenatedToken (String tok ) {
444+ String [] matchHyphenatedToken (String tok ) {
426445 int len = tok .length ();
427- // (\p{L}+)-$
428- if (len > 1 && tok .charAt (len - 1 ) == '-' && isAllLetters (tok , 0 , len - 1 )) {
446+ if (len > 1 && tok .charAt (len - 1 ) == HYPHEN_CHAR && lettersEnd (tok , 0 ) == len - 1 ) {
429447 return new String [] {tok .substring (0 , len - 1 ), null , null };
430448 }
431- // ^-(\p{L}+)(.*)
432- if (tok .charAt (0 ) == '-' ) {
449+ if (tok .charAt (0 ) == HYPHEN_CHAR ) {
433450 int lettersEnd = lettersEnd (tok , 1 );
434451 if (lettersEnd > 1 ) {
435452 return new String [] {null , tok .substring (1 , lettersEnd ), tok .substring (lettersEnd )};
436453 }
437454 return null ;
438455 }
439- // (\p{L}+)-(\p{L}+)(.*)
440456 int firstEnd = lettersEnd (tok , 0 );
441- if (firstEnd > 0 && firstEnd + 1 < len && tok .charAt (firstEnd ) == '-' ) {
457+ if (firstEnd > 0 && firstEnd + 1 < len && tok .charAt (firstEnd ) == HYPHEN_CHAR ) {
442458 int secondEnd = lettersEnd (tok , firstEnd + 1 );
443459 if (secondEnd > firstEnd + 1 ) {
444460 return new String [] {tok .substring (0 , firstEnd ),
@@ -455,7 +471,7 @@ static String[] matchHyphenatedToken(String tok) {
455471 * @param from The start offset.
456472 * @return The offset after the run, or {@code from} if no letter starts there.
457473 */
458- private static int lettersEnd (String s , int from ) {
474+ private int lettersEnd (String s , int from ) {
459475 int i = from ;
460476 while (i < s .length ()) {
461477 int cp = s .codePointAt (i );
@@ -467,38 +483,18 @@ private static int lettersEnd(String s, int from) {
467483 return i ;
468484 }
469485
470- /**
471- * Tests whether a range holds letters only.
472- *
473- * @param s The text.
474- * @param from The inclusive start.
475- * @param to The exclusive end.
476- * @return {@code true} if every code point in the range is a letter.
477- */
478- private static boolean isAllLetters (String s , int from , int to ) {
479- int i = from ;
480- while (i < to ) {
481- int cp = s .codePointAt (i );
482- if (!Character .isLetter (cp )) {
483- return false ;
484- }
485- i += Character .charCount (cp );
486- }
487- return true ;
488- }
489-
490486 /**
491487 * Extracts the content of a NER tag in Arvores Deitadas format, between the optional
492488 * {@code NER:} prefix and the closing angle bracket.
493489 *
494490 * @param t The tag.
495491 * @return The content, or {@code null} if {@code t} is not enclosed in angle brackets.
496492 */
497- static String tagContent (String t ) {
498- if (t .length () < 2 || t .charAt (0 ) != '<' || t .charAt (t .length () - 1 ) != '>' ) {
493+ String tagContent (String t ) {
494+ if (t .length () < 2 || t .charAt (0 ) != TAG_OPEN || t .charAt (t .length () - 1 ) != TAG_CLOSE ) {
499495 return null ;
500496 }
501- int start = t .startsWith ("NER:" , 1 ) ? 5 : 1 ;
497+ int start = t .startsWith (NER_PREFIX , 1 ) ? 1 + NER_PREFIX . length () : 1 ;
502498 return t .substring (start , t .length () - 1 );
503499 }
504500
@@ -508,11 +504,11 @@ static String tagContent(String t) {
508504 * @param tags The NER tag in Arvores Deitadas format.
509505 * @return The NER tag, or {@code null} if not a NER tag in Arvores Deitadas format.
510506 */
511- private static String getNER (String tags ) {
507+ private String getNER (String tags ) {
512508 if (tags .contains ("<NER2>" )) {
513509 return null ;
514510 }
515- String [] tag = WhitespaceTokenizer . INSTANCE . tokenize (tags );
511+ String [] tag = StringUtil . splitOnUnicodeWhitespace (tags );
516512 for (String t : tag ) {
517513 String ner = tagContent (t );
518514 if (ner != null && HAREM .containsKey (ner )) {
@@ -532,29 +528,31 @@ public void close() throws IOException {
532528 adSentenceStream .close ();
533529 }
534530
531+ /**
532+ * Reads the id of the text a sentence belongs to; adaptive data is cleared when it changes. In
533+ * the Amazonia corpus it is the text id of the metadata. In the literary and scientific corpora
534+ * the text name stands in for it, and the id is the same for all sentences (OPENNLP-1951).
535+ *
536+ * @param paragraph The sentence.
537+ * @return The id.
538+ * @throws RuntimeException If the metadata has no id or one that does not fit into an
539+ * {@code int}.
540+ */
535541 private int getTextID (Sentence paragraph ) {
536-
537542 final String meta = paragraph .metadata ();
538- int textIdMeta2 = -1 ;
539- String textMeta2 = "" ;
540-
541- if (meta .startsWith ("LIT" ) || meta .startsWith ("CIE" )) {
542- String textId = meta .startsWith ("LIT" ) ? ADMetadata .textPrefix (meta ) : ADMetadata .source (meta );
543- if (textId == null ) {
544- throw new RuntimeException ("Invalid metadata: " + meta );
545- }
546- if (!textId .equals (textMeta2 )) {
547- textIdMeta2 ++;
548- textMeta2 = textId ;
543+ boolean literary = meta .startsWith (LITERARY_PREFIX );
544+ if (literary || meta .startsWith (SCIENTIFIC_PREFIX )) {
545+ String textName = literary ? ADMetadata .textPrefix (meta ) : ADMetadata .source (meta );
546+ if (textName == null ) {
547+ throw new RuntimeException (INVALID_METADATA + meta );
549548 }
550- return textIdMeta2 ;
549+ return textName . isEmpty () ? - 1 : 0 ;
551550 }
552- // Amazonia
553- String textId = ADMetadata .textId (meta );
554- if (textId == null ) {
555- throw new RuntimeException ("Invalid metadata: " + meta );
551+ ADMetadata .TextAndParagraph ids = ADMetadata .parseTextAndParagraph (meta );
552+ if (ids == null ) {
553+ throw new RuntimeException (INVALID_METADATA + meta );
556554 }
557- return Integer . parseInt ( textId );
555+ return ids . text ( );
558556 }
559557
560558}
0 commit comments