You are here

class BibtexEncoder in Bibliography & Citation 8

Same name and namespace in other branches
  1. 2.0.x modules/bibcite_bibtex/src/Encoder/BibtexEncoder.php \Drupal\bibcite_bibtex\Encoder\BibtexEncoder

BibTeX format encoder.

Hierarchy

  • class \Drupal\bibcite_bibtex\Encoder\BibtexEncoder implements \Symfony\Component\Serializer\Encoder\EncoderInterface, \Symfony\Component\Serializer\Encoder\DecoderInterface

Expanded class hierarchy of BibtexEncoder

4 files declare their use of BibtexEncoder
BibtexCaseDecodeTest.php in modules/bibcite_bibtex/tests/src/Kernel/BibtexCaseDecodeTest.php
BibtexDecodeTest.php in modules/bibcite_bibtex/tests/src/Kernel/BibtexDecodeTest.php
BibtexEncoderTest.php in modules/bibcite_bibtex/tests/src/Unit/BibtexEncoderTest.php
BibtexEncodeTest.php in modules/bibcite_bibtex/tests/src/Kernel/BibtexEncodeTest.php
2 string references to 'BibtexEncoder'
bibcite_bibtex.bibcite_format.yml in modules/bibcite_bibtex/bibcite_bibtex.bibcite_format.yml
modules/bibcite_bibtex/bibcite_bibtex.bibcite_format.yml
bibcite_bibtex.services.yml in modules/bibcite_bibtex/bibcite_bibtex.services.yml
modules/bibcite_bibtex/bibcite_bibtex.services.yml
1 service uses BibtexEncoder
bibcite_bibtex.encoder.bibtex in modules/bibcite_bibtex/bibcite_bibtex.services.yml
Drupal\bibcite_bibtex\Encoder\BibtexEncoder

File

modules/bibcite_bibtex/src/Encoder/BibtexEncoder.php, line 13

Namespace

Drupal\bibcite_bibtex\Encoder
View source
class BibtexEncoder implements EncoderInterface, DecoderInterface {

  /**
   * The format that this encoder supports.
   *
   * @var string
   */
  protected static $format = 'bibtex';

  /**
   * {@inheritdoc}
   */
  public function supportsDecoding($format) {
    return $format == static::$format;
  }

  /**
   * {@inheritdoc}
   */
  public function decode($data, $format, array $context = []) {
    $data = $this
      ->lineEndingsReplace($data);

    /*
     * Handle type as case-insensitive.
     * Tags should be handled as case-insensitive as well, but it's done by BibtexParser library.
     * @see https://www.drupal.org/node/2890060
     */
    $data = preg_replace_callback('/^@(\\w+){/m', function ($word) {
      return '@' . strtolower($word[1]) . '{';
    }, $data);

    /*
     * Ignore "type" tag inside records.
     * Type in BibTeX must go before content.
     * @see https://en.wikipedia.org/wiki/BibTeX
     * @see https://www.drupal.org/node/2882855
     */
    $data = preg_replace('/^ *type *= *{.*}.*$/m', '', $data);
    $parsed = BibtexParser::parse_string($data);
    foreach ($parsed as $i => $entry) {
      unset($entry['raw']);
      unset($entry['lines']);
      $parsed[$i] = $entry;

      // BibtexParser library treat "editor" field as regular string instead
      // of array as it does with "author" field.
      // Hotfix "editor" field so it behaves the same as "author" field.
      // @todo Remove this hotfix once used library is fixed or different library is used.
      if (isset($parsed[$i]['editor'])) {
        $parsed[$i]['editor'] = explode(' and ', $parsed[$i]['editor']);
      }
    }
    $keys = array_keys($parsed);
    if (count($keys) === 0 || $keys[0] === -1) {
      $format_definition = \Drupal::service('plugin.manager.bibcite_format')
        ->getDefinition($format);
      $format_label = $format_definition['label'];
      throw new UnexpectedValueException("Incorrect '{$format_label}' format or empty set.");
    }
    $this
      ->processEntries($parsed);
    return $parsed;
  }

