<commit_before>/*
 * BibDB.ts
 *
 * Copyright (C) 2020 by RStudio, PBC
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */
import { BibFieldTypes, NameDictObject, NodeArray, RangeArray, BibField, BibLatexExporter, BibTypes, BibType } from "biblatex-csl-converter";

import { Mark, Node as ProsemirrorNode } from "prosemirror-model";

import { CSL, CSLDate, cslDateToEDTFDate, CSLName } from "../csl";
import { cslTextToProsemirrorNode } from "../csl-text";
import { bibDbToBibTeX } from "../bibtex/bibtex";


export type BibDB = Record<string, EntryObject>;

export interface EntryObject {
  csl_type?: string;
  bib_type: string;
  entry_key: string;
  fields: Record<string, any>;
  incomplete?: boolean;
  unexpected_fields?: Record<string, any>;
  unknown_fields?: Record<string, any>;
}



// This is our wrapper of a typescript BibLaTeX exporter
// https://github.com/fiduswriter/biblatex-csl-converter

// Traditional Form Looks Like:
// author = {{Abbas}, {<NAME>} and {Ibrahim}, {<NAME>} and {Ismail}, {<NAME>}}
//
// Non traditional form looks like:
// author = {given={<NAME>}, family={Abbas} and given={Issa Ghada}, family={Ibrahim} and given={Abdel-<NAME>man}, family={Ismail}}
const kUseTraditionalNameForm = false;

// Generates bibLaTeX for a given CSL object / id
export function toBibLaTeX(id: string, csl: CSL): string | undefined {

  // A BibDB is basically a map of key / EntryObject[] that is
  // used by the exporter to generate BibLaTeX
  const bibDB = cslToBibDB(id, csl);
  if (bibDB) {
    // Use the exported to parse the bibDB and generate bibLaTeX
    const exporter: BibLatexExporter = new BibLatexExporter(bibDB, false, { traditionalNames: kUseTraditionalNameForm });
    const sourceAsBibLaTeX = exporter.parse();

    // Indent any , new lines
    return sourceAsBibLaTeX.replace(/,\n/g, ',\n\t');
  }
  return undefined;
}

export function toBibTeX(id: string, csl: CSL): string | undefined {
  // A BibDB is basically a map of key / EntryObject[] that is
  // used by the exporter to generate BibLaTeX
  const bibDB = cslToBibDB(id, csl);
  if (bibDB) {
    // Use the exported to parse the bibDB and generate bibLaTeX
    const sourceAsBibTeX = bibDbToBibTeX(bibDB);

    // Indent any , new lines
    return sourceAsBibTeX;
  }
  return undefined;
}

