text:
package com.johnsnowlabs.nlp.annotators

import com.amazonaws.thirdparty.joda.time.LocalDateTime
import com.amazonaws.thirdparty.joda.time.format.DateTimeFormat
import com.johnsnowlabs.nlp.{Annotation, DataBuilder}
import com.johnsnowlabs.tags.FastTest
import org.apache.spark.ml.Pipeline
import org.apache.spark.sql.{Dataset, Row}
import org.scalatest.flatspec.AnyFlatSpec

import java.time.LocalDate
import java.time.format.DateTimeFormatter

class DateMatcherMultiLanguageTestSpec extends AnyFlatSpec with DateMatcherBehaviors {

  /** ITALIAN * */

  "a DateMatcher" should "be catching formatted italian dates" taggedAs FastTest in {

    val data: Dataset[Row] = DataBuilder.basicDataBuild("Sono arrivato in Italia il 15/9/2012.")

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat("MM/dd/yyyy")
      .setSourceLanguage("it")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    assert(annotations.head.result == "09/15/2012")
  }

  "a DateMatcher" should "be catching unformatted italian dates" taggedAs FastTest in {

    val data: Dataset[Row] =
      DataBuilder.basicDataBuild("Sono arrivato in Italia il 15 Settembre 2012.")

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat("MM/dd/yyyy")
      .setSourceLanguage("it")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    assert(annotations.head.result == "09/15/2012")
  }
  "a DateMatcher" should "be catching unformatted italian language dates" taggedAs FastTest in {

    val data: Dataset[Row] =
      DataBuilder.basicDataBuild("Sono arrivato in Italia il 15 Settembre 2012.")

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat("MM/dd/yyyy")
      .setSourceLanguage("it")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    assert(annotations.head.result == "09/15/2012")
  }

  "a DateMatcher" should "be catching relative unformatted italian language dates yearly" taggedAs FastTest in {

    val data: Dataset[Row] = DataBuilder.basicDataBuild("Sono arrivato in Italia 2 anni fa.")

    val DateFormat = "MM/dd/yyyy"

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat(DateFormat)
      .setSourceLanguage("it")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    val localDate = LocalDate.now.minusYears(2L)
    val formatter = DateTimeFormatter.ofPattern(DateFormat)
    val formattedDateString = localDate.format(formatter)

    assert(annotations.head.result == formattedDateString)
  }

  "a DateMatcher" should "be catching relative unformatted italian language dates weekly" taggedAs FastTest in {

    val data: Dataset[Row] = DataBuilder.basicDataBuild("Sono arrivato in Italia 2 settimane fa.")

    val DateFormat = "MM/dd/yyyy"

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat(DateFormat)
      .setSourceLanguage("it")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    val localDate = LocalDate.now.minusWeeks(2L)
    val formatter = DateTimeFormatter.ofPattern(DateFormat)
    val formattedDateString = localDate.format(formatter)

    assert(annotations.head.result == formattedDateString)
  }

  "a DateMatcher" should "be catching relative unformatted italian language dates daily" taggedAs FastTest in {

    val data: Dataset[Row] = DataBuilder.basicDataBuild("Sono arrivato in Italia 2 giorni fa.")

    val DateFormat = "MM/dd/yyyy"

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat(DateFormat)
      .setSourceLanguage("it")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    val localDate = LocalDate.now.minusDays(2L)
    val formatter = DateTimeFormatter.ofPattern(DateFormat)
    val formattedDateString = localDate.format(formatter)

    assert(annotations.head.result == formattedDateString)
  }

  "a DateMatcher" should "be catching relative unformatted italian language future dates yearly" taggedAs FastTest in {

    val data: Dataset[Row] = DataBuilder.basicDataBuild("Il prossimo anno tornerò in Italia.")

    val DateFormat = "MM/dd/yyyy"

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat(DateFormat)
      .setSourceLanguage("it")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    val localDate = LocalDate.now.plusYears(1L)
    val formatter = DateTimeFormatter.ofPattern(DateFormat)
    val formattedDateString = localDate.format(formatter)

    assert(annotations.head.result == formattedDateString)
  }

  "a DateMatcher" should "be catching relative unformatted italian language future dates monthly" taggedAs FastTest in {

    val data: Dataset[Row] = DataBuilder.basicDataBuild("Il mese prossimo tornerò in Italia.")

    val DateFormat = "MM/dd/yyyy"

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat(DateFormat)
      .setSourceLanguage("it")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    val localDate = LocalDate.now.plusMonths(1L)
    val formatter = DateTimeFormatter.ofPattern(DateFormat)
    val formattedDateString = localDate.format(formatter)

    assert(annotations.head.result == formattedDateString)
  }

  "a DateMatcher" should "be catching relative unformatted italian language future dates weekly" taggedAs FastTest in {

    val data: Dataset[Row] =
      DataBuilder.basicDataBuild("La settimana prossima tornerò in Italia.")

    val DateFormat = "MM/dd/yyyy"

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat(DateFormat)
      .setSourceLanguage("it")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    val localDate = LocalDate.now.plusWeeks(1L)
    val formatter = DateTimeFormatter.ofPattern(DateFormat)
    val formattedDateString = localDate.format(formatter)

    assert(annotations.head.result == formattedDateString)
  }

  "a DateMatcher" should "be catching relative unformatted italian language future dates daily" taggedAs FastTest in {

    val data: Dataset[Row] = DataBuilder.basicDataBuild("Domani andrò in Italia.")

    val DateFormat = "MM/dd/yyyy"

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat(DateFormat)
      .setSourceLanguage("it")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    val localDate = LocalDate.now.plusDays(1L)
    val formatter = DateTimeFormatter.ofPattern(DateFormat)
    val formattedDateString = localDate.format(formatter)

    assert(annotations.head.result == formattedDateString)
  }

  "a DateMatcher" should "be catching relative numeric date in italian language daily" taggedAs FastTest in {

    val data: Dataset[Row] = DataBuilder.basicDataBuild("Andrò in Italia tra 4 giorni.")

    val DateFormat = "MM/dd/yyyy"

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat(DateFormat)
      .setSourceLanguage("it")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    val localDate = LocalDate.now.plusDays(4L)
    val formatter = DateTimeFormatter.ofPattern(DateFormat)
    val formattedDateString = localDate.format(formatter)

    assert(annotations.head.result == formattedDateString)
  }

  "a DateMatcher" should "be catching relative numeric date in italian language hourly" taggedAs FastTest in {

    val data: Dataset[Row] = DataBuilder.basicDataBuild("Partirò per l'Italia tra 4 ore.")

    val DateFormat = "MM/dd/yyyy"

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat(DateFormat)
      .setSourceLanguage("it")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    val localDateTime = LocalDateTime.now.plusHours(4)
    val formatter = DateTimeFormat.forPattern(DateFormat)
    val formattedDateString = formatter.print(localDateTime)

    assert(annotations.head.result == formattedDateString)
  }

  /** FRENCH * */

  "a DateMatcher" should "be catching formatted french dates" taggedAs FastTest in {

    val data: Dataset[Row] = DataBuilder.basicDataBuild("Je suis arrivé en France le 23/5/2019.")

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat("MM/dd/yyyy")
      .setSourceLanguage("fr")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    assert(annotations.head.result == "05/23/2019")
  }

  "a DateMatcher" should "be catching unformatted french dates" taggedAs FastTest in {

    val data: Dataset[Row] =
      DataBuilder.basicDataBuild("Je suis arrivé en France le 23 avril 2019.")

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat("MM/dd/yyyy")
      .setSourceLanguage("fr")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    assert(annotations.head.result == "04/23/2019")
  }

  "a DateMatcher" should "be catching unformatted french language dates" taggedAs FastTest in {

    val data: Dataset[Row] =
      DataBuilder.basicDataBuild("Je suis arrivé en France le 23 février 2019.")

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat("MM/dd/yyyy")
      .setSourceLanguage("fr")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    assert(annotations.head.result == "02/23/2019")
  }

  "a DateMatcher" should "be catching relative unformatted french language dates yearly" taggedAs FastTest in {

    val data: Dataset[Row] = DataBuilder.basicDataBuild("Je suis arrivé en France il y a 2 ans.")

    val DateFormat = "MM/dd/yyyy"

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat(DateFormat)
      .setSourceLanguage("fr")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    val localDate = LocalDate.now.minusYears(2L)
    val formatter = DateTimeFormatter.ofPattern(DateFormat)
    val formattedDateString = localDate.format(formatter)

    assert(annotations.head.result == formattedDateString)
  }

  "a DateMatcher" should "be catching relative unformatted french language dates weekly" taggedAs FastTest in {

    val data: Dataset[Row] =
      DataBuilder.basicDataBuild("Je suis arrivé en France il y a 2 semaines.")

    val DateFormat = "MM/dd/yyyy"

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat(DateFormat)
      .setSourceLanguage("fr")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    val localDate = LocalDate.now.minusWeeks(2L)
    val formatter = DateTimeFormatter.ofPattern(DateFormat)
    val formattedDateString = localDate.format(formatter)

    assert(annotations.head.result == formattedDateString)
  }

  "a DateMatcher" should "be catching relative unformatted french language dates daily" taggedAs FastTest in {

    val data: Dataset[Row] =
      DataBuilder.basicDataBuild("Je suis arrivé en France il y a 2 jours.")

    val DateFormat = "MM/dd/yyyy"

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat(DateFormat)
      .setSourceLanguage("fr")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    val localDate = LocalDate.now.minusDays(2L)
    val formatter = DateTimeFormatter.ofPattern(DateFormat)
    val formattedDateString = localDate.format(formatter)

    assert(annotations.head.result == formattedDateString)
  }

  "a DateMatcher" should "be catching relative unformatted french language future dates yearly" taggedAs FastTest in {

    val data: Dataset[Row] =
      DataBuilder.basicDataBuild("Je retournerai en Italie l'année prochaine.")

    val DateFormat = "MM/dd/yyyy"

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat(DateFormat)
      .setSourceLanguage("fr")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    val localDate = LocalDate.now.plusYears(1L)
    val formatter = DateTimeFormatter.ofPattern(DateFormat)
    val formattedDateString = localDate.format(formatter)

    assert(annotations.head.result == formattedDateString)
  }