  /**
   * Convert line endings function.
   *
   * Different sources uses different line endings in exports.
   * Convert all line endings to unix which is expected by BibtexParser.
   *
   * @param string $data
   *   Input string from file.
   *
   * @return string
   *   Unix formatted string
   */
  public function lineEndingsReplace($data) {

    /*
     * \R is escape sequence of newline, equivalent to the following: (\r\n|\n|\x0b|\f|\r|\x85)
     * @see http://www.pcre.org/original/doc/html/pcrepattern.html Newline sequences.
     */
    return preg_replace("/\\R/", "\n", $data);
  }

  /**
   * Workaround about some things in BibtexParser library.
   *
   * @param array $parsed
   *   List of parsed entries.
   */
  protected function processEntries(array &$parsed) {
    foreach ($parsed as &$entry) {
      if (!empty($entry['pages']) && is_array($entry['pages'])) {
        $entry['pages'] = implode('-', $entry['pages']);
      }
      if (!empty($entry['keywords'])) {
        $entry['keywords'] = array_map(function ($keyword) {
          return trim($keyword);
        }, explode(',', str_replace(';', ',', $entry['keywords'])));
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  public function supportsEncoding($format) {
    return $format == static::$format;
  }

  /**
   * {@inheritdoc}
   */
  public function encode($data, $format, array $context = []) {
    if (isset($data['type'])) {
      $data = [
        $data,
      ];
    }
    $data = array_map(function ($raw) {
      return $this
        ->buildEntry($raw);
    }, $data);
    return implode("\n", $data);
  }

  /**
   * Build BibTeX entry string.
   *
   * @param array $data
   *   Array of BibTeX values.
   *
   * @return string
   *   Formatted BibTeX string.
   */
  protected function buildEntry(array $data) {
    if (empty($data['reference'])) {
      $data['reference'] = $data['type'];
    }

    // Hotfix "editor" field so it behaves the same as "author" field.
    if (isset($data['editor'])) {
      $data['editor'] = implode(' and ', $data['editor']);
    }
    $entry = $this
      ->buildStart($data['type'], $data['reference']);
    unset($data['type']);
    unset($data['reference']);
    foreach ($data as $key => $value) {
      $entry .= $this
        ->buildLine($key, $value);
    }
    $entry .= $this
      ->buildEnd();
    return $entry;
  }

  /**
   * Build first string for BibTeX entry.
   *
   * @param string $type
   *   Publication type in BibTeX format.
   * @param string $reference
   *   Reference key.
   *
   * @return string
   *   First entry string.
   */
  protected function buildStart($type, $reference) {
    return '@' . $type . '{' . $reference . ',' . "\n";
  }

  /**
   * Build entry line.
   *
   * @param string $key
   *   Line key.
   * @param string|array $value
   *   Line value.
   *
   * @return string
   *   Entry line.
   */
  protected function buildLine($key, $value) {
    switch ($key) {
      case 'author':
        $value = implode(' and ', $value);
        break;
      case 'keywords':
        $value = implode(', ', $value);
        break;
    }
    return '  ' . $key . ' = {' . $value . '},' . "\n";
  }

  /**
   * Build the end of BibTeX entry.
   *
   * @return string
   *   End line for the BibTeX entry.
   */
  protected function buildEnd() {
    return "}\n";
  }

}

Members

Namesort descending Modifiers Type Description Overrides
BibtexEncoder::$format protected static property The format that this encoder supports.
BibtexEncoder::buildEnd protected function Build the end of BibTeX entry.
BibtexEncoder::buildEntry protected function Build BibTeX entry string.
BibtexEncoder::buildLine protected function Build entry line.
BibtexEncoder::buildStart protected function Build first string for BibTeX entry.
BibtexEncoder::decode public function
BibtexEncoder::encode public function
BibtexEncoder::lineEndingsReplace public function Convert line endings function.
BibtexEncoder::processEntries protected function Workaround about some things in BibtexParser library.
BibtexEncoder::supportsDecoding public function
BibtexEncoder::supportsEncoding public function