Skip to content
This repository has been archived by the owner on Jun 17, 2023. It is now read-only.

Commit

Permalink
Get rid of traits in Result
Browse files Browse the repository at this point in the history
  • Loading branch information
alexander-myltsev committed Jun 30, 2018
1 parent 7bea6d5 commit 9bf2248
Show file tree
Hide file tree
Showing 15 changed files with 144 additions and 95 deletions.
Expand Up @@ -6,6 +6,7 @@ public class ParserJava {
public static void main(String[] args) {
String jsonStr = ScientificNameParser.instance()
.fromString("Homo sapiens L.")
.jsonRenderer()
.renderCompactJson();
System.out.println(jsonStr);
}
Expand Down
Expand Up @@ -3,6 +3,6 @@ package org.globalnames.parser.examples
import org.globalnames.parser.ScientificNameParser.{instance => snp}

object ParserScala extends App {
val jsonStr = snp.fromString("Homo sapiens L.").renderCompactJson
val jsonStr = snp.fromString("Homo sapiens L.").jsonRenderer.renderCompactJson
println(jsonStr)
}
Expand Up @@ -2,36 +2,66 @@ package org.globalnames
package parser

import formatters._
import org.json4s.JsonAST.JValue
import org.parboiled2._
import shapeless._

import scala.util.{Failure, Success}

abstract class ScientificNameParser {
import ScientificNameParser.Result
class Result(val preprocessorResult: Preprocessor.Result,
val scientificName: ScientificName,
val version: String,
val warnings: Vector[Warning] = Vector())

class ResultFat(val result: Result) {

val canonizer: Canonizer = new Canonizer(result)
val normalizer: Normalizer = new Normalizer(result)
val details: Details = new Details(result, normalizer)
val positions: Positions = new Positions(result)
val jsonRenderer: JsonRenderer =
new JsonRenderer(result, result.version, canonizer, normalizer, positions, details)
val delimitedStringRenderer: DelimitedStringRenderer =
new DelimitedStringRenderer(result, canonizer, normalizer)

def json(showCanonicalUuid: Boolean = false): JValue =
jsonRenderer.json(showCanonicalUuid)

def delimitedString(delimiter: String = "\t"): String =
delimitedStringRenderer.delimitedString(delimiter)
}

object ResultFat {
def apply(result: Result): ResultFat = {
new ResultFat(result)
}
}

abstract class ScientificNameParser {
val version: String

def fromString(input: String): Result =
def fromString(input: String): ResultFat =
fromString(input, collectParsingErrors = false)

def fromString(input: String,
collectParsingErrors: Boolean): Result = {
collectParsingErrors: Boolean): ResultFat = {
val preprocessorResult = Preprocessor.process(Option(input).getOrElse(""))
if (preprocessorResult.virus || preprocessorResult.noParse) {
Result(preprocessorResult, ScientificName(), version)
} else {
val parser = new Parser(preprocessorResult, collectParsingErrors)
parser.sciName.run() match {
case Success(scientificName :: warnings :: HNil) =>
Result(preprocessorResult, scientificName, version, warnings)
case Failure(err: ParseError) if collectParsingErrors =>
Console.err.println(err.format(preprocessorResult.verbatim))
Result(preprocessorResult, ScientificName(), version)
case Failure(err) =>
Result(preprocessorResult, ScientificName(), version)
val result =
if (preprocessorResult.virus || preprocessorResult.noParse) {
new Result(preprocessorResult, ScientificName(), version)
} else {
val parser = new Parser(preprocessorResult, collectParsingErrors)
parser.sciName.run() match {
case Success(scientificName :: warnings :: HNil) =>
new Result(preprocessorResult, scientificName, version, warnings)
case Failure(err: ParseError) if collectParsingErrors =>
Console.err.println(err.format(preprocessorResult.verbatim))
new Result(preprocessorResult, ScientificName(), version)
case Failure(_) =>
new Result(preprocessorResult, ScientificName(), version)
}
}
}
ResultFat(result)
}
}

Expand All @@ -40,21 +70,4 @@ object ScientificNameParser {
final val instance = new ScientificNameParser {
override final val version: String = BuildInfo.version
}

case class Result(preprocessorResult: Preprocessor.Result, scientificName: ScientificName,
version: String, warnings: Vector[Warning] = Vector.empty)
extends JsonRenderer with DelimitedStringRenderer with Details
with Positions with Normalizer with Canonizer {

private[parser] def stringOf(astNode: AstNode): String =
preprocessorResult.unescaped.substring(astNode.pos.start, astNode.pos.end)

private[parser] def namesEqual(name1: Name, name2: Name): Boolean = {
val name1str = stringOf(name1)
val name2str = stringOf(name2)
!name1.uninomial.implied && !name2.uninomial.implied &&
(name1str.startsWith(name2str) ||
(name2str.endsWith(".") && name1str.startsWith(name2str.substring(0, name2str.length - 1))))
}
}
}
Expand Up @@ -16,7 +16,9 @@ case class Canonical(value: String) extends AnyVal {
def id: UUID = UuidGenerator.generate(value)
}

trait Canonizer { parsedResult: ScientificNameParser.Result =>
class Canonizer(parsedResult: Result) extends CommonOps {
protected val preprocessorResult: Preprocessor.Result = parsedResult.preprocessorResult

private val canonicalRanked = computeCanonical(showRanks = true)
private val canonicalRankedLess = computeCanonical(showRanks = false)

Expand Down
@@ -0,0 +1,17 @@
package org.globalnames.parser
package formatters

trait CommonOps {
protected val preprocessorResult: Preprocessor.Result

private[parser] def stringOf(astNode: AstNode): String =
preprocessorResult.unescaped.substring(astNode.pos.start, astNode.pos.end)

private[parser] def namesEqual(name1: Name, name2: Name): Boolean = {
val name1str = stringOf(name1)
val name2str = stringOf(name2)
!name1.uninomial.implied && !name2.uninomial.implied &&
(name1str.startsWith(name2str) ||
(name2str.endsWith(".") && name1str.startsWith(name2str.substring(0, name2str.length - 1))))
}
}
Expand Up @@ -5,8 +5,10 @@ package formatters
import scalaz._
import Scalaz._

trait DelimitedStringRenderer {
parserResult: ScientificNameParser.Result with Normalizer =>
class DelimitedStringRenderer(parserResult: Result,
canonizer: Canonizer,
normalizer: Normalizer) extends CommonOps {
protected val preprocessorResult: Preprocessor.Result = parserResult.preprocessorResult

protected[globalnames] val ambiguousAuthorship: Boolean = {
val isAmbiguousOpt = for {
Expand All @@ -18,12 +20,12 @@ trait DelimitedStringRenderer {

protected[globalnames] val authorshipDelimited: Option[String] =
(!ambiguousAuthorship).option {
parserResult.scientificName.authorship.flatMap { normalizedAuthorship }
parserResult.scientificName.authorship.flatMap { normalizer.normalizedAuthorship }
}.flatten

val yearDelimited: Option[String] =
(!ambiguousAuthorship).option {
val year: Option[Year] = scientificName.namesGroup.flatMap { ng =>
val year: Option[Year] = parserResult.scientificName.namesGroup.flatMap { ng =>
val infraspeciesYear = ng.name.infraspecies.flatMap {
_.group.last.authorship.flatMap { _.authors.authors.year }
}
Expand All @@ -32,7 +34,7 @@ trait DelimitedStringRenderer {
val uninomialYear = ng.name.uninomial.authorship.flatMap { _.authors.authors.year }
infraspeciesYear <+> speciesYear <+> uninomialYear
}
year.map { normalizedYear }
year.map { normalizer.normalizedYear }
}.flatten

/**
Expand All @@ -45,8 +47,8 @@ trait DelimitedStringRenderer {
def delimitedString(delimiter: String = "\t"): String = {
val uuid = parserResult.preprocessorResult.id
val verbatim = parserResult.preprocessorResult.verbatim
val canonical = parserResult.canonized().orZero
val canonicalExtended = parserResult.canonized(showRanks = true).orZero
val canonical = canonizer.canonized().orZero
val canonicalExtended = canonizer.canonized(showRanks = true).orZero
val quality = parserResult.scientificName.quality
Seq(uuid, verbatim, canonical, canonicalExtended,
authorshipDelimited.orZero, yearDelimited.orZero, quality).mkString(delimiter)
Expand All @@ -62,7 +64,9 @@ trait DelimitedStringRenderer {
Seq()
} else {
parserResult.scientificName.authorship.map { as =>
val authorsNames = as.authors.authors.authors.map { a => a.words.map { w => stringOf(w) } }
val authorsNames = as.authors.authors.authors.map {
a => a.words.map { w => stringOf(w) }
}
val authorsExNames = as.authors.authorsEx.map { at => at.authors.map { a =>
a.words.map { w => stringOf(w) }
}}.getOrElse(Seq())
Expand Down
@@ -1,13 +1,15 @@
package org.globalnames.parser.formatters
package org.globalnames.parser
package formatters

import org.globalnames.parser._
import org.json4s.JsonAST.{JBool, JNothing}
import org.json4s.JsonDSL._
import org.json4s.{JObject, JString, JValue}

import scalaz.Scalaz._

trait Details { parsedResult: ScientificNameParser.Result =>
class Details(parsedResult: Result,
normalizer: Normalizer) extends CommonOps {
protected val preprocessorResult: Preprocessor.Result = parsedResult.preprocessorResult

def detailed: JValue = {
def detailedNamesGroup(namesGroup: NamesGroup): JValue = {
Expand Down Expand Up @@ -77,7 +79,7 @@ trait Details { parsedResult: ScientificNameParser.Result =>
}

def detailedAuthorship(as: Authorship): JObject = {
def detailedAuthor(a: Author): String = normalizedAuthor(a)
def detailedAuthor(a: Author): String = normalizer.normalizedAuthor(a)
def detailedAuthorsTeam(at: AuthorsTeam): JObject = {
val res: JObject = "authors" -> at.authors.map(detailedAuthor)
at.years.foldLeft(res) { (r, y) => r ~ ("year" -> detailedYear(y)) }
Expand All @@ -88,10 +90,10 @@ trait Details { parsedResult: ScientificNameParser.Result =>
("emend_authors" -> ag.authorsEmend.map { at => detailedAuthorsTeam(at) })

"authorship" -> (
("value" -> parsedResult.normalizedAuthorship(as)) ~
("value" -> normalizer.normalizedAuthorship(as)) ~
("basionym_authorship" -> as.basionym.map(detailedAuthorsGroup)) ~
("combination_authorship" -> as.combination.map(detailedAuthorsGroup))
)
)
}

parsedResult.scientificName.namesGroup.map(detailedNamesGroup)
Expand Down
@@ -1,24 +1,30 @@
package org.globalnames.parser.formatters
package org.globalnames.parser
package formatters

import org.globalnames.parser.ScientificNameParser
import org.json4s.JsonAST.{JArray, JNothing, JValue}
import org.json4s.JsonDSL._
import org.json4s.jackson.JsonMethods

import scalaz._
import Scalaz._

trait JsonRenderer { parserResult: ScientificNameParser.Result =>
class JsonRenderer(parserResult: Result,
version: String,
canonizer: Canonizer,
normalizer: Normalizer,
positions: Positions,
details: Details) {

def json(showCanonicalUuid: Boolean = false): JValue = {
val canonical = parserResult.canonized()
def json(showCanonicalUuid: Boolean): JValue = {
val canonical = canonizer.canonized()
val parsed = canonical.isDefined

val canonicalName: JValue =
if (parsed) {
("id" -> showCanonicalUuid.option { canonizedUuid().map { _.id.toString } }.join) ~
val canonizedUuidStrOpt = canonizer.canonizedUuid().map { _.id.toString }
("id" -> showCanonicalUuid.option { canonizedUuidStrOpt }.join) ~
("value" -> canonical) ~
("value_ranked" -> parserResult.canonized(showRanks = true))
("value_ranked" -> canonizer.canonized(showRanks = true))
} else JNothing

val quality = canonical.map { _ => parserResult.scientificName.quality }
Expand All @@ -31,7 +37,7 @@ trait JsonRenderer { parserResult: ScientificNameParser.Result =>
warningsJArr.some
}
val positionsJson: Option[JArray] = parsed.option {
parserResult.positioned.map { position =>
positions.positioned.map { position =>
JArray(List(position.nodeName,
parserResult.preprocessorResult.verbatimPosAt(position.start),
parserResult.preprocessorResult.verbatimPosAt(position.end)))
Expand All @@ -45,14 +51,14 @@ trait JsonRenderer { parserResult: ScientificNameParser.Result =>
("quality_warnings" -> qualityWarnings) ~
("parser_version" -> version) ~
("verbatim" -> parserResult.preprocessorResult.verbatim) ~
("normalized" -> parserResult.normalized) ~
("normalized" -> normalizer.normalized) ~
("canonical_name" -> canonicalName) ~
("hybrid" -> parserResult.scientificName.hybrid) ~
("surrogate" -> parserResult.scientificName.surrogate) ~
("unparsed_tail" -> parserResult.scientificName.unparsedTail) ~
("virus" -> parserResult.preprocessorResult.virus) ~
("bacteria" -> parserResult.scientificName.bacteria) ~
("details" -> parserResult.detailed) ~
("details" -> details.detailed) ~
("positions" -> positionsJson))
}

Expand Down
@@ -1,10 +1,12 @@
package org.globalnames.parser.formatters
package org.globalnames.parser
package formatters

import org.globalnames.parser._
import scalaz.{Name => _, _}
import Scalaz._

trait Normalizer { parsedResult: ScientificNameParser.Result =>
class Normalizer(parsedResult: Result) extends CommonOps {
private val canonizer: Canonizer = new Canonizer(parsedResult)
protected val preprocessorResult: Preprocessor.Result = parsedResult.preprocessorResult

def normalized: Option[String] = {

Expand Down Expand Up @@ -37,7 +39,7 @@ trait Normalizer { parsedResult: ScientificNameParser.Result =>
def normalizedUninomial(u: Uninomial): Option[String] =
(!u.implied).option {
val parts =
Vector(canonizedUninomial(u, showRanks = true),
Vector(canonizer.canonizedUninomial(u, showRanks = true),
u.authorship.flatMap { normalizedAuthorship })
parts.flatten.mkString(" ")
}
Expand Down
Expand Up @@ -4,7 +4,7 @@ import org.globalnames.parser._

import scalaz.Scalaz._

trait Positions { parsedResult: ScientificNameParser.Result =>
class Positions(parsedResult: Result) {

import Positions.Position

Expand Down

0 comments on commit 9bf2248

Please sign in to comment.