  "a DateMatcher" should "be catching relative unformatted french language future dates monthly" taggedAs FastTest in {

    val data: Dataset[Row] =
      DataBuilder.basicDataBuild("Je retournerai en Italie le mois prochain.")

    val DateFormat = "MM/dd/yyyy"

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat(DateFormat)
      .setSourceLanguage("fr")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    val localDate = LocalDate.now.plusMonths(1L)
    val formatter = DateTimeFormatter.ofPattern(DateFormat)
    val formattedDateString = localDate.format(formatter)

    assert(annotations.head.result == formattedDateString)
  }

  "a DateMatcher" should "be catching relative unformatted french language future dates weekly" taggedAs FastTest in {

    val data: Dataset[Row] =
      DataBuilder.basicDataBuild("La semaine prochaine, je retournerai en Italie.")

    val DateFormat = "MM/dd/yyyy"

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat(DateFormat)
      .setSourceLanguage("fr")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    val localDate = LocalDate.now.plusWeeks(1L)
    val formatter = DateTimeFormatter.ofPattern(DateFormat)
    val formattedDateString = localDate.format(formatter)

    assert(annotations.head.result == formattedDateString)
  }

  "a DateMatcher" should "be catching relative unformatted french language future dates daily" taggedAs FastTest in {

    val data: Dataset[Row] = DataBuilder.basicDataBuild("Demain j'irai en France.")

    val DateFormat = "MM/dd/yyyy"

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat(DateFormat)
      .setSourceLanguage("fr")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    val localDate = LocalDate.now.plusDays(1L)
    val formatter = DateTimeFormatter.ofPattern(DateFormat)
    val formattedDateString = localDate.format(formatter)

    assert(annotations.head.result == formattedDateString)
  }

  "a DateMatcher" should "be catching relative numeric date in french language daily" taggedAs FastTest in {

    val data: Dataset[Row] = DataBuilder.basicDataBuild("J'irai en Italie dans 4 jours.")

    val DateFormat = "MM/dd/yyyy"

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat(DateFormat)
      .setSourceLanguage("fr")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    val localDate = LocalDate.now.plusDays(4L)
    val formatter = DateTimeFormatter.ofPattern(DateFormat)
    val formattedDateString = localDate.format(formatter)

    assert(annotations.head.result == formattedDateString)
  }

  "a DateMatcher" should "be catching relative numeric date in french language hourly" taggedAs FastTest in {

    val data: Dataset[Row] = DataBuilder.basicDataBuild("Je pars pour l'Italie dans 4 heures.")

    val DateFormat = "MM/dd/yyyy"

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat(DateFormat)
      .setSourceLanguage("fr")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    val localDateTime = LocalDateTime.now.plusHours(4)
    val formatter = DateTimeFormat.forPattern(DateFormat)
    val formattedDateString = formatter.print(localDateTime)

    assert(annotations.head.result == formattedDateString)
  }

  /** PORTUGUESE * */

  "a DateMatcher" should "be catching formatted portuguese dates" taggedAs FastTest in {

    val data: Dataset[Row] = DataBuilder.basicDataBuild("Cheguei à França no dia 23/5/2019.")

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat("MM/dd/yyyy")
      .setSourceLanguage("pt")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    assert(annotations.head.result == "05/23/2019")
  }

  "a DateMatcher" should "be catching unformatted portuguese dates" taggedAs FastTest in {

    val data: Dataset[Row] = DataBuilder.basicDataBuild("Cheguei à França em 23 de maio de 2019.")

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat("MM/dd/yyyy")
      .setSourceLanguage("pt")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    assert(annotations.head.result == "05/23/2019")
  }

  "a DateMatcher" should "be catching relative unformatted portuguese language dates yearly" taggedAs FastTest in {

    val data: Dataset[Row] = DataBuilder.basicDataBuild("Eu cheguei na França 2 anos atrás.")

    val DateFormat = "MM/dd/yyyy"

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat(DateFormat)
      .setSourceLanguage("pt")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    val localDate = LocalDate.now.minusYears(2L)
    val formatter = DateTimeFormatter.ofPattern(DateFormat)
    val formattedDateString = localDate.format(formatter)

    assert(annotations.head.result == formattedDateString)
  }

  "a DateMatcher" should "be catching relative unformatted portuguese language dates weekly" taggedAs FastTest in {

    val data: Dataset[Row] = DataBuilder.basicDataBuild("Eu cheguei na França 2 semanas atrás.")

    val DateFormat = "MM/dd/yyyy"

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat(DateFormat)
      .setSourceLanguage("pt")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    val localDate = LocalDate.now.minusWeeks(2L)
    val formatter = DateTimeFormatter.ofPattern(DateFormat)
    val formattedDateString = localDate.format(formatter)

    assert(annotations.head.result == formattedDateString)
  }

  "a DateMatcher" should "be catching relative unformatted portuguese language dates daily" taggedAs FastTest in {

    val data: Dataset[Row] = DataBuilder.basicDataBuild("Eu cheguei na França 2 dias atrás.")

    val DateFormat = "MM/dd/yyyy"

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat(DateFormat)
      .setSourceLanguage("pt")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    val localDate = LocalDate.now.minusDays(2L)
    val formatter = DateTimeFormatter.ofPattern(DateFormat)
    val formattedDateString = localDate.format(formatter)

    assert(annotations.head.result == formattedDateString)
  }

  "a DateMatcher" should "be catching relative unformatted portuguese language future dates yearly" taggedAs FastTest in {

    val data: Dataset[Row] =
      DataBuilder.basicDataBuild("No próximo ano, eu voltarei novamente au Portugal.")

    val DateFormat = "MM/dd/yyyy"

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat(DateFormat)
      .setSourceLanguage("pt")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    val localDate = LocalDate.now.plusYears(1L)
    val formatter = DateTimeFormatter.ofPattern(DateFormat)
    val formattedDateString = localDate.format(formatter)

    assert(annotations.head.result == formattedDateString)
  }

  "a DateMatcher" should "be catching relative unformatted portuguese language future dates monthly" taggedAs FastTest in {

    val data: Dataset[Row] =
      DataBuilder.basicDataBuild("No próximo mês irei novamente a Portugal.")

    val DateFormat = "MM/dd/yyyy"

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat(DateFormat)
      .setSourceLanguage("pt")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    val localDate = LocalDate.now.plusMonths(1L)
    val formatter = DateTimeFormatter.ofPattern(DateFormat)
    val formattedDateString = localDate.format(formatter)

    assert(annotations.head.result == formattedDateString)
  }

  "a DateMatcher" should "be catching relative unformatted portuguese language future dates weekly" taggedAs FastTest in {

    val data: Dataset[Row] = DataBuilder.basicDataBuild("Na próxima semana vou para portugal.")

    val DateFormat = "MM/dd/yyyy"

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat(DateFormat)
      .setSourceLanguage("pt")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    val localDate = LocalDate.now.plusWeeks(1L)
    val formatter = DateTimeFormatter.ofPattern(DateFormat)
    val formattedDateString = localDate.format(formatter)

    assert(annotations.head.result == formattedDateString)
  }

  "a DateMatcher" should "be catching relative unformatted portuguese language future dates daily" taggedAs FastTest in {

    val data: Dataset[Row] = DataBuilder.basicDataBuild("Amanhã vou para portugal.")

    val DateFormat = "MM/dd/yyyy"

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat(DateFormat)
      .setSourceLanguage("pt")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    val localDate = LocalDate.now.plusDays(1L)
    val formatter = DateTimeFormatter.ofPattern(DateFormat)
    val formattedDateString = localDate.format(formatter)

    assert(annotations.head.result == formattedDateString)
  }

  "a DateMatcher" should "be catching relative numeric date in portuguese language daily" taggedAs FastTest in {

    val data: Dataset[Row] = DataBuilder.basicDataBuild("Vou visitar Portugal em 4 dias.")

    val DateFormat = "MM/dd/yyyy"

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat(DateFormat)
      .setSourceLanguage("pt")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    val localDate = LocalDate.now.plusDays(4L)
    val formatter = DateTimeFormatter.ofPattern(DateFormat)
    val formattedDateString = localDate.format(formatter)

    assert(annotations.head.result == formattedDateString)
  }

  "a DateMatcher" should "be catching relative numeric date in portuguese language hourly" taggedAs FastTest in {

    val data: Dataset[Row] = DataBuilder.basicDataBuild("Vou chegar a Portugal em 4 horas.")

    val DateFormat = "MM/dd/yyyy"

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat(DateFormat)
      .setSourceLanguage("pt")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    val localDateTime = LocalDateTime.now.plusHours(4)
    val formatter = DateTimeFormat.forPattern(DateFormat)
    val formattedDateString = formatter.print(localDateTime)

    assert(annotations.head.result == formattedDateString)
  }

  /** SPANISH * */

  "a DateMatcher" should "be catching formatted spanish dates" taggedAs FastTest in {

    val data: Dataset[Row] = DataBuilder.basicDataBuild("Llegué a España el 23/05/2019.")

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat("MM/dd/yyyy")
      .setSourceLanguage("es")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    assert(annotations.head.result == "05/23/2019")
  }

  "a DateMatcher" should "be catching unformatted spanish dates" taggedAs FastTest in {

    val data: Dataset[Row] = DataBuilder.basicDataBuild("Llegué a España el 23 de mayo de 2019.")

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat("MM/dd/yyyy")
      .setSourceLanguage("es")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    assert(annotations.head.result == "05/23/2019")
  }

  "a DateMatcher" should "be catching relative unformatted spanish language dates yearly" taggedAs FastTest in {

    val data: Dataset[Row] = DataBuilder.basicDataBuild("Llegué a españa hace 2 años.")

    val DateFormat = "MM/dd/yyyy"

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat(DateFormat)
      .setSourceLanguage("es")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    val localDate = LocalDate.now.minusYears(2L)
    val formatter = DateTimeFormatter.ofPattern(DateFormat)
    val formattedDateString = localDate.format(formatter)

    assert(annotations.head.result == formattedDateString)
  }

  "a DateMatcher" should "be catching relative unformatted spanish language dates weekly" taggedAs FastTest in {

    val data: Dataset[Row] = DataBuilder.basicDataBuild("Llegué a España hace 2 semanas.")

    val DateFormat = "MM/dd/yyyy"

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat(DateFormat)
      .setSourceLanguage("es")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    val localDate = LocalDate.now.minusWeeks(2L)
    val formatter = DateTimeFormatter.ofPattern(DateFormat)
    val formattedDateString = localDate.format(formatter)

    assert(annotations.head.result == formattedDateString)
  }

