ElectronicBabylonianLiterature/ebl-frontend

View on GitHub
src/fragmentarium/ui/image-annotation/annotation-tool/mapTokensToAnnotationTokens.ts

Summary

Maintainability
A
25 mins
Test Coverage
B
84%
import {
  CompoundGrapheme,
  effectiveEnclosure,
  isStrictlyPartiallyEnclosed,
  NamedSign,
  Token,
} from 'transliteration/domain/token'
import { AnnotationTokenType } from 'fragmentarium/domain/annotation'
import { isNamedSign } from 'transliteration/domain/type-guards'
import { AbstractLine } from 'transliteration/domain/abstract-line'
import { ColumnAtLine, SurfaceAtLine } from 'transliteration/domain/at-lines'
import { RulingDollarLine } from 'transliteration/domain/dollar-lines'
import { Text } from 'transliteration/domain/text'
import { AnnotationToken } from 'fragmentarium/domain/annotation-token'

function typeToAnnotationTokenType(
  tokenType: string
): AnnotationTokenType.HasSign | AnnotationTokenType.Number {
  return tokenType === 'Number'
    ? AnnotationTokenType.Number
    : AnnotationTokenType.HasSign
}

function namedSignTokenToAnnotationToken(
  token: NamedSign,
  path: readonly number[]
): AnnotationToken {
  let tokenType: AnnotationTokenType
  if (effectiveEnclosure(token).includes('BROKEN_AWAY')) {
    return AnnotationToken.initDeactive(
      '',
      AnnotationTokenType.CompletelyBroken,
      path
    )
  } else if (token.flags.includes('#')) {
    tokenType = AnnotationTokenType.Damaged
  } else if (isStrictlyPartiallyEnclosed(token, 'BROKEN_AWAY')) {
    tokenType = AnnotationTokenType.PartiallyBroken
  } else {
    tokenType = typeToAnnotationTokenType(token.type)
  }
  return AnnotationToken.initActive(
    token.cleanValue,
    tokenType,
    token.value,
    path,
    token.name,
    token.subIndex
  )
}

function compoundGraphemeToAnnotationToken(
  token: CompoundGrapheme,
  path: readonly number[]
): AnnotationToken {
  const compoundGrapheme = token as CompoundGrapheme
  if (compoundGrapheme.enclosureType.includes('BROKEN_AWAY')) {
    return AnnotationToken.initDeactive(
      '',
      AnnotationTokenType.CompletelyBroken,
      path
    )
  } else {
    return AnnotationToken.initActive(
      compoundGrapheme.cleanValue,
      AnnotationTokenType.CompoundGrapheme,
      compoundGrapheme.value,
      path,
      compoundGrapheme.cleanValue,
      1
    )
  }
}

function unannotatableTokenToAnnotationToken(
  token: Token,
  path: readonly number[]
): AnnotationToken {
  if (token.enclosureType.includes('BROKEN_AWAY')) {
    return AnnotationToken.initDeactive(
      '',
      AnnotationTokenType.CompletelyBroken,
      path
    )
  } else {
    return AnnotationToken.initDeactive(
      token.value,
      AnnotationTokenType.Disabled,
      path
    )
  }
}

function tokenToAnnotationToken(
  token: Token,
  path: readonly number[]
): AnnotationToken {
  if ('sign' in token && token.sign) {
    return AnnotationToken.initFromTokenSign(
      token.cleanValue,
      token.value,
      path,
      token.sign.cleanValue
    )
  }
  if (isNamedSign(token)) {
    return namedSignTokenToAnnotationToken(token, path)
  }

  if (token.type === 'UnclearSign') {
    return AnnotationToken.unclear(path)
  }
  if (token.type === 'CompoundGrapheme') {
    const compoundGrapheme = token as CompoundGrapheme
    return compoundGraphemeToAnnotationToken(compoundGrapheme, path)
  } else {
    throw Error('Unknown token type')
  }
}

function mapToken(
  token: Token,
  path: readonly number[]
): AnnotationToken | AnnotationToken[] {
  if (
    [
      'Reading',
      'Logogram',
      'CompoundGrapheme',
      'Number',
      'UnclearSign',
    ].includes(token.type)
  ) {
    return tokenToAnnotationToken(token, path)
  } else if (token.parts) {
    return token.parts.flatMap((part: Token, index: number) =>
      mapToken(part, [...path, index])
    )
  } else {
    return unannotatableTokenToAnnotationToken(token, path)
  }
}
function surfaceAtLineToAnnotationToken(
  line: SurfaceAtLine,
  path: readonly number[]
): AnnotationToken {
  return AnnotationToken.initActive(
    line.label.surface,
    AnnotationTokenType.SurfaceAtLine,
    line.label.surface.toLowerCase(),
    path
  )
}
function columnAtLineToAnnotationToken(
  line: ColumnAtLine,
  path: readonly number[]
): AnnotationToken {
  return AnnotationToken.initActive(
    line.label.column.toString(),
    AnnotationTokenType.ColumnAtLine,
    `column ${line.label.column.toString()}`,
    path
  )
}

function rulingDollarLineToAnnotationToken(
  line: RulingDollarLine,
  path: readonly number[]
): AnnotationToken {
  return AnnotationToken.initActive(
    line.number,
    AnnotationTokenType.RulingDollarLine,
    `${line.number.toLowerCase()} ruling`,
    path
  )
}

function structureLineToTokens(
  line: AbstractLine,
  lineNumber: number
): readonly AnnotationToken[] {
  const results: AnnotationToken[] = [
    AnnotationToken.initDeactive(line.prefix, AnnotationTokenType.Disabled, [
      lineNumber,
    ]),
  ]
  if (line instanceof SurfaceAtLine) {
    results.push(surfaceAtLineToAnnotationToken(line, [lineNumber, 0]))
  } else if (line instanceof ColumnAtLine) {
    results.push(columnAtLineToAnnotationToken(line, [lineNumber, 0]))
  } else if (line instanceof RulingDollarLine) {
    results.push(rulingDollarLineToAnnotationToken(line, [lineNumber, 0]))
  } else {
    results.push(
      ...line.content.flatMap((token, index) =>
        mapToken(token, [lineNumber, index])
      )
    )
  }
  return results
}

export function createAnnotationTokens(
  text: Text
): ReadonlyArray<ReadonlyArray<AnnotationToken>> {
  return text.lines.map((line, lineNumber) => [
    ...structureLineToTokens(line, lineNumber),
  ])
}