// Converts a single CSL item to a bibDB containing
// a single EntryObject representing that CSL item
function cslToBibDB(id: string, csl: CSL): BibDB | undefined {

  const bibType = bibTypeForCSL(csl.type);
  if (bibType) {

    const bibObject: EntryObject = {
      bib_type: bibType[0],
      csl_type: bibType[1].csl,
      entry_key: id,
      'fields': {}
    };

    const enumerableCSL = csl as any;
    sortedKeys(csl).forEach(key => {
      const value: any = enumerableCSL[key];

      const bibFieldDatas = bibFieldForValue(key, csl.type);

      bibFieldDatas?.forEach(bibFieldData => {
        if (bibFieldData) {
          const bibFieldKey = bibFieldData[0];
          const bibField = bibFieldData[1];
          const type = bibField.type;
          let nodeValue: any;
          switch (type) {
            case ('f_date'):
              // f_date = // EDTF 1.0 level 0/1 compliant string. (2000-12-31)
              const cslDate = value as CSLDate;
              if (cslDate) {
                const edtfDate = cslDateToEDTFDate(cslDate);
                if (edtfDate) {
                  nodeValue = edtfDate;
                }
              }
              break;
            case ('f_integer'):
            case ('f_literal'):
            case ('f_long_literal'):
            case ('f_title'):
              // f_integer, f_literal, f_long_literal, f_title = [nodeValue]
              // l_literal = [nodeValue]
              if (value && value.length > 0) {
                nodeValue = textNodes(value);
              }
              break;
            case ('l_literal'):
              // l_literal = [NodeArray]
              if (value && value.length > 0) {
                nodeValue = [textNodes(value)];
              }
              break;
            case ('f_key'):
              // f_key: string | NodeArray (string points to another key 
              // name in BibObject whose value is used for this key)
              if (bibField.options) {
                const options = bibField.options as any;
                Object.keys(options).find(optionKey => {
                  const optionValue: any = options[optionKey];
                  if (optionValue.csl === value) {
                    nodeValue = optionKey;
                    return true;
                  }
                });

                if (!nodeValue) {
                  nodeValue = textNodes(value);
                }
              }

              break;
            case ('l_key'):
              // l_key, list of [string | NodeArray]
              if (bibField.options) {
                const options = bibField.options as any;
                Object.keys(options).find(optionKey => {
                  const optionValue: any = options[optionKey];
                  if (optionValue.csl === value) {
                    nodeValue = [optionKey];
                    return true;
                  }
                });

                if (!nodeValue && value && value.length > 0) {
                  nodeValue = textNodes(value);
                }
              }
              break;
            case ('l_range'):
              // l_range Array<RangeArray>
              const valueStr = value as string;
              const parts = valueStr.split('-');
              const range = rangeArray(parts);
              if (range) {
                nodeValue = [range];
              }
              break;
            case ('f_uri'):
            case ('f_verbatim'):
              // f_uri, f_verbatim: string
              nodeValue = value;
              break;
            case ('l_name'):
              // l_name Array<NameDictObject>
              const names = value as CSLName[];
              nodeValue = names.map(name => {
                const nameDict: NameDictObject = {
                  family: textNodes(name.family),
                  given: textNodes(name.given),
                  literal: name.literal ? textNodes(name.literal) : undefined,
                };<commit_msg>If author family or given names are omitted, omit the text node altogether<commit_after>/*
 * BibDB.ts
 *
 * Copyright (C) 2020 by RStudio, PBC
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */
import { BibFieldTypes, NameDictObject, NodeArray, RangeArray, BibField, BibLatexExporter, BibTypes, BibType } from "biblatex-csl-converter";

import { Mark, Node as ProsemirrorNode } from "prosemirror-model";

import { CSL, CSLDate, cslDateToEDTFDate, CSLName } from "../csl";
import { cslTextToProsemirrorNode } from "../csl-text";
import { bibDbToBibTeX } from "../bibtex/bibtex";


export type BibDB = Record<string, EntryObject>;

export interface EntryObject {
  csl_type?: string;
  bib_type: string;
  entry_key: string;
  fields: Record<string, any>;
  incomplete?: boolean;
  unexpected_fields?: Record<string, any>;
  unknown_fields?: Record<string, any>;
}



// This is our wrapper of a typescript BibLaTeX exporter
// https://github.com/fiduswriter/biblatex-csl-converter

// Traditional Form Looks Like:
// author = {{Abbas}, {<NAME>} and {Ibrahim}, {<NAME>} and {Ismail}, {<NAME>}}
//
// Non traditional form looks like:
// author = {given={<NAME>}, family={Abbas} and given={<NAME>}, family={Ibrahim} and given={<NAME>}, family={Ismail}}
const kUseTraditionalNameForm = false;

// Generates bibLaTeX for a given CSL object / id
export function toBibLaTeX(id: string, csl: CSL): string | undefined {

  // A BibDB is basically a map of key / EntryObject[] that is
  // used by the exporter to generate BibLaTeX
  const bibDB = cslToBibDB(id, csl);
  if (bibDB) {
    // Use the exported to parse the bibDB and generate bibLaTeX
    const exporter: BibLatexExporter = new BibLatexExporter(bibDB, false, { traditionalNames: kUseTraditionalNameForm });
    const sourceAsBibLaTeX = exporter.parse();

    // Indent any , new lines
    return sourceAsBibLaTeX.replace(/,\n/g, ',\n\t');
  }
  return undefined;
}

export function toBibTeX(id: string, csl: CSL): string | undefined {
  // A BibDB is basically a map of key / EntryObject[] that is
  // used by the exporter to generate BibLaTeX
  const bibDB = cslToBibDB(id, csl);
  if (bibDB) {
    // Use the exported to parse the bibDB and generate bibLaTeX
    const sourceAsBibTeX = bibDbToBibTeX(bibDB);

    // Indent any , new lines
    return sourceAsBibTeX;
  }
  return undefined;
}

// Converts a single CSL item to a bibDB containing
// a single EntryObject representing that CSL item
function cslToBibDB(id: string, csl: CSL): BibDB | undefined {

  const bibType = bibTypeForCSL(csl.type);
  if (bibType) {

    const bibObject: EntryObject = {
      bib_type: bibType[0],
      csl_type: bibType[1].csl,
      entry_key: id,
      'fields': {}
    };

    const enumerableCSL = csl as any;
    sortedKeys(csl).forEach(key => {
      const value: any = enumerableCSL[key];

      const bibFieldDatas = bibFieldForValue(key, csl.type);

      bibFieldDatas?.forEach(bibFieldData => {
        if (bibFieldData) {
          const bibFieldKey = bibFieldData[0];
          const bibField = bibFieldData[1];
          const type = bibField.type;
          let nodeValue: any;
          switch (type) {
            case ('f_date'):
              // f_date = // EDTF 1.0 level 0/1 compliant string. (2000-12-31)
              const cslDate = value as CSLDate;
              if (cslDate) {
                const edtfDate = cslDateToEDTFDate(cslDate);
                if (edtfDate) {
                  nodeValue = edtfDate;
                }
              }
              break;
            case ('f_integer'):
            case ('f_literal'):
            case ('f_long_literal'):
            case ('f_title'):
              // f_integer, f_literal, f_long_literal, f_title = [nodeValue]
              // l_literal = [nodeValue]
              if (value && value.length > 0) {
                nodeValue = textNodes(value);
              }
              break;
            case ('l_literal'):
              // l_literal = [NodeArray]
              if (value && value.length > 0) {
                nodeValue = [textNodes(value)];
              }
              break;
            case ('f_key'):
              // f_key: string | NodeArray (string points to another key 
              // name in BibObject whose value is used for this key)
              if (bibField.options) {
                const options = bibField.options as any;
                Object.keys(options).find(optionKey => {
                  const optionValue: any = options[optionKey];
                  if (optionValue.csl === value) {
                    nodeValue = optionKey;
                    return true;
                  }
                });

                if (!nodeValue) {
                  nodeValue = textNodes(value);
                }
              }

              break;
            case ('l_key'):
              // l_key, list of [string | NodeArray]
              if (bibField.options) {
                const options = bibField.options as any;
                Object.keys(options).find(optionKey => {
                  const optionValue: any = options[optionKey];
                  if (optionValue.csl === value) {
                    nodeValue = [optionKey];
                    return true;
                  }
                });

                if (!nodeValue && value && value.length > 0) {
                  nodeValue = textNodes(value);
                }
              }
              break;
            case ('l_range'):
              // l_range Array<RangeArray>
              const valueStr = value as string;
              const parts = valueStr.split('-');
              const range = rangeArray(parts);
              if (range) {
                nodeValue = [range];
              }
              break;
            case ('f_uri'):
            case ('f_verbatim'):
              // f_uri, f_verbatim: string
              nodeValue = value;
              break;
            case ('l_name'):
              // l_name Array<NameDictObject>
              const names = value as CSLName[];
              nodeValue = names.map(name => {
                const nameDict: NameDictObject = {
                  family: name.family ? textNodes(name.family) : undefined,
                  given: name.given ? textNodes(name.given) : undefined,
                  literal: name.literal ? textNodes(name.literal) : undefined,
                };
{'fraction_non_alphanumeric': 0.10466740169055494, 'fraction_numerical': 0.003822124219037119, 'mean_word_length': 1.911619944361224, 'pattern_counts': {'":': 0, '<': 26, '<?xml version=': 0, '>': 42, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4041497', 'n_tokens_mistral': 4116, 'n_tokens_neox': 3780, 'n_words': 1129}