  "a DateMatcher" should "be catching relative unformatted spanish language dates daily" taggedAs FastTest in {

    val data: Dataset[Row] = DataBuilder.basicDataBuild("Llegué a España hace 2 días.")

    val DateFormat = "MM/dd/yyyy"

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat(DateFormat)
      .setSourceLanguage("es")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    val localDate = LocalDate.now.minusDays(2L)
    val formatter = DateTimeFormatter.ofPattern(DateFormat)
    val formattedDateString = localDate.format(formatter)

    assert(annotations.head.result == formattedDateString)
  }

  "a DateMatcher" should "be catching relative unformatted spanish language future dates yearly" taggedAs FastTest in {

    val data: Dataset[Row] = DataBuilder.basicDataBuild("El año que viene volveré a España.")

    val DateFormat = "MM/dd/yyyy"

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat(DateFormat)
      .setSourceLanguage("es")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    val localDate = LocalDate.now.plusYears(1L)
    val formatter = DateTimeFormatter.ofPattern(DateFormat)
    val formattedDateString = localDate.format(formatter)

    assert(annotations.head.result == formattedDateString)
  }

  "a DateMatcher" should "be catching relative unformatted spanish language future dates monthly" taggedAs FastTest in {

    val data: Dataset[Row] = DataBuilder.basicDataBuild("El mes que viene volveré a España.")

    val DateFormat = "MM/dd/yyyy"

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat(DateFormat)
      .setSourceLanguage("es")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    val localDate = LocalDate.now.plusMonths(1L)
    val formatter = DateTimeFormatter.ofPattern(DateFormat)
    val formattedDateString = localDate.format(formatter)

    assert(annotations.head.result == formattedDateString)
  }

  "a DateMatcher" should "be catching relative unformatted spanish language future dates weekly" taggedAs FastTest in {

    val data: Dataset[Row] = DataBuilder.basicDataBuild("La semana que viene me voy a España.")

    val DateFormat = "MM/dd/yyyy"

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat(DateFormat)
      .setSourceLanguage("es")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    val localDate = LocalDate.now.plusWeeks(1L)
    val formatter = DateTimeFormatter.ofPattern(DateFormat)
    val formattedDateString = localDate.format(formatter)

    assert(annotations.head.result == formattedDateString)
  }

  "a DateMatcher" should "be catching relative unformatted spanish language future dates daily" taggedAs FastTest in {

    val data: Dataset[Row] = DataBuilder.basicDataBuild("Mañana me voy a España.")

    val DateFormat = "MM/dd/yyyy"

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat(DateFormat)
      .setSourceLanguage("es")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    val localDate = LocalDate.now.plusDays(1L)
    val formatter = DateTimeFormatter.ofPattern(DateFormat)
    val formattedDateString = localDate.format(formatter)

    assert(annotations.head.result == formattedDateString)
  }

  "a DateMatcher" should "be catching relative numeric date in spanish language daily" taggedAs FastTest in {

    val data: Dataset[Row] = DataBuilder.basicDataBuild("Visitaré España en 4 días.")

    val DateFormat = "MM/dd/yyyy"

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat(DateFormat)
      .setSourceLanguage("es")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    val localDate = LocalDate.now.plusDays(4L)
    val formatter = DateTimeFormatter.ofPattern(DateFormat)
    val formattedDateString = localDate.format(formatter)

    assert(annotations.head.result == formattedDateString)
  }

  "a DateMatcher" should "be catching relative numeric date in spanish language hourly" taggedAs FastTest in {

    val data: Dataset[Row] = DataBuilder.basicDataBuild("Visitaré España en 4 horas.")

    val DateFormat = "MM/dd/yyyy"

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat(DateFormat)
      .setSourceLanguage("es")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    val localDateTime = LocalDateTime.now.plusHours(4)
    val formatter = DateTimeFormat.forPattern(DateFormat)
    val formattedDateString = formatter.print(localDateTime)

    assert(annotations.head.result == formattedDateString)
  }

  /** GERMAN * */

  "a DateMatcher" should "be catching formatted german dates" taggedAs FastTest in {

    val data: Dataset[Row] =
      DataBuilder.basicDataBuild("Ich bin am 23/05/2019 in Deutschland angekommen.")

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat("MM/dd/yyyy")
      .setSourceLanguage("de")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    assert(annotations.head.result == "05/23/2019")
  }

  "a DateMatcher" should "be catching unformatted german dates" taggedAs FastTest in {

    val data: Dataset[Row] =
      DataBuilder.basicDataBuild("Ich bin am 23 Mai 2019 in Deutschland angekommen.")

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat("MM/dd/yyyy")
      .setSourceLanguage("de")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    assert(annotations.head.result == "05/23/2019")
  }

  "a DateMatcher" should "be catching relative unformatted german language dates yearly" taggedAs FastTest in {

    val data: Dataset[Row] =
      DataBuilder.basicDataBuild("Ich bin vor 2 jahren in Deutschland angekommen.")

    val DateFormat = "MM/dd/yyyy"

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat(DateFormat)
      .setSourceLanguage("de")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    val localDate = LocalDate.now.minusYears(2L)
    val formatter = DateTimeFormatter.ofPattern(DateFormat)
    val formattedDateString = localDate.format(formatter)

    assert(annotations.head.result == formattedDateString)
  }

  "a DateMatcher" should "be catching relative unformatted german language dates weekly" taggedAs FastTest in {

    val data: Dataset[Row] =
      DataBuilder.basicDataBuild("Ich bin vor 2 wochen in Deutschland angekommen.")

    val DateFormat = "MM/dd/yyyy"

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat(DateFormat)
      .setSourceLanguage("de")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    val localDate = LocalDate.now.minusWeeks(2L)
    val formatter = DateTimeFormatter.ofPattern(DateFormat)
    val formattedDateString = localDate.format(formatter)

    assert(annotations.head.result == formattedDateString)
  }

  "a DateMatcher" should "be catching relative unformatted german language dates daily" taggedAs FastTest in {

    val data: Dataset[Row] =
      DataBuilder.basicDataBuild("Ich bin vor 2 tagen in Deutschland angekommen.")

    val DateFormat = "MM/dd/yyyy"

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat(DateFormat)
      .setSourceLanguage("de")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    val localDate = LocalDate.now.minusDays(2L)
    val formatter = DateTimeFormatter.ofPattern(DateFormat)
    val formattedDateString = localDate.format(formatter)

    assert(annotations.head.result == formattedDateString)
  }

  "a DateMatcher" should "be catching relative unformatted german language future dates yearly" taggedAs FastTest in {

    val data: Dataset[Row] =
      DataBuilder.basicDataBuild("Nächstes jahr fahre ich nach Deutschland.")

    val DateFormat = "MM/dd/yyyy"

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat(DateFormat)
      .setSourceLanguage("de")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    val localDate = LocalDate.now.plusYears(1L)
    val formatter = DateTimeFormatter.ofPattern(DateFormat)
    val formattedDateString = localDate.format(formatter)

    assert(annotations.head.result == formattedDateString)
  }

  "a DateMatcher" should "be catching relative unformatted german language future dates monthly" taggedAs FastTest in {

    val data: Dataset[Row] =
      DataBuilder.basicDataBuild("Nächsten monat fahre ich nach Deutschland.")

    val DateFormat = "MM/dd/yyyy"

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat(DateFormat)
      .setSourceLanguage("de")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    val localDate = LocalDate.now.plusMonths(1L)
    val formatter = DateTimeFormatter.ofPattern(DateFormat)
    val formattedDateString = localDate.format(formatter)

    assert(annotations.head.result == formattedDateString)
  }

  "a DateMatcher" should "be catching relative unformatted german language future dates weekly" taggedAs FastTest in {

    val data: Dataset[Row] =
      DataBuilder.basicDataBuild("Nächste woche fahre ich nach Deutschland.")

    val DateFormat = "MM/dd/yyyy"

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat(DateFormat)
      .setSourceLanguage("de")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    val localDate = LocalDate.now.plusWeeks(1L)
    val formatter = DateTimeFormatter.ofPattern(DateFormat)
    val formattedDateString = localDate.format(formatter)

    assert(annotations.head.result == formattedDateString)
  }

  "a DateMatcher" should "be catching relative unformatted german language future dates daily" taggedAs FastTest in {

    val data: Dataset[Row] = DataBuilder.basicDataBuild("Morgen fahre ich nach Deutschland.")

    val DateFormat = "MM/dd/yyyy"

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat(DateFormat)
      .setSourceLanguage("de")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    val localDate = LocalDate.now.plusDays(1L)
    val formatter = DateTimeFormatter.ofPattern(DateFormat)
    val formattedDateString = localDate.format(formatter)

    assert(annotations.head.result == formattedDateString)
  }

  "a DateMatcher" should "be catching relative numeric date in german language daily" taggedAs FastTest in {

    val data: Dataset[Row] =
      DataBuilder.basicDataBuild("Ich werde Deutschland in 4 tagen besuchen.")

    val DateFormat = "MM/dd/yyyy"

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat(DateFormat)
      .setSourceLanguage("de")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    val localDate = LocalDate.now.plusDays(4L)
    val formatter = DateTimeFormatter.ofPattern(DateFormat)
    val formattedDateString = localDate.format(formatter)

    assert(annotations.head.result == formattedDateString)
  }

  "a DateMatcher" should "be catching relative numeric date in german language hourly" taggedAs FastTest in {

    val data: Dataset[Row] =
      DataBuilder.basicDataBuild("Ich werde Deutschland in 4 stunden besuchen.")

    val DateFormat = "MM/dd/yyyy"

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat(DateFormat)
      .setSourceLanguage("de")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    val localDateTime = LocalDateTime.now.plusHours(4)
    val formatter = DateTimeFormat.forPattern(DateFormat)
    val formattedDateString = formatter.print(localDateTime)

    assert(annotations.head.result == formattedDateString)
  }

