digitalheir/bibliography-js

View on GitHub
depr/bibtex/field_value/AuthorValue.js

Summary

Maintainability
C
1 day
Test Coverage
import {diacritics, specialChars} from './specialCharsHandlers'
import StringValue from './StringValue'
import PersonName from '../../bibliography/PersonName'
import {startsWithLowerCase} from './utils'

function isPartOfName(char) {
  return (char == ',' || char.match(/\s/));
}

function splitOnAnd(authorTokens) {
  return authorTokens.reduce((prev, curr)=> {
    //console.log(curr);
    if (curr.length == 1 && curr[0] == 'and') prev.push([]);
    else prev[prev.length - 1].push(curr);
    return prev;
  }, [[]]);
}


function firstVonLast(authorTokens) {
  let vonStartInclusive = -1;
  let vonEndExclusive = -1;
  let firstNameEndExclusive = -1;
  for (let i = 0; i < authorTokens.length - 1; i++) {// -1 because last word must be lastName
    //console.log("STARLOW", (authorTokens[i]));
    //console.log("STARLOW", startsWithLowerCase(authorTokens[i]));
    if (startsWithLowerCase(authorTokens[i])) {
      if (vonStartInclusive < 0) vonStartInclusive = i;
      vonEndExclusive = i + 1;
    }
  }
  if (vonStartInclusive > 0) firstNameEndExclusive = vonStartInclusive;
  else firstNameEndExclusive = authorTokens.length - 1;

  const von = vonStartInclusive > 0 ? getSubStringAsArray(authorTokens, vonStartInclusive, vonEndExclusive) : [];
  const firstName = getSubStringAsArray(authorTokens, 0, firstNameEndExclusive);
  const lastName = getSubStringAsArray(authorTokens, Math.max(vonEndExclusive, firstNameEndExclusive), authorTokens.length);

  return new PersonName(
    firstName,
    von,
    lastName,
    []
  );
}

function vonLastFirst(authorTokens) {
  let commaPos = -1;
  for (let i = 0; i < authorTokens.length; i++)
    if (authorTokens[i].type == ',') {
      commaPos = i;
      break;
    }
  let vonStartInclusive = -1;
  let vonEndExclusive = -1;

  for (let i = 0; i < commaPos; i++)
    if (startsWithLowerCase(authorTokens[i])) {
      if (vonStartInclusive < 0) vonStartInclusive = i;
      vonEndExclusive = i + 1;
    }

  const von = vonStartInclusive > 0 ? getSubStringAsArray(authorTokens, 0, vonEndExclusive) : [];
  const firstName = getSubStringAsArray(authorTokens, commaPos + 1, authorTokens.length);
  const lastName = getSubStringAsArray(authorTokens, Math.max(vonEndExclusive, 0), commaPos);

  return new PersonName(
    firstName,
    von,
    lastName,
    []
  );
}


function getSubStringAsArray(tokens, startIncl, endExcl) {
  let arr = [];
  for (let i = startIncl; i < endExcl; i++) {
    if (!(tokens[i].constructor == Array && tokens[i].length == 0)) arr.push(tokens[i]);
  }
  return arr;
}
function vonLastJrFirst(authorTokens) {
  let commaPos = -1;
  for (let i = 0; i < authorTokens.length; i++)
    if (authorTokens[i].type == ',') {
      commaPos = i;
      break;
    }
  let commaPos2 = -1;
  for (let i = commaPos + 1; i < authorTokens.length; i++)
    if (authorTokens[i].type == ',') {
      commaPos2 = i;
      break;
    }
  let vonStartInclusive = -1;
  let vonEndExclusive = -1;

  for (let i = 0; i < commaPos; i++)
    if (startsWithLowerCase(authorTokens[i])) {
      if (vonStartInclusive < 0) vonStartInclusive = i;
      vonEndExclusive = i + 1;
    }

  const von = vonStartInclusive > 0 ? getSubStringAsArray(authorTokens, 0, vonEndExclusive) : [];
  const firstName = getSubStringAsArray(authorTokens, commaPos2 + 1, authorTokens.length);
  const jr = getSubStringAsArray(authorTokens, commaPos + 1, commaPos2);
  const lastName = getSubStringAsArray(authorTokens, Math.max(vonEndExclusive, 0), commaPos);

  return new PersonName(
    firstName,
    von,
    lastName,
    jr
  );
}

/**
 * BibTEX must be able to distinguish between the different parts of the author field. To that
 * aim, BibTEX recognizes three possible formats:
 * • First von Last;
 * • von Last, First;
 * • von Last, Jr, First.
 *
 * The format to be considered is obtained by counting the number of commas in the name. Here are
 * the characteristics of these formats:
 * @param authorRaw
 */
function parseAuthor(authorRaw) {
  const commaCount = authorRaw.reduce((prev, cur)=> {
      return prev + (cur.type == ',' ? 1 : 0)
    }, 0
  );
  //console.log(commaCount,JSON.stringify(authorRaw));
  switch (commaCount) {
    case 0:
      return firstVonLast(authorRaw);
    case 1:
      return vonLastFirst(authorRaw);
    case 2:
      return vonLastJrFirst(authorRaw);
    default:
      throw new Error("Could not parse author name: found " + commaCount + " commas in " + JSON.stringify(authorRaw));
  }
}
export default class AuthorValue extends StringValue {
  constructor(raw) {
    super(raw);
    const authors = splitOnAnd(this._normalizedRaw);
    this._authors = authors.map(parseAuthor);
  }

  getAuthors() {
    return this._authors;
  }
}