From 56c333245a5b4c0b1c15b92e04e9d52303748a18 Mon Sep 17 00:00:00 2001 From: Guilhem Lavaux Date: Sat, 23 Nov 2024 22:46:58 +0100 Subject: [PATCH] Update website --- vendor/composer/autoload_psr4.php | 1 + vendor/composer/autoload_static.php | 8 + vendor/composer/installed.json | 65 ++ vendor/composer/installed.php | 9 + vendor/renanbr/bibtex-parser/LICENSE | 18 + vendor/renanbr/bibtex-parser/Makefile | 74 +++ vendor/renanbr/bibtex-parser/README.md | 569 ++++++++++++++++++ vendor/renanbr/bibtex-parser/composer.json | 50 ++ .../src/Exception/ExceptionInterface.php | 19 + .../src/Exception/ParserException.php | 35 ++ .../src/Exception/ProcessorException.php | 18 + vendor/renanbr/bibtex-parser/src/Listener.php | 109 ++++ .../bibtex-parser/src/ListenerInterface.php | 26 + vendor/renanbr/bibtex-parser/src/Parser.php | 566 +++++++++++++++++ .../src/Processor/DateProcessor.php | 58 ++ .../src/Processor/FillMissingProcessor.php | 40 ++ .../src/Processor/KeywordsProcessor.php | 38 ++ .../src/Processor/LatexToUnicodeProcessor.php | 104 ++++ .../src/Processor/NamesProcessor.php | 243 ++++++++ .../src/Processor/TagCoverageTrait.php | 63 ++ .../src/Processor/TagNameCaseProcessor.php | 37 ++ .../src/Processor/TagSearchTrait.php | 35 ++ .../src/Processor/TrimProcessor.php | 58 ++ .../src/Processor/UrlFromDoiProcessor.php | 49 ++ 24 files changed, 2292 insertions(+) create mode 100644 vendor/renanbr/bibtex-parser/LICENSE create mode 100644 vendor/renanbr/bibtex-parser/Makefile create mode 100644 vendor/renanbr/bibtex-parser/README.md create mode 100644 vendor/renanbr/bibtex-parser/composer.json create mode 100644 vendor/renanbr/bibtex-parser/src/Exception/ExceptionInterface.php create mode 100644 vendor/renanbr/bibtex-parser/src/Exception/ParserException.php create mode 100644 vendor/renanbr/bibtex-parser/src/Exception/ProcessorException.php create mode 100644 vendor/renanbr/bibtex-parser/src/Listener.php create mode 100644 vendor/renanbr/bibtex-parser/src/ListenerInterface.php create mode 100644 vendor/renanbr/bibtex-parser/src/Parser.php create mode 100644 vendor/renanbr/bibtex-parser/src/Processor/DateProcessor.php create mode 100644 vendor/renanbr/bibtex-parser/src/Processor/FillMissingProcessor.php create mode 100644 vendor/renanbr/bibtex-parser/src/Processor/KeywordsProcessor.php create mode 100644 vendor/renanbr/bibtex-parser/src/Processor/LatexToUnicodeProcessor.php create mode 100644 vendor/renanbr/bibtex-parser/src/Processor/NamesProcessor.php create mode 100644 vendor/renanbr/bibtex-parser/src/Processor/TagCoverageTrait.php create mode 100644 vendor/renanbr/bibtex-parser/src/Processor/TagNameCaseProcessor.php create mode 100644 vendor/renanbr/bibtex-parser/src/Processor/TagSearchTrait.php create mode 100644 vendor/renanbr/bibtex-parser/src/Processor/TrimProcessor.php create mode 100644 vendor/renanbr/bibtex-parser/src/Processor/UrlFromDoiProcessor.php diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php index ea22912..ffbf5dc 100644 --- a/vendor/composer/autoload_psr4.php +++ b/vendor/composer/autoload_psr4.php @@ -25,6 +25,7 @@ return array( 'Symfony\\Component\\Filesystem\\' => array($vendorDir . '/symfony/filesystem'), 'Symfony\\Component\\ErrorHandler\\' => array($vendorDir . '/symfony/error-handler'), 'Stevenmaguire\\OAuth2\\Client\\' => array($vendorDir . '/stevenmaguire/oauth2-bitbucket/src'), + 'RenanBr\\BibTexParser\\' => array($vendorDir . '/renanbr/bibtex-parser/src'), 'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'), 'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-factory/src', $vendorDir . '/psr/http-message/src'), 'Psr\\Http\\Client\\' => array($vendorDir . '/psr/http-client/src'), diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index ff243b9..0951bcd 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -51,6 +51,10 @@ class ComposerStaticInitc94a8368bcea9853dd31683be0b15c06 'Symfony\\Component\\ErrorHandler\\' => 31, 'Stevenmaguire\\OAuth2\\Client\\' => 28, ), + 'R' => + array ( + 'RenanBr\\BibTexParser\\' => 21, + ), 'P' => array ( 'Psr\\Log\\' => 8, @@ -159,6 +163,10 @@ class ComposerStaticInitc94a8368bcea9853dd31683be0b15c06 array ( 0 => __DIR__ . '/..' . '/stevenmaguire/oauth2-bitbucket/src', ), + 'RenanBr\\BibTexParser\\' => + array ( + 0 => __DIR__ . '/..' . '/renanbr/bibtex-parser/src', + ), 'Psr\\Log\\' => array ( 0 => __DIR__ . '/..' . '/psr/log/Psr/Log', diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index 4107283..6f21f40 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -1402,6 +1402,71 @@ }, "install-path": "../ralouphie/getallheaders" }, + { + "name": "renanbr/bibtex-parser", + "version": "2.2.0", + "version_normalized": "2.2.0.0", + "source": { + "type": "git", + "url": "https://github.com/renanbr/bibtex-parser.git", + "reference": "d02d2426822235f5179ecdf635ba710c9d6d2ddd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/renanbr/bibtex-parser/zipball/d02d2426822235f5179ecdf635ba710c9d6d2ddd", + "reference": "d02d2426822235f5179ecdf635ba710c9d6d2ddd", + "shasum": "" + }, + "require": { + "php": ">=5.6.0" + }, + "require-dev": { + "phpunit/phpunit": ">=5.7", + "ryakad/pandoc-php": "^1.0" + }, + "suggest": { + "ryakad/pandoc-php": "Needed to support LaTeX decoder in class RenanBr\\BibTexParser\\Processor\\LatexToUnicodeProcessor", + "ueberdosis/pandoc": "Alternate Pandoc PHP package which (if available) will be preferred over ryakad/pandoc-php" + }, + "time": "2023-08-25T11:21:46+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "RenanBr\\BibTexParser\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Renan de Lima Barbosa", + "email": "renandelima@gmail.com" + } + ], + "description": "BibTex Parser provides an API to read .bib files programmatically", + "keywords": [ + "Bibliography", + "bib", + "bibtex", + "citation", + "cite", + "latex", + "parser" + ], + "support": { + "issues": "https://github.com/renanbr/bibtex-parser/issues", + "source": "https://github.com/renanbr/bibtex-parser/tree/2.2.0" + }, + "install-path": "../renanbr/bibtex-parser" + }, { "name": "stevenmaguire/oauth2-bitbucket", "version": "3.0.0", diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php index 16bfab1..0bfc856 100644 --- a/vendor/composer/installed.php +++ b/vendor/composer/installed.php @@ -220,6 +220,15 @@ 'aliases' => array(), 'dev_requirement' => false, ), + 'renanbr/bibtex-parser' => array( + 'pretty_version' => '2.2.0', + 'version' => '2.2.0.0', + 'reference' => 'd02d2426822235f5179ecdf635ba710c9d6d2ddd', + 'type' => 'library', + 'install_path' => __DIR__ . '/../renanbr/bibtex-parser', + 'aliases' => array(), + 'dev_requirement' => false, + ), 'stevenmaguire/oauth2-bitbucket' => array( 'pretty_version' => '3.0.0', 'version' => '3.0.0.0', diff --git a/vendor/renanbr/bibtex-parser/LICENSE b/vendor/renanbr/bibtex-parser/LICENSE new file mode 100644 index 0000000..05cbcd3 --- /dev/null +++ b/vendor/renanbr/bibtex-parser/LICENSE @@ -0,0 +1,18 @@ +Copyright (c) 2017 Renan de Lima Barbosa + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/renanbr/bibtex-parser/Makefile b/vendor/renanbr/bibtex-parser/Makefile new file mode 100644 index 0000000..4094b83 --- /dev/null +++ b/vendor/renanbr/bibtex-parser/Makefile @@ -0,0 +1,74 @@ +ifeq ($(shell type podman > /dev/null 2>&1; echo $$?), 0) + ENGINE ?= podman +else ifeq ($(shell type docker > /dev/null 2>&1; echo $$?), 0) + ENGINE ?= docker +endif + +PHP_VERSION ?= 7.4 + +IMAGE_BASE = jakzal/phpqa:php$(PHP_VERSION) +IMAGE = renanbr/bibtex-parser:php$(PHP_VERSION) +LABEL = maintainer=renanbr-bibtex-parser + +RUN = $(ENGINE) run --init -it --rm -v "$(CURDIR):/project" -w /project + +.DEFAULT_GOAL := help + +help: ## Display this message help + @make -v | head -n 1 + @awk '\ + BEGIN {\ + FS = ":.*##";\ + printf "\n\033[33mUsage:\033[0m\n [PHP_VERSION=major.minor] make [target]\n\n\033[33mAvailable targets:\033[0m\n" \ + } /^[a-zA-Z0-9_-]+:.*?##/ { \ + printf " \033[32m%-18s\033[0m %s\n", $$1, $$2 \ + } /^##/ { \ + printf "\033[33m %s\033[0m\n", substr($$0, 4) \ + }' $(MAKEFILE_LIST) +.PHONY: help + +## Checks + +check: static-analysis cs-check test ## Run all checks + +static-analysis: vendor ## Run static analysis + $(RUN) $(IMAGE) phpstan analyse --verbose +.PHONY: static-analysis + +cs-check: check-engine ## Check for coding standards violations + mkdir -p var + $(RUN) $(IMAGE_BASE) php-cs-fixer fix --dry-run --verbose +.PHONY: cs-check + +test: vendor ## Run tests + $(RUN) $(IMAGE) php -d pcov.enabled=1 ./vendor/bin/phpunit --testdox --coverage-text --verbose +.PHONY: test + +## Fixers + +cs-fix: check-engine ## Fix coding standards + mkdir -p var + $(RUN) $(IMAGE_BASE) php-cs-fixer fix +.PHONY: cs-fix + +## Misc + +clean: check-engine ## Clean up workspace + $(RUN) $(IMAGE_BASE) rm -rf composer.lock var/ vendor/ + $(ENGINE) image rm --force $$($(ENGINE) images --filter "label=$(LABEL)" --quiet) 2>&1 | true +.PHONY: clean + +vendor: build-image + $(RUN) $(IMAGE) composer install -vvv + +## Container engine + +check-engine: +ifeq ($(ENGINE),) + $(error "Container engine not found. Did you install podman or docker?") +endif +.PHONY: check-engine + +build-image: check-engine + $(ENGINE) build --tag $(IMAGE) --build-arg FROM=$(IMAGE_BASE) --label $(LABEL) .docker/ +.PHONY: build-image diff --git a/vendor/renanbr/bibtex-parser/README.md b/vendor/renanbr/bibtex-parser/README.md new file mode 100644 index 0000000..0a3860e --- /dev/null +++ b/vendor/renanbr/bibtex-parser/README.md @@ -0,0 +1,569 @@ +

PHP BibTeX Parser 2.x

+

+ This is a + BibTeX + parser written in + PHP. +

+

+ + BibTeX logo + + + PHP logo + +

+ +![Tests](https://github.com/renanbr/bibtex-parser/workflows/Tests/badge.svg) +[![codecov](https://codecov.io/gh/renanbr/bibtex-parser/branch/master/graph/badge.svg)](https://codecov.io/gh/renanbr/bibtex-parser) +![Static Analysis](https://github.com/renanbr/bibtex-parser/workflows/Static%20Analysis/badge.svg) +![Coding Standards](https://github.com/renanbr/bibtex-parser/workflows/Coding%20Standards/badge.svg) + +You are browsing the documentation of **BibTeX Parser 2.x**, the latest version. + +## Table of contents + +* [Installing](#installing) +* [Usage](#usage) +* [Vocabulary](#vocabulary) +* [Processors](#processors) + * [Tag name case](#tag-name-case) + * [Authors and editors](#authors-and-editors) + * [Keywords](#keywords) + * [Date](#date) + * [Fill missing tag](#fill-missing-tag) + * [Trim tags](#trim-tags) + * [Determine URL from the DOI](#determine-url-from-the-doi) + * [LaTeX to unicode](#latex-to-unicode) + * [Custom](#custom) +* [Handling errors](#handling-errors) +* [Advanced usage](#advanced-usage) + +## Installing + +```bash +composer require renanbr/bibtex-parser +``` + +## Usage + +```php +use RenanBr\BibTexParser\Listener; +use RenanBr\BibTexParser\Parser; +use RenanBr\BibTexParser\Processor; + +require 'vendor/autoload.php'; + +$bibtex = <<addProcessor(new Processor\TagNameCaseProcessor(CASE_LOWER)); +// $listener->addProcessor(new Processor\NamesProcessor()); +// $listener->addProcessor(new Processor\KeywordsProcessor()); +// $listener->addProcessor(new Processor\DateProcessor()); +// $listener->addProcessor(new Processor\FillMissingProcessor([/* ... */])); +// $listener->addProcessor(new Processor\TrimProcessor()); +// $listener->addProcessor(new Processor\UrlFromDoiProcessor()); +// $listener->addProcessor(new Processor\LatexToUnicodeProcessor()); +// ... you can append as many Processors as you want + +// Create a Parser and attach the listener +$parser = new Parser(); +$parser->addListener($listener); + +// Parse the content, then read processed data from the Listener +$parser->parseString($bibtex); // or parseFile('/path/to/file.bib') +$entries = $listener->export(); + +print_r($entries); +``` + +This will output: + +``` +Array +( + [0] => Array + ( + [_type] => article + [citation-key] => einstein1916relativity + [title] => Relativity: The Special and General Theory + [author] => Einstein, Albert + [year] => 1916 + ) +) +``` + +## Vocabulary + +[BibTeX] is all about "entry", "tag's name" and "tag's content". + +> A [BibTeX] **entry** consists of the type (the word after @), a citation-key and a number of tags which define various characteristics of the specific [BibTeX] entry. +> (...) A [BibTeX] **tag** is specified by its **name** followed by an equals sign, and the **content**. + +Source: http://www.bibtex.org/Format/ + +Note: +This library considers "type" and "citation-key" as tags. +This behavior can be changed [implementing your own Listener](#advanced-usage). + +## Processors + +`Processor` is a [callable] that receives an entry as argument and returns a modified entry. + +This library contains three main parts: + +- `Parser` class, responsible for detecting units inside a [BibTeX] input; +- `Listener` class, responsible for gathering units and transforming them into a list of entries; +- `Processor` classes, responsible for manipulating entries. + +Despite you can't configure the `Parser`, you can append as many `Processor` as you want to the `Listener` through `Listener::addProcessor()` before exporting the contents. +Be aware that `Listener` provides, by default, these features: + +- Found entries are reachable through `Listener::export()` method; +- [Tag content concatenation](http://www.bibtex.org/Format/); + - e.g. `hello # " world"` tag's content will generate `hello world` [string] +- [Tag content abbreviation handling](http://www.bibtex.org/Format/); + - e.g. `@string{foo="bar"} @misc{bar=foo}` will make `$entries[1]['bar']` assume `bar` as value +- Publication's type exposed as `_type` tag; +- Citation key exposed as `citation-key` tag; +- Original entry text exposed as `_original` tag. + +This project ships some useful processors. + +### Tag name case + +In [BibTeX] the tag's names aren't case-sensitive. +This library exposes entries as [array], in which keys are case-sensitive. +To avoid this misunderstanding, you can force the tags' name character case using `TagNameCaseProcessor`. + +
Usage + +```php +use RenanBr\BibTexParser\Processor\TagNameCaseProcessor; + +$listener->addProcessor(new TagNameCaseProcessor(CASE_UPPER)); // or CASE_LOWER +``` + +```bib +@article{ + title={BibTeX rocks} +} +``` + +``` +Array +( + [0] => Array + ( + [TYPE] => article + [TITLE] => BibTeX rocks + ) +) +``` + +
+ +### Authors and editors + +[BibTeX] recognizes four parts of an author's name: First Von Last Jr. +If you would like to parse the `author` and `editor` tags included in your entries, you can use the `NamesProcessor` class. + +
Usage + +```php +use RenanBr\BibTexParser\Processor\NamesProcessor; + +$listener->addProcessor(new NamesProcessor()); +``` + +```bib +@article{ + title={Relativity: The Special and General Theory}, + author={Einstein, Albert} +} +``` + +``` +Array +( + [0] => Array + ( + [type] => article + [title] => Relativity: The Special and General Theory + [author] => Array + ( + [0] => Array + ( + [first] => Albert + [von] => + [last] => Einstein + [jr] => + ) + ) + ) +) +``` + +
+ +### Keywords + +The `keywords` tag contains a list of expressions represented as [string], you might want to read them as an [array] instead. + +
Usage + +```php +use RenanBr\BibTexParser\Processor\KeywordsProcessor; + +$listener->addProcessor(new KeywordsProcessor()); +``` + +```bib +@misc{ + title={The End of Theory: The Data Deluge Makes the Scientific Method Obsolete}, + keywords={big data, data deluge, scientific method} +} +``` + +``` +Array +( + [0] => Array + ( + [type] => misc + [title] => The End of Theory: The Data Deluge Makes the Scientific Method Obsolete + [keywords] => Array + ( + [0] => big data + [1] => data deluge + [2] => scientific method + ) + ) +) +``` + +
+ +### Date + +It adds a new tag `_date` as [DateTimeImmutable]. +This processor adds the new tag **if and only if** this the tags `month` and `year` are fulfilled. + +
Usage + +```php +use RenanBr\BibTexParser\Processor\DateProcessor; + +$listener->addProcessor(new DateProcessor()); +``` + +```bib +@misc{ + month="1~oct", + year=2000 +} +``` + +``` +Array +( + [0] => Array + ( + [type] => misc + [month] => 1~oct + [year] => 2000 + [_date] => DateTimeImmutable Object + ( + [date] => 2000-10-01 00:00:00.000000 + [timezone_type] => 3 + [timezone] => UTC + ) + ) +) +``` + +
+ +### Fill missing tag + +It puts a default value to some missing field. + +
Usage + +```php +use RenanBr\BibTexParser\Processor\FillMissingProcessor; + +$listener->addProcessor(new FillMissingProcessor([ + 'title' => 'This entry has no title', + 'year' => 1970, +])); +``` + +```bib +@misc{ +} + +@misc{ + title="I do exist" +} +``` + +``` +Array +( + [0] => Array + ( + [type] => misc + [title] => This entry has no title + [year] => 1970 + ) + [1] => Array + ( + [type] => misc + [title] => I do exist + [year] => 1970 + ) +) +``` + +
+ +### Trim tags + +Apply [trim()] to all tags. + +
Usage + +```php +use RenanBr\BibTexParser\Processor\TrimProcessor; + +$listener->addProcessor(new TrimProcessor()); +``` + +```bib +@misc{ + title=" too much space " +} +``` + +``` +Array +( + [0] => Array + ( + [type] => misc + [title] => too much space + ) + +) +``` + +
+ +### Determine URL from the DOI + +Sets `url` tag with [DOI] if `doi` tag is present and `url` tag is missing. + +
Usage + +```php +use RenanBr\BibTexParser\Processor\UrlFromDoiProcessor; + +$listener->addProcessor(new UrlFromDoiProcessor()); +``` + +```bib +@misc{ + doi="qwerty" +} + +@misc{ + doi="azerty", + url="http://example.org" +} +``` + +``` +Array +( + [0] => Array + ( + [type] => misc + [doi] => qwerty + [url] => https://doi.org/qwerty + ) + + [1] => Array + ( + [type] => misc + [doi] => azerty + [url] => http://example.org + ) +) +``` + +
+ +### LaTeX to unicode + +[BibTeX] files store [LaTeX] contents. +You might want to read them as unicode instead. +The `LatexToUnicodeProcessor` class solves this problem, but before adding the processor to the listener you must: + +- [install Pandoc](http://pandoc.org/installing.html) in your system; and +- add [ryakad/pandoc-php](https://github.com/ryakad/pandoc-php) or [ueberdosis/pandoc](https://github.com/ueberdosis/pandoc) as a dependency of your project. + +
Usage + +```php +use RenanBr\BibTexParser\Processor\LatexToUnicodeProcessor; + +$listener->addProcessor(new LatexToUnicodeProcessor()); +``` + +```bib +@article{ + title={Caf\\'{e}s and bars} +} +``` + +``` +Array +( + [0] => Array + ( + [type] => article + [title] => Cafés and bars + ) +) +``` + +
+ +Note: Order matters, add this processor as the last. + +### Custom + +The `Listener::addProcessor()` method expects a [callable] as argument. +In the example shown below, we append the text `with laser` to the `title` tags for all entries. + +
Usage + +```php +$listener->addProcessor(static function (array $entry) { + $entry['title'] .= ' with laser'; + return $entry; +}); +``` + +``` +@article{ + title={BibTeX rocks} +} +``` + +``` +Array +( + [0] => Array + ( + [type] => article + [title] => BibTeX rocks with laser + ) +) +``` + +
+ +## Handling errors + +This library throws two types of exception: `ParserException` and `ProcessorException`. +The first one may happen during the data extraction. +When it occurs it probably means the parsed BibTeX isn't valid. +The second exception may happen during the data processing. +When it occurs it means the listener's processors can't handle properly the data found. +Both implement `ExceptionInterface`. + +```php +use RenanBr\BibTexParser\Exception\ExceptionInterface; +use RenanBr\BibTexParser\Exception\ParserException; +use RenanBr\BibTexParser\Exception\ProcessorException; + +try { + // ... parser and listener configuration + + $parser->parseFile('/path/to/file.bib'); + $entries = $listener->export(); +} catch (ParserException $exception) { + // The BibTeX isn't valid +} catch (ProcessorException $exception) { + // Listener's processors aren't able to handle data found +} catch (ExceptionInterface $exception) { + // Alternatively, you can use this exception to catch all of them at once +} +``` + +## Advanced usage + +The core of this library contains these main classes: + +- `RenanBr\BibTexParser\Parser` responsible for detecting units inside a [BibTeX] input; +- `RenanBr\BibTexParser\ListenerInterface` responsible for treating units found. + +You can attach listeners to the parser through `Parser::addListener()`. +The parser is able to detect [BibTeX] units, such as "type", "tag's name", "tag's content". +As the parser finds a unit, it triggers the listeners attached to it. + +You can code your own listener! All you have to do is handle units. + +```php +namespace RenanBr\BibTexParser; + +interface ListenerInterface +{ + /** + * Called when an unit is found. + * + * @param string $text The original content of the unit found. + * Escape character will not be sent. + * @param string $type The type of unit found. + * It can assume one of Parser's constant value. + * @param array $context Contains details of the unit found. + */ + public function bibTexUnitFound($text, $type, array $context); +} +``` + +`$type` may assume one of these values: + +- `Parser::TYPE` +- `Parser::CITATION_KEY` +- `Parser::TAG_NAME` +- `Parser::RAW_TAG_CONTENT` +- `Parser::BRACED_TAG_CONTENT` +- `Parser::QUOTED_TAG_CONTENT` +- `Parser::ENTRY` + +`$context` is an [array] with these keys: + +- `offset` contains the `$text`'s beginning position. + It may be useful, for example, to [seek on a file pointer](https://php.net/fseek); +- `length` contains the original `$text`'s length. + It may differ from [string] length sent to the listener because may there are escaped characters. + +[BibTeX]: https://tug.org/bibtex/ +[DOI]: https://www.doi.org/ +[DateTimeImmutable]: https://www.php.net/manual/class.datetimeimmutable.php +[LaTeX]: https://www.latex-project.org/ +[array]: https://php.net/manual/language.types.array.php +[callable]: https://php.net/manual/en/language.types.callable.php +[string]: https://php.net/manual/language.types.string.php +[trim()]: https://www.php.net/trim diff --git a/vendor/renanbr/bibtex-parser/composer.json b/vendor/renanbr/bibtex-parser/composer.json new file mode 100644 index 0000000..2bbbabc --- /dev/null +++ b/vendor/renanbr/bibtex-parser/composer.json @@ -0,0 +1,50 @@ +{ + "name": "renanbr/bibtex-parser", + "type": "library", + "description": "BibTex Parser provides an API to read .bib files programmatically", + "keywords": [ + "bib", + "bibtex", + "latex", + "parser", + "bibliography", + "citation", + "cite" + ], + "license": "MIT", + "authors": [ + { + "name": "Renan de Lima Barbosa", + "email": "renandelima@gmail.com" + } + ], + "require": { + "php": ">=5.6.0" + }, + "require-dev": { + "phpunit/phpunit": ">=5.7", + "ryakad/pandoc-php": "^1.0" + }, + "suggest": { + "ryakad/pandoc-php": "Needed to support LaTeX decoder in class RenanBr\\BibTexParser\\Processor\\LatexToUnicodeProcessor", + "ueberdosis/pandoc": "Alternate Pandoc PHP package which (if available) will be preferred over ryakad/pandoc-php" + }, + "config": { + "sort-packages": true + }, + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "RenanBr\\BibTexParser\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "RenanBr\\BibTexParser\\Test\\": "tests/" + } + } +} diff --git a/vendor/renanbr/bibtex-parser/src/Exception/ExceptionInterface.php b/vendor/renanbr/bibtex-parser/src/Exception/ExceptionInterface.php new file mode 100644 index 0000000..4cecdcb --- /dev/null +++ b/vendor/renanbr/bibtex-parser/src/Exception/ExceptionInterface.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace RenanBr\BibTexParser\Exception; + +/** + * Interface for package exceptions. + */ +interface ExceptionInterface +{ +} diff --git a/vendor/renanbr/bibtex-parser/src/Exception/ParserException.php b/vendor/renanbr/bibtex-parser/src/Exception/ParserException.php new file mode 100644 index 0000000..f2d8803 --- /dev/null +++ b/vendor/renanbr/bibtex-parser/src/Exception/ParserException.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace RenanBr\BibTexParser\Exception; + +use Exception; + +class ParserException extends Exception implements ExceptionInterface +{ + /** + * @param string $character + * @param int $line + * @param int $column + */ + public static function unexpectedCharacter($character, $line, $column) + { + // Avoid var_export() weird treatment for \0 + $character = "\0" === $character ? "'\\0'" : var_export($character, true); + + return new self(sprintf( + 'Unexpected character %s at line %d column %d', + $character, + $line, + $column + )); + } +} diff --git a/vendor/renanbr/bibtex-parser/src/Exception/ProcessorException.php b/vendor/renanbr/bibtex-parser/src/Exception/ProcessorException.php new file mode 100644 index 0000000..a453f39 --- /dev/null +++ b/vendor/renanbr/bibtex-parser/src/Exception/ProcessorException.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace RenanBr\BibTexParser\Exception; + +use Exception; + +class ProcessorException extends Exception implements ExceptionInterface +{ +} diff --git a/vendor/renanbr/bibtex-parser/src/Listener.php b/vendor/renanbr/bibtex-parser/src/Listener.php new file mode 100644 index 0000000..91be372 --- /dev/null +++ b/vendor/renanbr/bibtex-parser/src/Listener.php @@ -0,0 +1,109 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace RenanBr\BibTexParser; + +class Listener implements ListenerInterface +{ + /** @var array */ + private $entries = []; + + /** + * Current tag name. + * + * Indicates where to save contents when triggered by the parser. + * + * @var string + */ + private $currentTagName; + + /** @var array */ + private $processors = []; + + /** @var array */ + private $processed = []; + + /** + * @return array all entries found during parsing process + */ + public function export() + { + $offset = \count($this->processed); + $missing = \array_slice($this->entries, $offset); + foreach ($this->processors as $processor) { + $missing = array_filter(array_map($processor, $missing)); + } + $this->processed = array_merge($this->processed, $missing); + + return $this->processed; + } + + /** + * @param callable $processor Function to be applied to every BibTeX entry. + * The processor given must return the modified entry. + * Processors will be applied in the same order in which they were added. + * The suggested signature is: + * function (array $entry): array + */ + public function addProcessor(callable $processor) + { + $this->processors[] = $processor; + } + + public function bibTexUnitFound($text, $type, array $context) + { + switch ($type) { + case Parser::TYPE: + // Starts a new entry + $this->entries[] = [ + '_type' => $text, + 'type' => $text, // compatibility + ]; + break; + + case Parser::CITATION_KEY: + $index = \count($this->entries) - 1; + $this->entries[$index]['citation-key'] = $text; + break; + + case Parser::TAG_NAME: + // Saves tag into the current entry + $index = \count($this->entries) - 1; + $this->currentTagName = $text; + $this->entries[$index][$this->currentTagName] = null; + break; + + case Parser::RAW_TAG_CONTENT: + // Searches for an abbreviation + foreach ($this->entries as $entry) { + if ('string' === $entry['type'] && \array_key_exists($text, $entry)) { + $text = $entry[$text]; + break; + } + } + // no break + + case Parser::BRACED_TAG_CONTENT: + case Parser::QUOTED_TAG_CONTENT: + // Appends content into the current tag + if (null !== $text) { + $index = \count($this->entries) - 1; + $this->entries[$index][$this->currentTagName] .= $text; + } + break; + + case Parser::ENTRY: + $index = \count($this->entries) - 1; + $this->entries[$index]['_original'] = $text; + break; + } + } +} diff --git a/vendor/renanbr/bibtex-parser/src/ListenerInterface.php b/vendor/renanbr/bibtex-parser/src/ListenerInterface.php new file mode 100644 index 0000000..b5f4f85 --- /dev/null +++ b/vendor/renanbr/bibtex-parser/src/ListenerInterface.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace RenanBr\BibTexParser; + +interface ListenerInterface +{ + /** + * Called when an unit is found. + * + * @param string $text The original content of the unit found. + * Escape character will not be sent. + * @param string $type The type of unit found. + * It can assume one of Parser's constant value. + * @param array $context contains details of the unit found + */ + public function bibTexUnitFound($text, $type, array $context); +} diff --git a/vendor/renanbr/bibtex-parser/src/Parser.php b/vendor/renanbr/bibtex-parser/src/Parser.php new file mode 100644 index 0000000..1c0cfa0 --- /dev/null +++ b/vendor/renanbr/bibtex-parser/src/Parser.php @@ -0,0 +1,566 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace RenanBr\BibTexParser; + +use ErrorException; +use RenanBr\BibTexParser\Exception\ParserException; + +class Parser +{ + const TYPE = 'type'; + const CITATION_KEY = 'citation_key'; + const TAG_NAME = 'tag_name'; + const RAW_TAG_CONTENT = 'raw_tag_content'; + const BRACED_TAG_CONTENT = 'braced_tag_content'; + const QUOTED_TAG_CONTENT = 'quoted_tag_content'; + const ENTRY = 'entry'; + + const NONE = 'none'; + const COMMENT = 'comment'; + const FIRST_TAG_NAME = 'first_tag_name'; + const POST_TYPE = 'post_type'; + const POST_TAG_NAME = 'post_tag_name'; + const PRE_TAG_CONTENT = 'pre_tag_content'; + + /** @var string */ + private $state; + + /** @var string */ + private $buffer; + + /** @var int|null */ + private $bufferOffset; + + /** @var array|null */ + private $firstTagSnapshot; + + /** @var string|null */ + private $originalEntryBuffer; + + /** @var int|null */ + private $originalEntryOffset; + + /** @var bool */ + private $skipOriginalEntryReading; + + /** @var int */ + private $line; + + /** @var int */ + private $column; + + /** @var int */ + private $offset; + + /** @var bool */ + private $isTagContentEscaped; + + /** @var bool */ + private $mayConcatenateTagContent; + + /** @var string|null */ + private $tagContentDelimiter; + + /** @var int */ + private $braceLevel; + + /** @var ListenerInterface[] */ + private $listeners = []; + + public function addListener(ListenerInterface $listener) + { + $this->listeners[] = $listener; + } + + /** + * @param string $file + * + * @throws ParserException if $file given is not a valid BibTeX + * @throws ErrorException if $file given is not readable + */ + public function parseFile($file) + { + $handle = @fopen($file, 'r'); + if (!$handle) { + throw new ErrorException(sprintf('Unable to open %s', $file)); + } + try { + $this->reset(); + while (!feof($handle)) { + $buffer = fread($handle, 128); + $this->parse($buffer); + } + $this->throwExceptionIfReadingEntry("\0"); + } finally { + fclose($handle); + } + } + + /** + * @param string $string + * + * @throws ParserException if $string given is not a valid BibTeX + */ + public function parseString($string) + { + $this->reset(); + $this->parse($string); + $this->throwExceptionIfReadingEntry("\0"); + } + + /** + * @param string $text + */ + private function parse($text) + { + $length = mb_strlen($text); + for ($position = 0; $position < $length; ++$position) { + $char = mb_substr($text, $position, 1); + $this->read($char); + if ("\n" === $char) { + ++$this->line; + $this->column = 1; + } else { + ++$this->column; + } + ++$this->offset; + } + } + + private function reset() + { + $this->state = self::NONE; + $this->buffer = ''; + $this->firstTagSnapshot = null; + $this->originalEntryBuffer = null; + $this->originalEntryOffset = null; + $this->skipOriginalEntryReading = false; + $this->line = 1; + $this->column = 1; + $this->offset = 0; + $this->mayConcatenateTagContent = false; + $this->isTagContentEscaped = false; + $this->tagContentDelimiter = null; + $this->braceLevel = 0; + } + + // ----- Readers ----------------------------------------------------------- + + /** + * @param string $char + */ + private function read($char) + { + $previousState = $this->state; + + switch ($this->state) { + case self::NONE: + $this->readNone($char); + break; + case self::COMMENT: + $this->readComment($char); + break; + case self::TYPE: + $this->readType($char); + break; + case self::POST_TYPE: + $this->readPostType($char); + break; + case self::FIRST_TAG_NAME: + case self::TAG_NAME: + $this->readTagName($char); + break; + case self::POST_TAG_NAME: + $this->readPostTagName($char); + break; + case self::PRE_TAG_CONTENT: + $this->readPreTagContent($char); + break; + case self::RAW_TAG_CONTENT: + $this->readRawTagContent($char); + break; + case self::QUOTED_TAG_CONTENT: + case self::BRACED_TAG_CONTENT: + $this->readDelimitedTagContent($char); + break; + } + + $this->readOriginalEntry($char, $previousState); + } + + /** + * @param string $char + */ + private function readNone($char) + { + if ('@' === $char) { + $this->state = self::TYPE; + } elseif (!$this->isWhitespace($char)) { + $this->state = self::COMMENT; + } + } + + /** + * @param string $char + */ + private function readComment($char) + { + if ($this->isWhitespace($char)) { + $this->state = self::NONE; + } + } + + /** + * @param string $char + */ + private function readType($char) + { + if (preg_match('/^[a-zA-Z]$/', $char)) { + $this->appendToBuffer($char); + } else { + $this->throwExceptionIfBufferIsEmpty($char); + + // Skips @comment type + if ('comment' === mb_strtolower($this->buffer)) { + $this->skipOriginalEntryReading = true; + $this->buffer = ''; + $this->bufferOffset = null; + $this->state = self::COMMENT; + $this->readComment($char); + + return; + } + + $this->triggerListenersWithCurrentBuffer(); + + // once $char isn't a valid character + // it must be interpreted as POST_TYPE + $this->state = self::POST_TYPE; + $this->readPostType($char); + } + } + + /** + * @param string $char + */ + private function readPostType($char) + { + if ('{' === $char) { + $this->state = self::FIRST_TAG_NAME; + } elseif (!$this->isWhitespace($char)) { + throw ParserException::unexpectedCharacter($char, $this->line, $this->column); + } + } + + /** + * @param string $char + */ + private function readTagName($char) + { + if (preg_match('/^[a-zA-Z0-9_\+:\-\.\/\x{00C0}-\x{01FF}]$/u', $char)) { + $this->appendToBuffer($char); + } elseif ($this->isWhitespace($char) && empty($this->buffer)) { + // Skips because we didn't start reading + } elseif ('}' === $char && empty($this->buffer)) { + // No tag name found, $char is just closing current entry + $this->state = self::NONE; + } else { + $this->throwExceptionIfBufferIsEmpty($char); + + if (self::FIRST_TAG_NAME === $this->state) { + // Takes a snapshot of current state to be triggered later as + // tag name or citation key, see readPostTagName() + $this->firstTagSnapshot = $this->takeBufferSnapshot(); + } else { + // Current buffer is a simple tag name + $this->triggerListenersWithCurrentBuffer(); + } + + // Once $char isn't a valid tag name character, it must be + // interpreted as post tag name + $this->state = self::POST_TAG_NAME; + $this->readPostTagName($char); + } + } + + /** + * @param string $char + */ + private function readPostTagName($char) + { + if ('=' === $char) { + // First tag name isn't a citation key, because it has content + $this->triggerListenersWithFirstTagSnapshotAs(self::TAG_NAME); + $this->state = self::PRE_TAG_CONTENT; + } elseif ('}' === $char) { + // First tag name is a citation key, because $char closes entry and + // lets first tag without value + $this->triggerListenersWithFirstTagSnapshotAs(self::CITATION_KEY); + $this->state = self::NONE; + } elseif (',' === $char) { + // First tag name is a citation key, because $char moves to the next + // tag and lets first tag without value + $this->triggerListenersWithFirstTagSnapshotAs(self::CITATION_KEY); + $this->state = self::TAG_NAME; + } elseif (!$this->isWhitespace($char)) { + throw ParserException::unexpectedCharacter($char, $this->line, $this->column); + } + } + + /** + * @param string $char + */ + private function readPreTagContent($char) + { + if (preg_match('/^[a-zA-Z0-9]$/', $char)) { + // When concatenation is available it means there is already a + // defined value, and parser expect a concatenator, a tag separator + // or an entry closing char as next $char + $this->throwExceptionAccordingToConcatenationAvailability($char, true); + $this->state = self::RAW_TAG_CONTENT; + $this->readRawTagContent($char); + } elseif ('"' === $char) { + // The exception is here for the same reason of the first case + $this->throwExceptionAccordingToConcatenationAvailability($char, true); + $this->tagContentDelimiter = '"'; + $this->state = self::QUOTED_TAG_CONTENT; + } elseif ('{' === $char) { + // The exception is here for the same reason of the first case + $this->throwExceptionAccordingToConcatenationAvailability($char, true); + $this->tagContentDelimiter = '}'; + $this->state = self::BRACED_TAG_CONTENT; + } elseif ('#' === $char) { + $this->throwExceptionAccordingToConcatenationAvailability($char, false); + $this->mayConcatenateTagContent = false; + } elseif (',' === $char) { + $this->throwExceptionAccordingToConcatenationAvailability($char, false); + $this->mayConcatenateTagContent = false; + $this->state = self::TAG_NAME; + } elseif ('}' === $char) { + $this->throwExceptionAccordingToConcatenationAvailability($char, false); + $this->mayConcatenateTagContent = false; + $this->state = self::NONE; + } elseif (!$this->isWhitespace($char)) { + throw ParserException::unexpectedCharacter($char, $this->line, $this->column); + } + } + + /** + * @param string $char + */ + private function readRawTagContent($char) + { + if (preg_match('/^[a-zA-Z0-9_\+:\-\.\/]$/', $char)) { + $this->appendToBuffer($char); + } else { + $this->throwExceptionIfBufferIsEmpty($char); + $this->triggerListenersWithCurrentBuffer(); + + // once $char isn't a valid character + // it must be interpreted as TAG_CONTENT + $this->mayConcatenateTagContent = true; + $this->state = self::PRE_TAG_CONTENT; + $this->readPreTagContent($char); + } + } + + /** + * @param string $char + */ + private function readDelimitedTagContent($char) + { + if ($this->isTagContentEscaped) { + $this->isTagContentEscaped = false; + if ($this->tagContentDelimiter !== $char && '\\' !== $char && '%' !== $char) { + $this->appendToBuffer('\\'); + } + $this->appendToBuffer($char); + } elseif ('}' === $this->tagContentDelimiter && '{' === $char) { + ++$this->braceLevel; + $this->appendToBuffer($char); + } elseif ($this->tagContentDelimiter === $char) { + if (0 === $this->braceLevel) { + $this->triggerListenersWithCurrentBuffer(); + $this->mayConcatenateTagContent = true; + $this->state = self::PRE_TAG_CONTENT; + } else { + --$this->braceLevel; + $this->appendToBuffer($char); + } + } elseif ('\\' === $char) { + $this->isTagContentEscaped = true; + } else { + $this->appendToBuffer($char); + } + } + + /** + * @param string $char + * @param string $previousState + */ + private function readOriginalEntry($char, $previousState) + { + if ($this->skipOriginalEntryReading) { + $this->originalEntryBuffer = ''; + $this->originalEntryOffset = null; + $this->skipOriginalEntryReading = false; + + return; + } + + // Checks whether we are reading an entry character or not + $isPreviousStateEntry = $this->isEntryState($previousState); + $isCurrentStateEntry = $this->isEntryState($this->state); + $isEntry = $isPreviousStateEntry || $isCurrentStateEntry; + if (!$isEntry) { + return; + } + + // Appends $char to the original entry buffer + if (empty($this->originalEntryBuffer)) { + $this->originalEntryOffset = $this->offset; + } + $this->originalEntryBuffer .= $char; + + // Sends original entry to the listeners when $char closes an entry + $isClosingEntry = $isPreviousStateEntry && !$isCurrentStateEntry; + if ($isClosingEntry) { + $this->triggerListeners($this->originalEntryBuffer, self::ENTRY, [ + 'offset' => $this->originalEntryOffset, + 'length' => $this->offset - $this->originalEntryOffset + 1, + ]); + $this->originalEntryBuffer = ''; + $this->originalEntryOffset = null; + } + } + + // ----- Listener triggers ------------------------------------------------- + + /** + * @param string $text + * @param string $type + */ + private function triggerListeners($text, $type, array $context) + { + foreach ($this->listeners as $listener) { + $listener->bibTexUnitFound($text, $type, $context); + } + } + + private function triggerListenersWithCurrentBuffer() + { + $snapshot = $this->takeBufferSnapshot(); + $text = $snapshot['text']; + $context = $snapshot['context']; + $this->triggerListeners($text, $this->state, $context); + } + + /** + * @param string $type + */ + private function triggerListenersWithFirstTagSnapshotAs($type) + { + if (empty($this->firstTagSnapshot)) { + return; + } + $text = $this->firstTagSnapshot['text']; + $context = $this->firstTagSnapshot['context']; + $this->firstTagSnapshot = null; + $this->triggerListeners($text, $type, $context); + } + + // ----- Buffer tools ------------------------------------------------------ + + /** + * @param string $char + */ + private function appendToBuffer($char) + { + if (empty($this->buffer)) { + $this->bufferOffset = $this->offset; + } + $this->buffer .= $char; + } + + /** + * @return array + */ + private function takeBufferSnapshot() + { + $snapshot = [ + 'text' => $this->buffer, + 'context' => [ + 'offset' => $this->bufferOffset, + 'length' => $this->offset - $this->bufferOffset, + ], + ]; + $this->bufferOffset = null; + $this->buffer = ''; + + return $snapshot; + } + + // ----- Exception throwers ------------------------------------------------ + + /** + * @param string $char + * @param bool $availability + */ + private function throwExceptionAccordingToConcatenationAvailability($char, $availability) + { + if ($availability === $this->mayConcatenateTagContent) { + throw ParserException::unexpectedCharacter($char, $this->line, $this->column); + } + } + + /** + * @param string $char + */ + private function throwExceptionIfBufferIsEmpty($char) + { + if (empty($this->buffer)) { + throw ParserException::unexpectedCharacter($char, $this->line, $this->column); + } + } + + /** + * @param string $char + */ + private function throwExceptionIfReadingEntry($char) + { + if ($this->isEntryState($this->state)) { + throw ParserException::unexpectedCharacter($char, $this->line, $this->column); + } + } + + // ----- Auxiliaries ------------------------------------------------------- + + /** + * @param string $state + * + * @return bool + */ + private function isEntryState($state) + { + return self::NONE !== $state && self::COMMENT !== $state; + } + + /** + * @param string $char + * + * @return bool + */ + private function isWhitespace($char) + { + return ' ' === $char || "\t" === $char || "\n" === $char || "\r" === $char; + } +} diff --git a/vendor/renanbr/bibtex-parser/src/Processor/DateProcessor.php b/vendor/renanbr/bibtex-parser/src/Processor/DateProcessor.php new file mode 100644 index 0000000..e07ee10 --- /dev/null +++ b/vendor/renanbr/bibtex-parser/src/Processor/DateProcessor.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace RenanBr\BibTexParser\Processor; + +use DateTimeImmutable; + +class DateProcessor +{ + use TagSearchTrait; + const TAG_NAME = '_date'; + + /** + * @var string + */ + private $tagName; + + /** + * @param string $tagName + */ + public function __construct($tagName = null) + { + $this->tagName = $tagName ?: self::TAG_NAME; + } + + /** + * @return array + */ + public function __invoke(array $entry) + { + $yearTag = $this->tagSearch('year', array_keys($entry)); + $monthTag = $this->tagSearch('month', array_keys($entry)); + if (null !== $yearTag && null !== $monthTag) { + $year = (int) $entry[$yearTag]; + $monthArray = explode('~', $entry[$monthTag]); + if (2 === \count($monthArray)) { + list($day, $month) = $monthArray; + $day = (int) $day; + $dateMonthNumber = date_parse($month); + $month = $dateMonthNumber['month'] ?: null; + if (checkdate($month, $day, $year)) { + $timestamp = mktime(0, 0, 0, $month, $day, $year); + $entry[$this->tagName] = new DateTimeImmutable(date('Y-m-d', $timestamp), new \DateTimeZone('UTC')); + } + } + } + + return $entry; + } +} diff --git a/vendor/renanbr/bibtex-parser/src/Processor/FillMissingProcessor.php b/vendor/renanbr/bibtex-parser/src/Processor/FillMissingProcessor.php new file mode 100644 index 0000000..aefbad9 --- /dev/null +++ b/vendor/renanbr/bibtex-parser/src/Processor/FillMissingProcessor.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace RenanBr\BibTexParser\Processor; + +class FillMissingProcessor +{ + use TagSearchTrait; + + /** + * @var array + */ + protected $missingFields; + + public function __construct(array $missingFields) + { + $this->missingFields = $missingFields; + } + + public function __invoke(array $entry) + { + $tags = array_keys($entry); + + foreach ($this->missingFields as $tag => $value) { + if (!$this->tagSearch($tag, $tags)) { + $entry[$tag] = $value; + } + } + + return $entry; + } +} diff --git a/vendor/renanbr/bibtex-parser/src/Processor/KeywordsProcessor.php b/vendor/renanbr/bibtex-parser/src/Processor/KeywordsProcessor.php new file mode 100644 index 0000000..1f19b6b --- /dev/null +++ b/vendor/renanbr/bibtex-parser/src/Processor/KeywordsProcessor.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace RenanBr\BibTexParser\Processor; + +/** + * Splits tags contents as array. + */ +class KeywordsProcessor +{ + use TagCoverageTrait; + + public function __construct() + { + $this->setTagCoverage(['keywords']); + } + + /** + * @return array + */ + public function __invoke(array $entry) + { + $covered = $this->getCoveredTags(array_keys($entry)); + foreach ($covered as $tag) { + $entry[$tag] = preg_split('/, |; /', $entry[$tag]); + } + + return $entry; + } +} diff --git a/vendor/renanbr/bibtex-parser/src/Processor/LatexToUnicodeProcessor.php b/vendor/renanbr/bibtex-parser/src/Processor/LatexToUnicodeProcessor.php new file mode 100644 index 0000000..f5eec61 --- /dev/null +++ b/vendor/renanbr/bibtex-parser/src/Processor/LatexToUnicodeProcessor.php @@ -0,0 +1,104 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace RenanBr\BibTexParser\Processor; + +use Composer\InstalledVersions; +use Exception; +use Pandoc\Pandoc; +use RenanBr\BibTexParser\Exception\ProcessorException; +use RuntimeException; + +/** + * Translates LaTeX texts to unicode. + */ +class LatexToUnicodeProcessor +{ + use TagCoverageTrait; + + /** @var (callable(string): string)|null */ + private $converter; + + /** + * @return array + */ + public function __invoke(array $entry) + { + $covered = $this->getCoveredTags(array_keys($entry)); + foreach ($covered as $tag) { + // Translate string + if (\is_string($entry[$tag])) { + $entry[$tag] = $this->decode($entry[$tag]); + continue; + } + + // Translate array + if (\is_array($entry[$tag])) { + array_walk_recursive($entry[$tag], function (&$text) { + if (\is_string($text)) { + $text = $this->decode($text); + } + }); + } + } + + return $entry; + } + + /** + * @param mixed $text + * + * @return string + */ + private function decode($text) + { + try { + return \call_user_func($this->getConverter(), $text); + } catch (Exception $exception) { + throw new ProcessorException(sprintf('Error while processing LaTeX to Unicode: %s', $exception->getMessage()), 0, $exception); + } + } + + /** + * @return (callable(string): string) + */ + private function getConverter() + { + if ($this->converter) { + return $this->converter; + } + + if (InstalledVersions::isInstalled('ueberdosis/pandoc')) { + $pandoc = new Pandoc(); + + return $this->converter = static function ($text) use ($pandoc) { + // @phpstan-ignore-next-line + return mb_substr($pandoc->input($text)->execute([ + '--from', 'latex', + '--to', 'plain', + '--wrap', 'none', + ]), 0, -1); + }; + } elseif (InstalledVersions::isInstalled('ryakad/pandoc-php')) { + $pandoc = new Pandoc(); + + return $this->converter = static function ($text) use ($pandoc) { + return $pandoc->runWith($text, [ + 'from' => 'latex', + 'to' => 'plain', + 'wrap' => 'none', + ]); + }; + } + + throw new RuntimeException('Pandoc wrapper not installed. Try running "composer require ueberdosis/pandoc"'); + } +} diff --git a/vendor/renanbr/bibtex-parser/src/Processor/NamesProcessor.php b/vendor/renanbr/bibtex-parser/src/Processor/NamesProcessor.php new file mode 100644 index 0000000..2d832eb --- /dev/null +++ b/vendor/renanbr/bibtex-parser/src/Processor/NamesProcessor.php @@ -0,0 +1,243 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace RenanBr\BibTexParser\Processor; + +use RenanBr\BibTexParser\Exception\ProcessorException; + +/** + * Splits names in four parts: First Von Last Jr. + * + * This class includes source code adapted from the Structures_BibTex package, + * (c) Elmar Pitschke , included here under PHP license: + * http://www.php.net/license/3_0.txt + * + * @author Andre Chalom + * + * @see https://github.com/pear/Structures_BibTex + */ +class NamesProcessor +{ + use TagCoverageTrait; + + public function __construct() + { + $this->setTagCoverage(['author', 'editor']); + } + + /** + * @return array + */ + public function __invoke(array $entry) + { + $covered = $this->getCoveredTags(array_keys($entry)); + foreach ($covered as $tag) { + $entry[$tag] = $this->extractAuthors($entry[$tag]); + } + + return $entry; + } + + /** + * Extracting the authors. + * + * @param string $entry The entry with the authors + * + * @return array the extracted authors + * + * @author Elmar Pitschke + */ + private function extractAuthors($entry) + { + // Sanitizes the entry to remove unwanted whitespace + $entry = trim(preg_replace('/\s+/', ' ', $entry)); + + $authorarray = []; + $authorarray = explode(' and ', $entry); + for ($i = 0; $i < \count($authorarray); ++$i) { + $author = trim($authorarray[$i]); + /*The first version of how an author could be written (First von Last) + has no commas in it*/ + $first = ''; + $von = ''; + $last = ''; + $jr = ''; + if (false === mb_strpos($author, ',')) { + $tmparray = []; + $tmparray = preg_split('/[\s\~]/', $author); + $size = \count($tmparray); + if (1 === $size) { //There is only a last + $last = $tmparray[0]; + } elseif (2 === $size) { //There is a first and a last + $first = $tmparray[0]; + $last = $tmparray[1]; + } else { + $invon = false; + $inlast = false; + for ($j = 0; $j < ($size - 1); ++$j) { + if ($inlast) { + $last .= ' '.$tmparray[$j]; + } elseif ($invon) { + try { + $case = $this->determineCase($tmparray[$j]); + + if ((0 === $case) || (-1 === $case)) { //Change from von to last + //You only change when there is no more lower case there + $islast = true; + for ($k = ($j + 1); $k < ($size - 1); ++$k) { + try { + $futurecase = $this->determineCase($tmparray[$k]); + if (0 === $futurecase) { + $islast = false; + } + } catch (ProcessorException $sbe) { + // Ignore + } + } + if ($islast) { + $inlast = true; + if (-1 === $case) { //Caseless belongs to the last + $last .= ' '.$tmparray[$j]; + } else { + $von .= ' '.$tmparray[$j]; + } + } else { + $von .= ' '.$tmparray[$j]; + } + } else { + $von .= ' '.$tmparray[$j]; + } + } catch (ProcessorException $sbe) { + // Ignore + } + } else { + try { + $case = $this->determineCase($tmparray[$j]); + if (0 === $case) { //Change from first to von + $invon = true; + $von .= ' '.$tmparray[$j]; + } else { + $first .= ' '.$tmparray[$j]; + } + } catch (ProcessorException $sbe) { + // Ignore + } + } + } + //The last entry is always the last! + $last .= ' '.$tmparray[$size - 1]; + } + } else { //Version 2 and 3 + $tmparray = []; + $tmparray = explode(',', $author); + //The first entry must contain von and last + $vonlastarray = []; + $vonlastarray = explode(' ', $tmparray[0]); + $size = \count($vonlastarray); + if (1 === $size) { //Only one entry->got to be the last + $last = $vonlastarray[0]; + } else { + $inlast = false; + for ($j = 0; $j < ($size - 1); ++$j) { + if ($inlast) { + $last .= ' '.$vonlastarray[$j]; + } else { + if (0 !== ($this->determineCase($vonlastarray[$j]))) { //Change from von to last + $islast = true; + for ($k = ($j + 1); $k < ($size - 1); ++$k) { + try { + $case = $this->determineCase($vonlastarray[$k]); + if (0 === $case) { + $islast = false; + } + } catch (ProcessorException $sbe) { + // Ignore + } + } + if ($islast) { + $inlast = true; + $last .= ' '.$vonlastarray[$j]; + } else { + $von .= ' '.$vonlastarray[$j]; + } + } else { + $von .= ' '.$vonlastarray[$j]; + } + } + } + $last .= ' '.$vonlastarray[$size - 1]; + } + //Now we check if it is version three (three entries in the array (two commas) + if (3 === \count($tmparray)) { + $jr = $tmparray[1]; + } + //Everything in the last entry is first + $first = $tmparray[\count($tmparray) - 1]; + } + $authorarray[$i] = ['first' => trim($first), 'von' => trim($von), 'last' => trim($last), 'jr' => trim($jr)]; + } + + return $authorarray; + } + + /** + * Case Determination according to the needs of BibTex. + * + * To parse the Author(s) correctly a determination is needed + * to get the Case of a word. There are three possible values: + * - Upper Case (return value 1) + * - Lower Case (return value 0) + * - Caseless (return value -1) + * + * @param string $word + * + * @throws ProcessorException + * + * @return int The Case + * + * @author Elmar Pitschke + */ + private function determineCase($word) + { + $ret = -1; + $trimmedword = trim($word); + /*We need this variable. Without the next of would not work + (trim changes the variable automatically to a string!)*/ + if (\is_string($word) && (mb_strlen($trimmedword) > 0)) { + $i = 0; + $found = false; + $openbrace = 0; + while (!$found && ($i <= mb_strlen($word))) { + $letter = mb_substr($trimmedword, $i, 1); + $ord = \ord($letter); + if (123 === $ord) { //Open brace + ++$openbrace; + } + if (125 === $ord) { //Closing brace + --$openbrace; + } + if (($ord >= 65) && ($ord <= 90) && (0 === $openbrace)) { //The first character is uppercase + $ret = 1; + $found = true; + } elseif (($ord >= 97) && ($ord <= 122) && (0 === $openbrace)) { //The first character is lowercase + $ret = 0; + $found = true; + } else { //Not yet found + ++$i; + } + } + } else { + throw new ProcessorException('Could not determine case on word: '.$word); + } + + return $ret; + } +} diff --git a/vendor/renanbr/bibtex-parser/src/Processor/TagCoverageTrait.php b/vendor/renanbr/bibtex-parser/src/Processor/TagCoverageTrait.php new file mode 100644 index 0000000..6bbea3d --- /dev/null +++ b/vendor/renanbr/bibtex-parser/src/Processor/TagCoverageTrait.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace RenanBr\BibTexParser\Processor; + +trait TagCoverageTrait +{ + use TagSearchTrait; + + /** @var array */ + private $tagCoverageList = [ + '_original', + '_type', + ]; + + /** @var string */ + private $tagCoverageStrategy = 'blacklist'; + + /** + * @param array $tags List of tags to be covered + * @param string $strategy Can assume "whitelist" (default) or "blacklist" + */ + public function setTagCoverage($tags, $strategy = null) + { + $this->tagCoverageList = $tags; + $this->tagCoverageStrategy = $strategy ?: 'whitelist'; + } + + /** + * Calculates which tags are covered. + * + * The search performed internally is case-insensitive. + * + * @return array + */ + protected function getCoveredTags(array $tags) + { + // Finds for actual tag names + $matched = []; + foreach ($this->tagCoverageList as $original) { + $actual = $this->tagSearch($original, $tags); + if (null !== $actual) { + $matched[] = $actual; + } + } + + // Whitelist + if ('whitelist' === $this->tagCoverageStrategy) { + return $matched; + } + + // Blacklist + return array_values(array_diff($tags, $matched)); + } +} diff --git a/vendor/renanbr/bibtex-parser/src/Processor/TagNameCaseProcessor.php b/vendor/renanbr/bibtex-parser/src/Processor/TagNameCaseProcessor.php new file mode 100644 index 0000000..ad6dda8 --- /dev/null +++ b/vendor/renanbr/bibtex-parser/src/Processor/TagNameCaseProcessor.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace RenanBr\BibTexParser\Processor; + +/** + * Change the case of all tag names. + */ +class TagNameCaseProcessor +{ + /** @var int */ + private $case; + + /** + * @param int $case + */ + public function __construct($case) + { + $this->case = $case; + } + + /** + * @return array + */ + public function __invoke(array $entry) + { + return array_change_key_case($entry, $this->case); + } +} diff --git a/vendor/renanbr/bibtex-parser/src/Processor/TagSearchTrait.php b/vendor/renanbr/bibtex-parser/src/Processor/TagSearchTrait.php new file mode 100644 index 0000000..99ab10e --- /dev/null +++ b/vendor/renanbr/bibtex-parser/src/Processor/TagSearchTrait.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace RenanBr\BibTexParser\Processor; + +trait TagSearchTrait +{ + /** + * Searches for the actual name of a tag. + * + * The search performed is case-insensitive. + * + * @param string $needle + * + * @return string|null + */ + protected function tagSearch($needle, array $haystack) + { + foreach ($haystack as $actual) { + if (0 === strcasecmp($needle, $actual)) { + return $actual; + } + } + + return null; + } +} diff --git a/vendor/renanbr/bibtex-parser/src/Processor/TrimProcessor.php b/vendor/renanbr/bibtex-parser/src/Processor/TrimProcessor.php new file mode 100644 index 0000000..bc77595 --- /dev/null +++ b/vendor/renanbr/bibtex-parser/src/Processor/TrimProcessor.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace RenanBr\BibTexParser\Processor; + +/** + * @author Florent DESPIERRES + */ +class TrimProcessor +{ + use TagCoverageTrait; + + public function __construct(array $fields = null) + { + if ($fields) { + $this->setTagCoverage($fields); + } + } + + /** + * @return array + */ + public function __invoke(array $entry) + { + $covered = $this->getCoveredTags(array_keys($entry)); + foreach ($covered as $tag) { + $entry[$tag] = $this->trim($entry[$tag]); + } + + return $entry; + } + + private function trim($value) + { + if (\is_array($value)) { + $trimmed = []; + foreach ($value as $key => $subValue) { + $trimmed[$key] = $this->trim($subValue); + } + + return $trimmed; + } + + if (\is_string($value)) { + return trim($value); + } + + return $value; + } +} diff --git a/vendor/renanbr/bibtex-parser/src/Processor/UrlFromDoiProcessor.php b/vendor/renanbr/bibtex-parser/src/Processor/UrlFromDoiProcessor.php new file mode 100644 index 0000000..7309a6a --- /dev/null +++ b/vendor/renanbr/bibtex-parser/src/Processor/UrlFromDoiProcessor.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace RenanBr\BibTexParser\Processor; + +class UrlFromDoiProcessor +{ + use TagSearchTrait; + + const FORMAT = 'https://doi.org/%s'; + + /** + * @var string + */ + private $urlFormat; + + /** + * @param string $urlFormat + */ + public function __construct($urlFormat = null) + { + $this->urlFormat = $urlFormat ?: self::FORMAT; + } + + /** + * @return array + */ + public function __invoke(array $entry) + { + $doiTag = $this->tagSearch('doi', array_keys($entry)); + $urlTag = $this->tagSearch('url', array_keys($entry)); + if (null === $urlTag && null !== $doiTag) { + $doiValue = $entry[$doiTag]; + if (\is_string($doiValue) && '' !== $doiValue) { + $entry['url'] = sprintf($this->urlFormat, $doiValue); + } + } + + return $entry; + } +}