  "a DateMatcher" should "be catching relative date in italian with complex sentence" taggedAs FastTest in {

    val data: Dataset[Row] = DataBuilder.basicDataBuild(
      "Così il ct azzurro Roberto Mancini, oggi, poco prima di entrare al Quirinale dove l'Italia campione " +
        "d'Europa sta per essere accolta dal Presidente della Repubblica Sergio Mattarella.")

    val DateFormat = "MM/dd/yyyy"

    val dateMatcher = new DateMatcher()
      .setInputCols("document")
      .setOutputCol("date")
      .setOutputFormat(DateFormat)
      .setSourceLanguage("it")

    val pipeline = new Pipeline().setStages(Array(dateMatcher))

    val annotated = pipeline.fit(data).transform(data)

    val annotations: Seq[Annotation] =
      Annotation.getAnnotations(annotated.select("date").collect().head, "date")

    val localDateTime = LocalDateTime.now
    val formatter = DateTimeFormat.forPattern(DateFormat)
    val formattedDateString = formatter.print(localDateTime)

    assert(annotations.head.result == formattedDateString)
  }
}


text:
package com.google.android.exoplayer2.ext.flac;

import android.os.Handler;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.audio.AudioCapabilities;
import com.google.android.exoplayer2.audio.AudioRendererEventListener;
import com.google.android.exoplayer2.audio.SimpleDecoderAudioRenderer;
import com.google.android.exoplayer2.drm.ExoMediaCrypto;
import com.google.android.exoplayer2.util.MimeTypes;

/**
 * Decodes and renders audio using the native Flac decoder.
 */
public class LibflacAudioRenderer extends SimpleDecoderAudioRenderer {

  private static final int NUM_BUFFERS = 16;

  public LibflacAudioRenderer() {
    this(null, null);
  }

  /**
   * @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
   *     null if delivery of events is not required.
   * @param eventListener A listener of events. May be null if delivery of events is not required.
   */
  public LibflacAudioRenderer(Handler eventHandler, AudioRendererEventListener eventListener) {
    super(eventHandler, eventListener);
  }

  /**
   * @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
   *     null if delivery of events is not required.
   * @param eventListener A listener of events. May be null if delivery of events is not required.
   * @param audioCapabilities The audio capabilities for playback on this device. May be null if the
   *     default capabilities (no encoded audio passthrough support) should be assumed.
   */
  public LibflacAudioRenderer(Handler eventHandler, AudioRendererEventListener eventListener,
      AudioCapabilities audioCapabilities) {
    super(eventHandler, eventListener, audioCapabilities);
  }

  @Override
  public int supportsFormat(Format format) {
    return FlacLibrary.isAvailable() && MimeTypes.AUDIO_FLAC.equalsIgnoreCase(format.sampleMimeType)
        ? FORMAT_HANDLED : FORMAT_UNSUPPORTED_TYPE;
  }

  @Override
  protected FlacDecoder createDecoder(Format format, ExoMediaCrypto mediaCrypto)
      throws FlacDecoderException {
    return new FlacDecoder(NUM_BUFFERS, NUM_BUFFERS, format.initializationData);
  }

}

redpajama_set_name: RedPajamaGithub


text:
Interacting with the containers
================================

Once the containers are up and running, you have a few options for interacting with them. They will automatically collect data for your repositories - but how do you add repositories? We're glad you asked!

Using the repo loading UI
--------------------------

Augur offers a special graphical interface for loading repository groups when using the Docker containers. This component is called ``augurface``, and is available anytime you are using the ``backend`` and ``frontend`` services together.

To use it, first start the two services (we recommend using `Docker Compose <docker-compose.html>`_ for this):

.. code-block:: bash

    # this example uses the database image, but will work with an external one as well
    $ docker-compose -f docker-compose.yml -f database-compose.yml up

Then, navigate to ``http://localhost:8080/augurface/`` in your browser - **note the trailing slash!** Once you're on this page, you'll need to enter in your Augur API key in the box on the top right. On a default Docker installation, you can use ``docker_key``, but we recommend changing this as soon as possible if you are planning to use the instance long-term. Commands for working with the API keys can be found `here <../getting-started/command-line-interface/db.html>`_.

Once you've entered your API key, you will be able to use the UI to automatically import GitHub organizations as a repo group, or manually create and edit repo groups yourself. Deleting repos or repo groups is not currently supported, but would be a great contribution!

.. warning::

    Because the UI only requires an API key to edit the database, **we recommend that you do not publicly deploy any Docker instance outside your local network or intranet.** The Docker build is intended ONLY for short term data collection usage and local development. We understand if this inconvenciences you, but the functionality is very new to Augur and still needs time to be production ready. **You have been warned.**

If are not using the ``frontend`` service, you can use the `database CLI <../getting-started/command-line-interface/db.html>`_ from within the container to add repos. See below for how to start a shell within the container.

Accessing the containers
---------------------------

If you need to access a running container (perhaps to check the worker logs) or run a CLI command, you can use the following helpful command, replacing ``<service_name>`` with the appropriate value:

.. code-block:: bash

    $ docker exec -it <service_name> /bin/bash

Viewing container logs
-------------------------

By default, the only logs shown by the container are the logs of Augur's main data collection process. If you started your container(s) in the background, and want to view these logs again, run the following command in the root ``augur`` directory\:

.. code-block:: bash

    # to quickly view the most recent logs
    $ docker-compose logs

    # to watch the logs in real time (like tail -f)
    $ docker-compose logs -f

Conclusion
-----------

This wraps up the Docker section of the Augur documentation. We hope it was useful! Happy hacking!


text:
.. _qibuild-and-autotools:

qiBuild and autotools
=====================

Introduction
-------------

This section is targeted towards autotools users wanting to know more about
qiBuild

The comparison between CMake and autotools applies to qiBuild and autotools,
too.

So, in a nutshell, using qiBuild/CMake over autotools has the following
advantages:

* Usable on windows without using a POSIX compatibility layer such as mingw or
  cygwin

* Can be used with Visual Studio

* Easy support for cross-compilation

* Somewhat nicer syntax

Using qiBuild with autotools projects
-------------------------------------

Right now there are no plans to make it easier to use qibuild with autotools
projects.

Here are two small tasks that could be tackled, though. (Patches welcome!)

Finding autotools-based projects
++++++++++++++++++++++++++++++++

There is already support for autotools in CMake.

It looks like:

.. code-block:: cmake

    find_package(PkgConfig)
    pkg_check_modules(FOO foo-0.42)

Unfortunately, this does not work if ``foo-0.42.pc`` is in a toolchain, so
you cannot use it to cross-compile a library which depends on ``foo``.

This may be fixed by patching ``PkgConfig.cmake`` to
set ``PKG_CONFIG_PATH`` to ``CMAKE_FIND_ROOT_PATH`` and ``--prefix`` to
``CMAKE_FIND_ROOT_PATH`` when calling ``pkg-config``.

Generating pkg config files
+++++++++++++++++++++++++++

Again, nothing hard to do here.

During the call to :cmake:function:`qi_stage_lib`, it should be easy to
generate the ``.pc`` file. All the information is already here.


text:
var FrameworkError = function (message, domain, code, reference) {
  Error.captureStackTrace(this, FrameworkError);

  this.name = 'FrameworkError';
  this.message = message;
  this.domain = domain;
  this.code = code;
  this.reference = reference;

}

var ErrorCode = {
  AUTHENTICATE_INVALID_TYPE: 1000,

  AUTHENTICATE_NO_USER: 1010,
  AUTHENTICATE_WRONG_PASSWORD: 1011,

  SESSION_INVALID: 1200,

  MODULE_NOT_INITIALIZE: 1100
};

var ErrorDomain = {
  FRAMEWORK: 1000
}

exports.FrameworkError = FrameworkError;
exports.ErrorCode = ErrorCode;
exports.ErrorDomain = ErrorDomain;


text:
require 'test_helper'

module SlimFast
  class ShortDomainTest < ActiveSupport::TestCase
    # test "the truth" do
    #   assert true
    # end
  end
end


text:
set(PROJECT_ROOT "${CMAKE_CURRENT_LIST_DIR}/../..")
set(BUILD_FILE "${PROJECT_ROOT}/BUILD.bazel")

file(GLOB_RECURSE HEADERS LIST_DIRECTORIES false RELATIVE "${PROJECT_ROOT}" "include/*.hpp")

file(WRITE "${BUILD_FILE}" [=[
cc_library(
    name = "json",
    hdrs = [
]=])

foreach(header ${HEADERS})
    file(APPEND "${BUILD_FILE}" "        \"${header}\",\n")
endforeach()

file(APPEND "${BUILD_FILE}" [=[
    ],
    includes = ["include"],
    visibility = ["//visibility:public"],
    alwayslink = True,
)
]=])


text:
<?php
###################################################################
#           Simple Desk Project - www.simpledesk.net              #
#                  Email Notifications Plugin                     #
###################################################################
#         An advanced help desk modifcation built on SMF          #
###################################################################
#                                                                 #
#           * Copyright 2015 - SimpleDesk.net                     #
#                                                                 #
#     This file and its contents are subject to the license       #
#     included with this distribution, license.txt, which         #
#     states that this software is New BSD Licensed.              #
#     Any questions, please contact SimpleDesk.net                #
#                                                                 #
###################################################################
# SimpleDesk Version: 2.1                                         #
# File Info: SimpleDesk-Notifications.php / 2.1                   #
###################################################################

/**
 *	@package source
 *	@since 2.0
*/

if (!defined('SMF'))
	die('Hacking attempt...');

function shd_notifications_notify_newticket(&$msgOptions, &$ticketOptions, &$posterOptions)
{
	global $smcFunc, $context, $modSettings, $scripturl;

	// Enabled?
	if (empty($modSettings['shd_notify_new_ticket']))
		return;

	// We don't always have this. But it should be available elsewhere.
	if (!isset($ticketOptions['private']))
		$ticketOptions['private'] = $context['ticket_form']['private']['setting'];
	if (empty($posterOptions['name']))
		$posterOptions['name'] = $context['user']['name'];
	if (empty($ticketOptions['subject']))
		$ticketOptions['subject'] = $context['ticket_form']['subject'];

	// So, we're getting the list of people that are being affected by this ticket being posted. Basically, that's a list of staff on new ticket, less people who've set preferences otherwise.
	$members = shd_get_visible_list($ticketOptions['dept'], $ticketOptions['private'], false, empty($modSettings['shd_admins_not_assignable']), false);
	if (empty($members))
		return;

	// Get the default preferences
	$prefs = shd_load_user_prefs(false);
	$pref_groups = $prefs['groups'];
	$base_prefs = $prefs['prefs'];

	// Apply the default preference
	$members = array_flip($members);
	foreach ($members as $member => $value)
		$members[$member] = $base_prefs['notify_new_ticket']['default'];

	// Query the database for the rest.
	$query = $smcFunc['db_query']('', '
		SELECT id_member, value
		FROM {db_prefix}helpdesk_preferences
		WHERE id_member IN ({array_int:members})
			AND variable = {string:pref}',
		array(
			'members' => array_keys($members),
			'pref' => 'notify_new_ticket',
		)
	);

	while ($row = $smcFunc['db_fetch_assoc']($query))
		$members[(int) $row['id_member']] = $row['value'];
	$smcFunc['db_free_result']($query);

	// OK, now figure out who we're sending to, and discard any member id's we're not sending to
	foreach ($members as $member => $value)
	{
		if (!empty($value))
			$members[$member] = 'new_ticket';
		else
			unset($members[$member]);
	}

	if (isset($members[$context['user']['id']]))
		unset($members[$context['user']['id']]);

	if (empty($members))
		return;

	// Build the big ol' data set
	$notify_data = array(
		'members' => $members,
		'ticketlink' => $scripturl . '?action=helpdesk;sa=ticket;ticket=' . $ticketOptions['id'],
		'subject' => $ticketOptions['subject'],
		'ticket' => $ticketOptions['id'],
		'body' => $msgOptions['body'],
		'poster_name' => $posterOptions['name'],
	);

	shd_notify_users($notify_data);
}

function shd_notifications_notify_newreply(&$msgOptions, &$ticketOptions, &$posterOptions)
{
	global $smcFunc, $context, $modSettings, $scripturl;

	// We did actually get a reply? Sometimes shd_modify_ticket_post() is called for other things, not just on reply.
	if (empty($msgOptions['body']))
		return;

	// Alternatively, they might be doing a silent update.
	if (!empty($_POST['silent_update']) && shd_allowed_to('shd_silent_update', $ticketOptions['dept']))
		return;

	// We're doing various things here, so grab some general details, not just what we may have been passed before.
	$ticketinfo = shd_load_ticket($ticketOptions['id']);
	// $staff is the sum total of staff + ticket starter, subject to visibility of the ticket.
	$staff = shd_get_visible_list($ticketOptions['dept'], $ticketinfo['private'], $ticketinfo['starter_id'], empty($modSettings['shd_admins_not_assignable']), false);

	// Might as well kick this off here.
	$notify_data = array(
		'members' => array(),
		'ticketlink' => $scripturl . '?action=helpdesk;sa=ticket;ticket=' . $ticketOptions['id'] . '.msg' . $msgOptions['id'] . '#msg' . $msgOptions['id'],
		'subject' => $ticketinfo['subject'],
		'ticket' => $ticketOptions['id'],
		'msg' => $msgOptions['id'],
		'body' => $msgOptions['body'],
		'poster_name' => $posterOptions['name'],
	);

	$members = array(); // who should get what type of notification, preferences depending

	// Someone replied to MY ticket? And it isn't me? I might want you to tell me about it!
	if (!empty($ticketinfo['starter_id']) && !empty($modSettings['shd_notify_new_reply_own']) && $posterOptions['id'] != $ticketinfo['starter_id'] && in_array($ticketinfo['starter_id'], $staff))
		$members[$ticketinfo['starter_id']]['new_reply_own'] = true;

	// So this is a ticket I'm supposed to deal with... has someone said something I missed? (And just in case it's our ticket, don't send another)
	if (!empty($modSettings['shd_notify_new_reply_assigned']))
		if (!empty($ticketinfo['assigned_id']) && $posterOptions['id'] != $ticketinfo['assigned_id'] && in_array($ticketinfo['assigned_id'], $staff))
			$members[$ticketinfo['assigned_id']]['new_reply_assigned'] = true;

	// So, if you're staff, and you've replied to a ticket before, do you want to be notified this time?
	if (!empty($modSettings['shd_notify_new_reply_previous']))
	{
		$query = $smcFunc['db_query']('', '
			SELECT id_member
			FROM {db_prefix}helpdesk_ticket_replies
			WHERE id_ticket = {int:ticket}
			GROUP BY id_member',
			array(
				'ticket' => $ticketOptions['id'],
			)
		);
		$responders = array();
		while ($row = $smcFunc['db_fetch_row']($query))
			$responders[] = $row[0]; // this shouldn't be nil, ever, because we're replying, so the topic already exists so there's at least one name in there...
		$smcFunc['db_free_result']($query);

		$responders = array_intersect($responders, $staff);
		foreach ($responders as $id)
			$members[$id]['new_reply_previous'] = true;
	}

	// Do staff just want general notifications about everything? Don't change things if they already had a notice though, this is just for the rabble at the back
	if (!empty($modSettings['shd_notify_new_reply_any']))
		foreach ($staff as $id)
			$members[$id]['new_reply_any'] = true;

	if (empty($members))
		return;

	// Get the default preferences
	$prefs = shd_load_user_prefs(false);
	$pref_groups = $prefs['groups'];
	$base_prefs = $prefs['prefs'];

	// Build a list of users -> default prefs
	$member_prefs = array();
	$pref_list = array();
	foreach ($members as $id => $type_list)
	{
		foreach ($type_list as $type => $value)
		{
			$member_prefs[$id][$type] = $base_prefs['notify_' . $type]['default'];
			$pref_list['notify_' . $type] = true;
		}
	}

	// Grab pref list from DB for these users and update
	$query = $smcFunc['db_query']('', '
		SELECT id_member, variable, value
		FROM {db_prefix}helpdesk_preferences
		WHERE id_member IN ({array_int:members})
			AND variable IN ({array_string:variables})',
		array(
			'members' => array_keys($members),
			'variables' => array_keys($pref_list),
		)
	);

	while ($row = $smcFunc['db_fetch_assoc']($query))
	{
		$row['id_member'] = (int) $row['id_member'];
		$variable = substr($row['variable'], 7);
		if (isset($member_prefs[$row['id_member']][$variable]))
			$member_prefs[$row['id_member']][$variable] = $row['value'];
	}
	$smcFunc['db_free_result']($query);

	// unset $members where member pref doesn't fit
	foreach ($member_prefs as $id => $value)
		foreach ($value as $pref_id => $pref_item)
			if (empty($pref_item))
				unset($members[$id][$pref_id]);

	// Lastly, get the list of people who wanted to be notified through their preferences - and anyone who wanted to be excluded.
	$overrides = shd_query_monitor_list($ticketOptions['id']);
	foreach ($overrides['always_notify'] as $member_id)
		$members[$member_id]['monitor'] = true;

	// And apply exclusions
	foreach ($overrides['never_notify'] as $member_id)
		unset($members[$member_id]);

	// And now, finally, receive the list of possible cases from the notification doodad, and verify against a list of possible people.
	if ($context['can_ping'])
	{
		if (!empty($_POST['notify']) && is_array($_POST['notify']))
		{
			$staff[] = $ticketinfo['starter_id']; // Add the ticket starter as a possible candidate.
			foreach ($_POST['notify'] as $id)
				if (in_array((int) $id, $staff))
					$members[$id]['ping'] = true;
		}
		elseif (!empty($_REQUEST['list']))
		{
			$list = explode(',', $_REQUEST['list']);
			foreach ($list as $id)
				if (in_array((int) $id, $staff))
					$members[$id]['ping'] = true;
		}
	}

	// Lastly, prevent the reply author getting notified for any reason.
	if (!empty($members[$context['user']['id']]))
		unset($members[$context['user']['id']]);

	// So, at this point, $members contains a list of the members and a sequence of the possible messages they could get. We need to make some sense of it.
	$notify_data['members'] = array();
	foreach ($members as $member_id => $notify_list)
	{
		if (empty($notify_list))
			continue; // Preferences killed all their possible choices.

		// We want the first non empty value on the list, and that's all.
		$item = '';
		foreach ($notify_list as $k => $v)
		{
			if (!empty($v))
			{
				$item = $k;
				break;
			}
		}

		if (!empty($item))
			$notify_data['members'][$member_id] = $item; // We want the first one only.
	}

	// AAAAAAAAAAAAND WE'RE OFF!
	if (!empty($notify_data['members']))
		shd_notify_users($notify_data);
}

function shd_notifications_notify_assign(&$ticket, &$assignment)
{
	global $smcFunc, $context, $modSettings, $scripturl;

	if (empty($modSettings['shd_notify_assign_me']) && empty($modSettings['shd_notify_assign_own']))
		return;

	$ticketinfo = shd_load_ticket($ticket);
	// ticket starter = $ticketinfo['starter_id']
	// $assignment = user id of assignee (0 for no longer being assigned)

	$notify_data = array(
		'members' => array(),
		'ticketlink' => $scripturl . '?action=helpdesk;sa=ticket;ticket=' . $ticket . '.0',
		'subject' => $ticketinfo['subject'],
		'ticket' => $ticket,
	);

	// Get the default preferences
	$prefs = shd_load_user_prefs(false);
	$pref_groups = $prefs['groups'];
	$base_prefs = $prefs['prefs'];

	$members = array();
	$member_prefs = array();
	// So, does the starter want to be notified if the ticket is assigned?
	if (!empty($ticketinfo['starter_id']) && !empty($modSettings['shd_notify_assign_own']))
	{
		$members[$ticketinfo['starter_id']] = 'assign_own';
		$member_prefs[$ticketinfo['starter_id']] = $base_prefs['notify_assign_own']['default'];
	}

	// Does the assignee want to be notified? This assumes it is actually a person...
	if (!empty($assignment) && !empty($modSettings['shd_notify_assign_me']))
	{
		$members[$assignment] = 'assign_me';
		$member_prefs[$assignment] = $base_prefs['notify_assign_me']['default'];
	}

	if (isset($members[$context['user']['id']]))
		unset($members[$context['user']['id']]);

	if (empty($members))
		return; // whoops

	// OK, so we've figured out what we'd send to folks. Now let's see what the users want
	$query = $smcFunc['db_query']('', '
		SELECT id_member, variable, value
		FROM {db_prefix}helpdesk_preferences
		WHERE id_member IN ({array_int:members})
			AND variable IN ({array_string:variables})',
		array(
			'members' => array_keys($members),
			'variables' => array('notify_assign_me', 'notify_assign_own'),
		)
	);

	while ($row = $smcFunc['db_fetch_assoc']($query))
	{
		$row['id_member'] = (int) $row['id_member'];
		if ($row['variable'] == 'notify_' . $members[$row['id_member']])
			$member_prefs[$row['id_member']] = $row['value'];
	}
	$smcFunc['db_free_result']($query);

	// unset $members where member pref doesn't fit
	foreach ($member_prefs as $id => $value)
	{
		if (empty($value))
			unset($members[$id]);
	}

	// move $members to $notify_data['members']
	$notify_data['members'] = $members;

	// AAAAAAAAAAAAND WE'RE OFF!
	if (!empty($notify_data['members']))
		shd_notify_users($notify_data);
}

/**
 *	Handle email notifications
 *
 *	@todo Finish documenting
 *	@since 2.0
*/
function shd_notify_users($notify_data)
{
	global $context, $txt, $modSettings, $language, $smcFunc, $sourcedir;

	if (empty($notify_data['members']))
		return;

	// So, for the folks we're sending a message to, figure out what languages etc we're loading
	$notify_lang = array();
	$query = $smcFunc['db_query']('', '
		SELECT id_member, lngfile, email_address
		FROM {db_prefix}members
		WHERE id_member IN ({array_int:members})',
		array(
			'members' => array_keys($notify_data['members']),
		)
	);

	while ($row = $smcFunc['db_fetch_assoc']($query))
	{
		$needed_language = empty($row['lngfile']) || empty($modSettings['userLanguage']) ? $language : $row['lngfile'];
		if (empty($notify_lang[$needed_language]))
			$notify_lang[$needed_language] = array();

		$notify_lang[$needed_language][] = $row['id_member'];
		$emails[$row['id_member']] = $row['email_address'];
	}
	$smcFunc['db_free_result']($query);

	// So, at this point, we have our list of language files to load so we can minimise the amount of actual work going on, and let's get ready
	$replacements = array(
		'{ticket_id}' => str_pad($notify_data['ticket'], $modSettings['shd_zerofill'], '0', STR_PAD_LEFT),
		'{subject}' => $notify_data['subject'],
		'{ticketlink}' => $notify_data['ticketlink'],
	);

	if (isset($notify_data['poster_name']))
		$replacements['{poster_name}'] = $notify_data['poster_name'];
	if (isset($notify_data['body']))
		$replacements['{body}'] = trim(un_htmlspecialchars(strip_tags(strtr(shd_format_text($notify_data['body'], false), array('
' => "\n", '
' => "\n", '
' => "\n", '[' => '[', ']' => ']')))));
	$withbody = isset($notify_data['body']) && !empty($modSettings['shd_notify_with_body']) ? 'bodyfull' : 'body';

	// Also note, we may not be coming from the post code... so make sure sendmail() is available
	if (!function_exists('sendmail'))
		require_once($sourcedir . '/Subs-Post.php');

	$log = array(
		'emails' => array(),
		'auto' => true,
		'subject' => $notify_data['subject'],
		'withbody' => $withbody == 'bodyfull',
	);
	foreach ($notify_lang as $this_lang => $lang_members)
	{
		shd_load_language('sd_language/SimpleDeskNotifications', $this_lang);

		foreach ($lang_members as $member)
		{
			$email_type = $notify_data['members'][$member];

			$subject = str_replace(array_keys($replacements), array_values($replacements), $txt['template_subject_notify_' . $email_type]);

			$body = $txt['template_' . $withbody . '_notify_' . $email_type] . "\n\n" . $txt['regards_team'];
			$body = str_replace(array_keys($replacements), array_values($replacements), $body);

			if (!isset($log['emails'][$email_type]))
				$log['emails'][$email_type] = array(
					'u' => array(),
				);

			// Now then, do we have a member?
			if (!empty($member))
				$log['emails'][$email_type]['u'][] = $member;

			//function sendmail($to, $subject, $message, $from = null, $message_id = null, $send_html = false, $priority = 3, $hotmail_fix = null, $is_private = false)
			if (!empty($modSettings['shd_notify_email']))
				$modSettings['mail_from'] = $modSettings['shd_notify_email'];
			sendmail($emails[$member], $subject, $body, null, 'shd_notify_' . $email_type . '_' . $member);
		}
	}

	// Now, let's fix up the log items. In the action log, emails is an array of notify subtypes, that relate to template_subject_notify_* in the notifications language file.
	// Within the type subarray, there's u and e, u is a comma separated list of user ids, e is a comma separated list of non user id emails.
	foreach ($log['emails'] as $type => $data)
	{
		if (!empty($data['u']))
			$log['emails'][$type]['u'] = implode(',', $data['u']);
		else
			unset($log['emails'][$type]['u']);

		if (empty($log['emails'][$type]))
			unset($log['emails'][$type]);
	}

	// We're doing it manually because we're bending some of the rules. It bypasses the usual shd_logopt_* check and the last_update change.
	if (empty($modSettings['shd_disable_action_log']) && !empty($log['emails']) && !empty($modSettings['shd_notify_log']))
		$smcFunc['db_insert']('',
			'{db_prefix}helpdesk_log_action',
			array(
				'log_time' => 'int', 'id_member' => 'int', 'ip' => 'string-16', 'action' => 'string', 'id_ticket' => 'int', 'id_msg' => 'int', 'extra' => 'string-65534',
			),
			array(
				time(), 0, '', 'notify', $notify_data['ticket'], !empty($notify_data['msg']) ? $notify_data['msg'] : 0, serialize($log),
			),
			array('id_action')
		);
}

/**
 *	Display the notice of email.
 *
 *	@todo Finish documenting
 *	@since 2.0
*/
function shd_notify_popup()
{
	global $txt, $context, $settings, $modSettings, $smcFunc, $user_profile, $user_info, $scripturl;

	// First, verify we got a log entry, that the log entry is right, and it points to a ticket we can actually see.
	$_GET['log'] = isset($_GET['log']) ? (int) $_GET['log'] : 0;

	$email_type = isset($_GET['template']) ? preg_replace('~[^a-z_]~', '', $_GET['template']) : '';

	if (empty($modSettings['shd_display_ticket_logs']) || empty($_GET['log']) || empty($email_type))
		fatal_lang_error('no_access', false);

	$query = $smcFunc['db_query']('', '
		SELECT hdla.id_member, hdla.id_ticket, hdla.id_msg, hdla.extra, IFNULL(hdtr.body, {string:empty}) AS body, IFNULL(mem.real_name, hdtr.poster_name) AS poster_name
		FROM {db_prefix}helpdesk_log_action AS hdla
			LEFT JOIN {db_prefix}helpdesk_ticket_replies AS hdtr ON (hdla.id_msg = hdtr.id_msg)
			LEFT JOIN {db_prefix}members AS mem ON (hdtr.id_member = mem.id_member)
		WHERE id_action = {int:log}
			AND action = {string:notify}',
		array(
			'log' => $_GET['log'],
			'notify' => 'notify',
			'empty' => '',
		)
	);
	if ($smcFunc['db_num_rows']($query) == 0)
	{
		$smcFunc['db_free_result']($query);
		fatal_lang_error('no_access');
	}
	$row = $smcFunc['db_fetch_assoc']($query);
	$smcFunc['db_free_result']($query);

	$row['extra'] = unserialize($row['extra']);

	// Just check we did actually log an email of that type.
	if (empty($row['extra']['emails'][$_GET['template']]))
		fatal_lang_error('no_access', false);

	$ticketinfo = shd_load_ticket($row['id_ticket']);

	// OK, if we're here, we can see the ticket. Can we actually see the email log at this point?
	if (!shd_allowed_to('shd_view_ticket_logs_any', $ticketinfo['dept']) && (!shd_allowed_to('shd_view_ticket_logs_own', $ticketinfo['dept']) || !$ticketinfo['is_own']))
		fatal_lang_error('no_access', false);

	// We're reusing the Help template, need its language file.
	loadLanguage('Help');
	shd_load_language('sd_language/SimpleDeskAdmin');
	shd_load_language('sd_language/SimpleDeskLogAction');
	shd_load_language('sd_language/SimpleDeskNotifications');

	// Set the page up
	loadTemplate('Help');
	$context['page_title'] = $context['forum_name'] . ' - ' . $txt['shd_log_notifications'];
	$context['template_layers'] = array();
	$context['sub_template'] = 'popup';

	$users = array();
	// First, get the list of users, and load their username etc if we haven't already attempted it before.
	if (isset($row['extra']['emails'][$email_type]['u']))
		$users = array_merge($users, explode(',', $row['extra']['emails'][$email_type]['u']));
	if (!empty($users))
	{
		$users = array_unique($users);
		if (!empty($users))
			loadMemberData($users, false, 'minimal');
	}

	// Now we have all the usernames for this instance, let's go and build this entry.
	$context['help_text'] = $txt['shd_log_notify_to'] . '
';

	$new_content = '';
	if (!empty($users))
	{
		$first = true;
		foreach ($users as $user)
		{
			if (empty($user_profile[$user]))
				continue;

			$new_content .= ($first ? ' ' : ', ') . shd_profile_link($user_profile[$user]['real_name'], $user);
			$first = false;
		}
	}

	if (!empty($row['extra']['emails'][$email_type]['e']))
	{
		$emails = explode(',', $row['extra']['emails'][$email_type]['e']);
		// Admins can see the actual emails.
		if (shd_allowed_to('admin_helpdesk', 0) || $user_info['is_admin'])
		{
			foreach ($emails as $key => $value)
				$emails[$key] = '' . $value . '';
			$new_content .= ' ' . implode(', ', $emails);
		}
		// No-one else can at the moment.
		else
			$new_content .= ' ' . (count($emails) == 1 ? $txt['shd_log_notify_hiddenemail_1'] : sprintf($txt['shd_log_notify_hiddenemail'], count($emails)));
	}
	if (!empty($new_content))
		$context['help_text'] .= $new_content;

	$context['help_text'] .= '
';

	// So the general prep is done. Now let's rebuild the email contents.
	if (empty($row['extra']['withbody']) || empty($row['body']))
		$body = '';
	else
		$body = trim(un_htmlspecialchars(strip_tags(strtr(shd_format_text($row['body'], false), array('
' => "\n", '
' => "\n", '
' => "\n", '[' => '[', ']' => ']')))));

	$replacements = array(
		"\n" => '
',
		'{ticket_id}' => str_pad($row['id_ticket'], $modSettings['shd_zerofill'], '0', STR_PAD_LEFT),
		'{subject}' => empty($row['extra']['subject']) ? $txt['no_subject'] : $row['extra']['subject'],
		'{ticketlink}' => $scripturl . '?action=helpdesk;sa=ticket;ticket=' . $row['id_ticket'] . (empty($row['id_msg']) ? '.0' : '.msg' . $row['id_msg'] . '#msg' . $row['id_msg']),
		'{body}' => $body,
		'{poster_name}' => $row['poster_name'],
	);

	$email_subject = str_replace(array_keys($replacements), array_values($replacements), $txt['template_subject_notify_' . $email_type]);
	$email_body = str_replace(array_keys($replacements), array_values($replacements), $txt['template_' . (empty($row['extra']['withbody']) || empty($row['body']) ? 'body' : 'bodyfull') . '_notify_' . $email_type]);

	$context['help_text'] .= '' . $txt['subject'] . ': ' . $email_subject . '

' . $email_body;
}

function shd_notify_ticket_options()
{
	global $context, $txt, $smcFunc;

	$ticketinfo = shd_load_ticket(); // This does permissions to access the ticket too.
	$ticket_starter = $ticketinfo['starter_id'] == $context['user']['id'];

	checkSession();

	if (empty($_REQUEST['notifyaction']))
		$_REQUEST['notifyaction'] = '';

	// Get any existing entry.
	$query = $smcFunc['db_query']('', '
		SELECT notify_state
		FROM {db_prefix}helpdesk_notify_override
		WHERE id_member = {int:user}
			AND id_ticket = {int:ticket}',
		array(
			'user' => $context['user']['id'],
			'ticket' => $context['ticket_id'],
		)
	);
	if ($smcFunc['db_num_rows']($query) == 0)
		$old_state = NOTIFY_PREFS;
	else
		list($old_state) = $smcFunc['db_fetch_row']($query);
	$smcFunc['db_free_result']($query);

	switch ($_REQUEST['notifyaction'])
	{
		case 'monitor_on';
			if (!shd_allowed_to('shd_monitor_ticket_any', $ticketinfo['dept']) && (!$ticket_starter || !shd_allowed_to('shd_monitor_ticket_own', $ticketinfo['dept'])))
				fatal_lang_error('cannot_monitor_ticket', false);

			// Unlike turning it off, we might be turning it on from either just off, or ignored, so log that fact.
			if ($old_state == NOTIFY_ALWAYS)
				break;
			elseif ($old_state == NOTIFY_NEVER)
				// Log turning off ignore list first
				shd_log_action('unignore',
					array(
						'ticket' => $context['ticket_id'],
						'subject' => $ticketinfo['subject'],
					)
				);
			// Then add the new status.
			$smcFunc['db_insert']('replace',
				'{db_prefix}helpdesk_notify_override',
				array(
					'id_member' => 'int', 'id_ticket' => 'int', 'notify_state' => 'int',
				),
				array(
					$context['user']['id'], $context['ticket_id'], NOTIFY_ALWAYS,
				),
				array('id_member', 'id_ticket')
			);
			shd_log_action('monitor',
				array(
					'ticket' => $context['ticket_id'],
					'subject' => $ticketinfo['subject'],
				)
			);
			break;
		case 'monitor_off';
			if (!shd_allowed_to('shd_monitor_ticket_any', $ticketinfo['dept']) && (!$ticket_starter || !shd_allowed_to('shd_monitor_ticket_own', $ticketinfo['dept'])))
				fatal_lang_error('cannot_unmonitor_ticket', false);
			// Just delete the old status.
			$smcFunc['db_query']('', '
				DELETE FROM {db_prefix}helpdesk_notify_override
				WHERE id_member = {int:member}
					AND id_ticket = {int:ticket}',
				array(
					'member' => $context['user']['id'],
					'ticket' => $context['ticket_id'],
				)
			);
			shd_log_action('unmonitor',
				array(
					'ticket' => $context['ticket_id'],
					'subject' => $ticketinfo['subject'],
				)
			);
			break;
		case 'ignore_on';
			if (!shd_allowed_to('shd_ignore_ticket_any', $ticketinfo['dept']) && (!$ticket_starter || !shd_allowed_to('shd_ignore_ticket_own', $ticketinfo['dept'])))
				fatal_lang_error('cannot_monitor_ticket', false);

			// Unlike turning it off, we might be turning it on from either just off, or ignored, so log that fact.
			if ($old_state == NOTIFY_NEVER)
				break;
			elseif ($old_state == NOTIFY_ALWAYS)
				// Log turning off ignore list first
				shd_log_action('unmonitor',
					array(
						'ticket' => $context['ticket_id'],
						'subject' => $ticketinfo['subject'],
					)
				);

			$smcFunc['db_insert']('replace',
				'{db_prefix}helpdesk_notify_override',
				array(
					'id_member' => 'int', 'id_ticket' => 'int', 'notify_state' => 'int',
				),
				array(
					$context['user']['id'], $context['ticket_id'], NOTIFY_NEVER,
				),
				array('id_member', 'id_ticket')
			);
			shd_log_action('ignore',
				array(
					'ticket' => $context['ticket_id'],
					'subject' => $ticketinfo['subject'],
				)
			);
			break;
		case 'ignore_off';
			if (!shd_allowed_to('shd_ignore_ticket_any', $ticketinfo['dept']) && (!$ticket_starter || !shd_allowed_to('shd_ignore_ticket_own', $ticketinfo['dept'])))
				fatal_lang_error('cannot_unmonitor_ticket', false);

			$smcFunc['db_query']('', '
				DELETE FROM {db_prefix}helpdesk_notify_override
				WHERE id_member = {int:member}
					AND id_ticket = {int:ticket}',
				array(
					'member' => $context['user']['id'],
					'ticket' => $context['ticket_id'],
				)
			);
			shd_log_action('unignore',
				array(
					'ticket' => $context['ticket_id'],
					'subject' => $ticketinfo['subject'],
				)
			);
			break;
		default:
			break;
	}

	redirectexit('action=helpdesk;sa=ticket;ticket=' . $context['ticket_id']);
}

function shd_query_monitor_list($ticket_id)
{
	global $smcFunc;

	$members = array(
		'always_notify' => array(),
		'never_notify' => array(),
	);

	$query = $smcFunc['db_query']('', '
		SELECT id_member, notify_state
		FROM {db_prefix}helpdesk_notify_override
		WHERE id_ticket = {int:ticket}',
		array(
			'ticket' => $ticket_id,
		)
	);

	while ($row = $smcFunc['db_fetch_assoc']($query))
	{
		if ($row['notify_state'] == NOTIFY_ALWAYS)
			$members['always_notify'][] = $row['id_member'];
		elseif ($row['notify_state'] == NOTIFY_NEVER)
			$members['never_notify'][] = $row['id_member'];
	}
	$smcFunc['db_free_result']($query);

	return $members;
}

/**
 *	Returns a list of all the possible people that would want notification, that can see the ticket we're interested in.
 *
 *	@param int $dept The department the given ticket is in.
 *	@param bool $private Whether the given ticket is private or not.
 *	@param int $ticket_starter User id of the ticket starter.
 *	@param bool $exclude_admin If true, exclude forum admins from the list of possible candidates.
 *	@return array An array of user ids of the staff members (and ticket starter) that can see tickets, matching the given criteria of department, privacy and permissions.
*/
function shd_get_visible_list($dept, $private, $ticket_starter = 0, $include_admin = true, $include_current_user = false)
{
	global $smcFunc, $context;

	// So, the list of possible people will include the staff list and the ticket starter if provided.
	$people = shd_members_allowed_to('shd_staff', $dept);
	if (!empty($ticket_starter))
		$people[] = $ticket_starter;

	// Firstly, figure out who can see tickets that aren't their own. (Or that they can see their own and this is their ticket)
	$see_tickets = shd_members_allowed_to('shd_view_ticket_any', $dept);
	if (!empty($ticket_starter) && !in_array($ticket_starter, $see_tickets))
	{
		$see_own = shd_members_allowed_to('shd_view_ticket_own', $dept);
		if (in_array($ticket_starter, $see_own))
			$see_tickets[] = $ticket_starter;
	}
	$people = array_intersect($people, $see_tickets);

	// If the ticket is private, we need to figure out who can see it. This is tricky.
	if ($private)
	{
		$private = array_merge(shd_members_allowed_to('shd_view_ticket_private_any', $dept), shd_members_allowed_to('shd_alter_privacy_any', $dept));
		// That covers for those who can see any, what about those who can see own (but not any)?
		if (!empty($ticket_starter) && !in_array($ticket_starter, $private))
		{
			$own_private = array_merge(shd_members_allowed_to('shd_view_ticket_private_own', $dept), shd_members_allowed_to('shd_alter_privacy_own', $dept));
			if (in_array($ticket_starter, $own_private))
				$private[] = $ticket_starter;
		}
		$people = array_intersect($people, $private);
	}

	if (!$include_admin)
	{
		$query = $smcFunc['db_query']('', '
			SELECT id_member
			FROM {db_prefix}members
			WHERE id_group = 1
				OR FIND_IN_SET(1, additional_groups)',
			array()
		);

		$context['list_admin_exclude'] = array();
		while ($row = $smcFunc['db_fetch_row']($query))
			$context['list_admin_exclude'][] = $row[0];

		$smcFunc['db_free_result']($query);
		$people = array_diff($people, $context['list_admin_exclude']);
	}

	if (!$include_current_user)
		$people = array_diff($people, array($context['user']['id']));

	return $people;
}


text:
package org.apache.jena.ttl;

public class RelativeURIException extends JenaURIException
{
    public RelativeURIException(String msg) { super(msg) ; }
}


text:
<!doctype html>

  <meta charset="UTF-8">
  <title>Example - example-$filter-production</title>

{{ originalText }}

{{ filteredText }}


text:
declare module 'ora' {
    type Color = 'black' | 'red' | 'green' | 'yellow' | 'blue' | 'magenta' | 'cyan' | 'white' | 'gray';
    type Text = string;
    interface Options {
        text?: Text;
        spinner?: string | { interval?: number; frames: string[]; }
        color?: Color;
        stream?: any;
    }
    interface Instance {
        start(): void;
        stop(): void;
        clear(): void;
        frame(): void;
        render(): void;
        text: Text;
        color: Color;
    }
    function ora(options: Options | Text): Instance;
    export = ora;
}


text:
package org.drools.model.functions.temporal;

import java.util.concurrent.TimeUnit;

import static org.drools.model.functions.temporal.TimeUtil.unitToLong;

public class FinishesPredicate extends AbstractTemporalPredicate {

    private final long endDev;

    public FinishesPredicate() {
        this(0);
    }

    public FinishesPredicate(long endDev, TimeUnit endDevTimeUnit) {
        this( unitToLong(endDev, endDevTimeUnit) );
    }

    private FinishesPredicate(long endDev) {
        this.endDev = endDev;
    }

    @Override
    public String toString() {
        return (negated ? "not " : "") + "finishes[" + endDev + "]";
    }

    @Override
    public Interval getInterval() {
        return negated ? new Interval( Interval.MIN, Interval.MAX ) : new Interval( 0, Interval.MAX );
    }

    @Override
    public boolean evaluate(long start1, long duration1, long end1, long start2, long duration2, long end2) {

        long distStart = start1 - start2;
        long distEnd = Math.abs( end2 - end1 );
        return negated ^ (distStart > 0 && distEnd <= this.endDev);
    }
}


text:
package org.asteriskjava.pbx.asterisk.wrap.events;

import org.asteriskjava.pbx.Channel;
import org.asteriskjava.pbx.InvalidChannelName;
import org.asteriskjava.pbx.PBXFactory;
import org.asteriskjava.pbx.internal.core.AsteriskPBX;

public class VarSetEvent extends ManagerEvent implements ChannelEvent
{
    private static final long serialVersionUID = 1L;

    /**
     * The name of the channel.
     */
    private final Channel _channel;

    private String _variableName;

    private String _variableValue;

    public VarSetEvent(final org.asteriskjava.manager.event.VarSetEvent event) throws InvalidChannelName
    {
        super(event);
        final AsteriskPBX pbx = (AsteriskPBX) PBXFactory.getActivePBX();
        this._channel = pbx.internalRegisterChannel(event.getChannel(), event.getUniqueId());
        this._variableName = event.getVariable();
        this._variableValue = event.getValue();
    }

    @Override
    public Channel getChannel()
    {
        return this._channel;
    }

    public String getVariableName()
    {
        return this._variableName;
    }

    public String getVariableValue()
    {
        return this._variableValue;
    }

}


text:
package controllers;

import models.StateModel.PauseMenuModel;
import utilities.GameStateManager;
import utilities.KeyCommand.KeyCommand;
import utilities.State.State;
import views.View;

import java.awt.event.KeyEvent;

/**
 * Created by Andy on 4/16/2016.
 */
public class PauseMenuController extends Controller {

    private PauseMenuModel model;
    private int currentPoint;

    public PauseMenuController(PauseMenuModel model, GameStateManager gsm){
        super(gsm);
        this.model = model;
        currentPoint = 0;
    }

    @Override
    public void loadKeyCommand() {
        keyMap.put(KeyEvent.VK_ESCAPE, new KeyCommand(){
            @Override
            public void execute() {
                statsStateTransition();
            }
        });

        keyMap.put(KeyEvent.VK_U, new KeyCommand(){
            @Override
            public void execute() {
                model.up();
            }
        });

        keyMap.put(KeyEvent.VK_J, new KeyCommand() {
            @Override
            public void execute() {
                model.down();
            }
        });

        keyMap.put(KeyEvent.VK_UP, new KeyCommand(){
            @Override
            public void execute() {
                model.up();
            }
        });

        keyMap.put(KeyEvent.VK_DOWN, new KeyCommand() {
            @Override
            public void execute() {
                model.down();
            }
        });
    }

    @Override
    public void updateModel() {

    }

    private void statsStateTransition() {
        gsm.removeState();
        View view = gsm.getCurrentView();
        Controller controller = gsm.getCurrentController();
        State state = new State(view, controller);
        gsm.changeState(state);
    }

}


text:
package com.devahoy.learn30androidlibraries.day24;

import org.andengine.engine.camera.Camera;
import org.andengine.engine.options.EngineOptions;
import org.andengine.engine.options.ScreenOrientation;
import org.andengine.engine.options.resolutionpolicy.IResolutionPolicy;
import org.andengine.engine.options.resolutionpolicy.RatioResolutionPolicy;
import org.andengine.entity.scene.Scene;
import org.andengine.entity.sprite.AnimatedSprite;
import org.andengine.entity.sprite.Sprite;
import org.andengine.opengl.texture.ITexture;
import org.andengine.opengl.texture.TextureOptions;
import org.andengine.opengl.texture.atlas.bitmap.BitmapTextureAtlas;
import org.andengine.opengl.texture.atlas.bitmap.BitmapTextureAtlasTextureRegionFactory;
import org.andengine.opengl.texture.bitmap.AssetBitmapTexture;
import org.andengine.opengl.texture.region.ITextureRegion;
import org.andengine.opengl.texture.region.TextureRegionFactory;
import org.andengine.opengl.texture.region.TiledTextureRegion;
import org.andengine.ui.activity.SimpleBaseGameActivity;

import java.io.IOException;

public class AndEngineActivity extends SimpleBaseGameActivity {

    private static final int CAMERA_WIDTH = 800;
    private static final int CAMERA_HEIGHT = 480;

    private Camera mCamera;

    private ITextureRegion mTextureRegion;
    private ITexture mTexture;

    private AnimatedSprite mAnimatedSprite;
    private TiledTextureRegion mTiledTextureRegion;
    private BitmapTextureAtlas mBitmapTextureAtlas;

    @Override
    public EngineOptions onCreateEngineOptions() {
        mCamera = new Camera(0, 0, CAMERA_WIDTH, CAMERA_HEIGHT);
        IResolutionPolicy resolutionPolicy =
                new RatioResolutionPolicy(CAMERA_WIDTH, CAMERA_HEIGHT);

        return new EngineOptions(true,
                ScreenOrientation.LANDSCAPE_FIXED,
                resolutionPolicy,
                mCamera);
    }

    @Override
    protected void onCreateResources() throws IOException {
        mTexture = new AssetBitmapTexture(getTextureManager(), getAssets(), "player.png");
        mTextureRegion = TextureRegionFactory.extractFromTexture(mTexture);
        mTexture.load();

        mBitmapTextureAtlas = new BitmapTextureAtlas(getTextureManager(), 100, 100, TextureOptions.BILINEAR);
        mTiledTextureRegion = BitmapTextureAtlasTextureRegionFactory
                .createTiledFromAsset(mBitmapTextureAtlas, getAssets(), "npc-oldman1.png", 0, 0, 4, 1);

        mBitmapTextureAtlas.load();
    }

    @Override
    protected Scene onCreateScene() {
        Scene scene = new Scene();
        scene.getBackground().setColor(0.2f, 0.5f, 0.7f);

        Sprite sprite = new Sprite(mCamera.getWidth() / 2, mCamera.getHeight() / 2,
                mTextureRegion, getVertexBufferObjectManager());
        scene.attachChild(sprite);

        mAnimatedSprite = new AnimatedSprite(200, 200, mTiledTextureRegion, this.getVertexBufferObjectManager());
        long[] frameDuration = {500, 500, 500, 500};
        mAnimatedSprite.animate(frameDuration);
        scene.attachChild(mAnimatedSprite);

        return scene;
    }

}


text:
package org.kie.workbench.common.widgets.metadata.client.widget;

import com.github.gwtbootstrap.client.ui.Button;
import com.github.gwtbootstrap.client.ui.ButtonGroup;
import com.github.gwtbootstrap.client.ui.TextBox;
import com.github.gwtbootstrap.client.ui.constants.ButtonType;
import com.github.gwtbootstrap.client.ui.constants.IconType;
import com.github.gwtbootstrap.client.ui.resources.ButtonSize;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Widget;
import org.guvnor.common.services.shared.metadata.model.Metadata;
import org.kie.workbench.common.widgets.metadata.client.resources.Images;
import org.kie.workbench.common.widgets.metadata.client.resources.i18n.MetadataConstants;
import org.uberfire.ext.widgets.common.client.common.SmallLabel;
import javax.inject.Inject;

import com.google.gwt.user.client.ui.IsWidget;
import com.google.gwt.user.client.ui.Widget;
import org.guvnor.common.services.shared.metadata.model.Metadata;
import org.jboss.errai.ioc.client.container.IOC;
import org.jboss.errai.ioc.client.container.IOCBeanDef;

/**
 * This is a viewer/selector for tags.
 * It will show a list of tags currently applicable, and allow you to
 * remove/add to them.
 * <p/>
 * It is intended to work with the meta data form.
 */
public class TagWidget implements IsWidget {

    private Metadata data;

    private TagWidgetView view;

    private boolean readOnly;

    @Inject
    public void setView( TagWidgetView view ) {
        this.view = view;
        view.setPresenter( this );
    }

    /**
     * @param d The meta data.
     * @param readOnly If it is to be non editable.
     */
    public void setContent( Metadata d,
                            boolean readOnly ) {
        this.data = d;

        this.readOnly = readOnly;

        view.setReadOnly(readOnly);

        loadData();
    }

    public void onAddTags( String text ) {
        if (text != null) {
            String[] tags = text.split( " " );
            for (String tag : tags) {
                if (!data.getTags().contains( tag )) {
                    data.addTag( tag );
                    view.addTag( tag, readOnly );
                }
            }
        }
    }

    public void onRemoveTag( String tag ) {
        data.getTags().remove( tag );
        loadData();
    }

    public void loadData( ) {
        view.clear();
        for (String tag : data.getTags()) {
            view.addTag( tag, readOnly );
        }
    }

    @Override
    public Widget asWidget() {
        if (view == null) initView();
        return view.asWidget();
    }

    // TODO: remove this method when the MetaDataWidget is moved to MVP
    private void initView() {
        setView( new TagWidgetViewImpl() );
    }
}