[
  {
    "path": ".codeclimate.yml",
    "content": "plugins:\n  sonar-php:\n    enabled: true\n    config:\n      tests_patterns:\n        - tests/**\n  fixme:\n    enabled: true\n"
  },
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*]\ncharset = utf-8\ntrim_trailing_whitespace = true\nend_of_line = lf\ninsert_final_newline = true\nindent_style = space\nij_visual_guides = 80, 120\n\n[*.php]\nindent_size = 4\nmax_line_length = 120\nij_php_align_multiline_chained_methods = true\nij_php_align_phpdoc_comments = true\nij_php_align_phpdoc_param_names = true\nij_php_align_assignments = true\nij_php_align_multiline_array_initializer_expression = true\nij_php_align_key_value_pairs = true\nij_php_align_match_arm_bodies = true\nij_php_phpdoc_use_fqcn = true\n\n[*.{yml, yaml, neon, xml, svg, json, js}]\nindent_size = 2\n\n[*.{md, markdown}]\ntrim_trailing_whitespace = false\nindent_size = 2\nmax_line_length = 80\nij_markdown_wrap_text_if_long = true\n"
  },
  {
    "path": ".gitattributes",
    "content": "/.github            export-ignore\n/.idea              export-ignore\n/config             export-ignore\n/docs               export-ignore\n/resources          export-ignore\n/tests              export-ignore\n/.codeclimate.yml   export-ignore\n/.editorconfig      export-ignore\n/.gitattributes     export-ignore\n/.gitignore         export-ignore\n/build.xml          export-ignore\n/mkdocs.yml         export-ignore\n/README.md          export-ignore\n"
  },
  {
    "path": ".github/workflows/docs.yaml",
    "content": "name: Publish documentation\n\non:\n  release:\n    types: [ published ]\n\njobs:\n  build:\n    name: Deploy docs\n    runs-on: ubuntu-20.04\n    steps:\n\n      - name: Checkout main\n        uses: actions/checkout@v2\n\n      - name: Deploy docs\n        uses: mhausenblas/mkdocs-deploy-gh-pages@master\n        env:\n          SITE_NAME: \"Case Converter ${{ github.event.release.tag_name }}\"\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/qa.yaml",
    "content": "name: Quality assurance\n\non:\n  pull_request:\n  push:\n    branches:\n      - master\n\njobs:\n  tests:\n    strategy:\n      matrix:\n        php: [ '7.4','8.0','8.1','8.2','8.3','8.4' ]\n    runs-on: ubuntu-24.04\n    steps:\n\n      - name: Setup PHP\n        uses: shivammathur/setup-php@v2\n        with:\n          php-version: ${{ matrix.php }}\n          extensions: mbstring, intl\n          coverage: xdebug\n          tools: phing:v3\n\n      - uses: actions/checkout@v2\n\n      - name: Phing setup\n        run: phing setup\n\n      - name: lint php\n        run: phing php:lint\n\n      - name: validate composer\n        run: phing composer:validate\n\n      - name: behat\n        run: phing behat:run\n\n      - name: phpstan\n        run: phing phpstan:analyse\n\n      - name: CC before build\n        run: phing cc:before-build\n        env:\n          CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}\n\n      - name: PHPUnit\n        run: phing phpunit:run\n\n      - name: CC after build\n        run: phing cc:after-build\n        env:\n          CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}\n"
  },
  {
    "path": ".gitignore",
    "content": "/.idea/\n!/.idea/dictionaries/\n/resources/coverage/\n/resources/phpdoc/\n/vendor/\n/bin/\n/composer-setup.php\n/composer.lock\n/coverage/codeclimate.json\n/resources/phploc.txt\n/resources/coverage-html/\n/site/\n/docs/CONTRIBUTING.md\n/docs/LICENSE.md\n/docs/license.md\n/docs/contributing.md\n/docs/changelog.md\n/build.png\n/build.svg\n/config/.phpunit.result.cache\n/clover.xml\n"
  },
  {
    "path": "LICENSE.md",
    "content": "The MIT License (MIT)\n=====================\n\nCopyright © 2015-2022 Jawira Portugal\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "Case converter\n==============\n\nUse this library to convert string between:\n\n| Name              | Method         | Output example    |\n|-------------------|----------------|-------------------|\n| 🐪 Camel case     | `toCamel()`    | `myNameIsBond`    |\n| 👨‍🏫 Pascal case | `toPascal()`   | `MyNameIsBond`    |\n| 🐍 Snake case     | `toSnake()`    | `my_name_is_bond` |\n| 👩‍🏫 Ada case    | `toAda()`      | `My_Name_Is_Bond` |\n| Ⓜ️ Macro case     | `toMacro()`    | `MY_NAME_IS_BOND` |\n| 🥙 Kebab case     | `toKebab()`    | `my-name-is-bond` |\n| 🚂 Train case     | `toTrain()`    | `My-Name-Is-Bond` |\n| 🏦 Cobol case     | `toCobol()`    | `MY-NAME-IS-BOND` |\n| 🔡 Lower case     | `toLower()`    | `my name is bond` |\n| 🔠 Upper case     | `toUpper()`    | `MY NAME IS BOND` |\n| 📰 Title case     | `toTitle()`    | `My Name Is Bond` |\n| ✍️ Sentence case  | `toSentence()` | `My name is bond` |\n| ⚙️ Dot notation   | `toDot()`      | `my.name.is.bond` |\n\nFeatures:\n\n* 🔁 [automatic case detection][detection algorithm]\n* 🏭 [factory][]\n* 🌐 [i18n](#i18n)\n\n[![Packagist Version](https://img.shields.io/packagist/v/jawira/case-converter?style=for-the-badge)](https://packagist.org/packages/jawira/case-converter)\n[![Packagist PHP Version Support](https://img.shields.io/packagist/php-v/jawira/case-converter?style=for-the-badge)](https://packagist.org/packages/jawira/case-converter)\n[![Packagist Downloads](https://img.shields.io/packagist/dt/jawira/case-converter?style=for-the-badge)](https://packagist.org/packages/jawira/case-converter)\n[![Packagist License](https://img.shields.io/packagist/l/jawira/case-converter?style=for-the-badge)](https://packagist.org/packages/jawira/case-converter)  \n[![Maintainability](https://api.codeclimate.com/v1/badges/35677f6ce7dac27a5d0c/maintainability)](https://codeclimate.com/github/jawira/case-converter/maintainability)\n[![Test Coverage](https://api.codeclimate.com/v1/badges/35677f6ce7dac27a5d0c/test_coverage)](https://codeclimate.com/github/jawira/case-converter/test_coverage)\n\nUsage\n-----\n\nInput string (i.e. _john-connor_) format is going to be\n[detected automatically][detection algorithm]. Here's an example:\n\n```php\nuse Jawira\\CaseConverter\\Convert;\n\n$hero = new Convert('john-connor');\n\necho $hero->toCamel();   // output: johnConnor\n```\n\nOf course you can explicitly set the format of input string:\n\n```php\necho $hero->fromKebab()->toSnake();   // output: john_connor\n```\n\nYou can also use the [provided factory][factory] to instantiate `Convert` class.\nA list of [all public methods] is also available.\n\ni18n\n----\n\nFully compatible with non-english alphabets:\n\n```php\n// Spanish\n$esp = new Convert('DON_RAMÓN_Y_ÑOÑO');\necho $esp->toCamel();   // output: donRamónYÑoño\n\n// Greek\n$grc = new Convert('πολύ-Καλό');\necho $grc->toCamel();   // output: πολύΚαλό\n\n// Russian\n$rus = new Convert('ОЧЕНЬ_ПРИЯТНО');\necho $rus->toCamel();   // output: оченьПриятно\n```\n\n`case-converter` is compatible with _Simple Case-Mapping_ and _Full\nCase-Mapping_.\n[Learn more about Case-Mapping][Case-Mapping].\n\nInstallation\n------------\n\n```console\ncomposer require jawira/case-converter\n```\n\nDocumentation\n-------------\n\n<https://jawira.github.io/case-converter/>\n\nContributing\n------------\n\n- If you liked this project, ⭐ star it on GitHub.\n  [![GitHub Repo stars](https://img.shields.io/github/stars/jawira/case-converter?style=social)](https://github.com/jawira/case-converter)\n- Or follow me on X.\n  [![Twitter Follow](https://img.shields.io/twitter/follow/jawira?style=social)](https://twitter.com/jawira)\n\nLicense\n-------\n\nThis library is licensed under the [MIT LICENSE].\n\n<!--mkdocs: Do not use relative path for links and images-->\n\n[all public methods]: https://jawira.github.io/case-converter/api.html\n\n[CONTRIBUTING.md]: https://jawira.github.io/case-converter/contributing.html\n\n[Countable interface]: https://php.net/manual/en/class.countable.php\n\n[Case-Mapping]: https://jawira.github.io/case-converter/case-mapping.html\n\n[magic method]: https://www.php.net/manual/en/language.oop5.magic.php#object.tostring\n\n[MIT LICENSE]: https://jawira.github.io/case-converter/license.html\n\n[open an issue]: https://github.com/jawira/case-converter/issues/new\n\n[detection algorithm]: https://jawira.github.io/case-converter/detection-algorithm.html\n\n[factory]: https://jawira.github.io/case-converter/using-the-factory.html\n\n[GitHub]: https://github.com/jawira/case-converter/\n\n***\n\nPackages from jawira\n--------------------\n\n<dl>\n\n<dt>\n    <a href=\"https://packagist.org/packages/jawira/emoji-catalog\">jawira/emoji-catalog\n    <img alt=\"GitHub stars\" src=\"https://badgen.net/github/stars/jawira/emoji-catalog?icon=github\"/></a>\n</dt>\n<dd>Get access to +3000 emojis as class constants.</dd>\n\n<dt>\n    <a href=\"https://packagist.org/packages/jawira/plantuml-client\"> jawira/plantuml-client\n    <img alt=\"GitHub stars\" src=\"https://badgen.net/github/stars/jawira/plantuml-client?icon=github\"/></a>\n</dt>\n<dd>Convert PlantUML diagrams into images (svg, png, ...).</dd>\n\n<dt>\n    <a href=\"https://packagist.org/packages/jawira/doctrine-diagram-bundle\">jawira/doctrine-diagram-bundle\n    <img alt=\"GitHub stars\" src=\"https://badgen.net/github/stars/jawira/doctrine-diagram-bundle?icon=github\"/></a>\n</dt>\n<dd>Symfony Bundle to generate database diagrams.</dd>\n\n<dt><a href=\"https://packagist.org/packages/jawira/\">more...</a></dt>\n</dl>\n"
  },
  {
    "path": "build.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project name=\"Case Converter\" description=\"Case Converter class\" default=\"help\" phingVersion=\"3\">\n\n  <defaultexcludes default=\"true\"/><!--Initializing default excludes-->\n  <defaultexcludes add=\"**/.idea\"/>\n  <defaultexcludes add=\"**/.idea/**\"/>\n\n  <target name=\"help\">\n    <visualizer format=\"svg\"/>\n    <open path=\"build.svg\"/>\n  </target>\n\n  <target name=\"setup\" description=\"Install dependencies\" depends=\"composer:install, cc:get\"/>\n\n  <target name=\"qa\" description=\"Run quality tests\"\n          depends=\"php:lint, composer:validate, phpunit:run, behat:run, phpstan:analyse\"/>\n\n  <target name=\"docs:refresh-images\" description=\"Update and regenerate docs images\"\n          depends=\"phing:visualize, plantuml:puml-to-png\"/>\n\n  <target name=\"composer:validate\" description=\"Check composer.json syntax\">\n    <composer command=\"validate\">\n      <arg value=\"--strict\"/>\n      <arg value=\"--no-check-lock\"/>\n    </composer>\n  </target>\n\n  <target name=\"composer:install\">\n    <composer command=\"install\">\n      <arg value=\"--no-interaction\"/>\n      <arg value=\"--profile\"/>\n      <arg value=\"--ansi\"/>\n      <arg value=\"--prefer-dist\"/>\n    </composer>\n  </target>\n\n  <target name=\"phpunit:open-coverage\" description=\"Open coverage site\" depends=\"phpunit:run\">\n    <exec executable=\"xdg-open\">\n      <arg file=\"resources/coverage-html/index.html\"/>\n    </exec>\n  </target>\n\n  <target name=\"php:lint\" description=\"Check syntax on PHP files\">\n    <phplint haltonfailure=\"true\">\n      <fileset dir=\"${project.basedir}\">\n        <include name=\"**/*.php\"/>\n        <exclude name=\"vendor/\"/>\n      </fileset>\n    </phplint>\n  </target>\n\n  <target name=\"behat:run\" description=\"Run behat tests\">\n    <exec executable=\"vendor/bin/behat\" passthru=\"true\" checkreturn=\"true\">\n      <arg value=\"--colors\"/>\n      <arg value=\"--no-interaction\"/>\n      <arg value=\"--stop-on-failure\"/>\n      <arg value=\"--rerun\"/>\n      <arg value=\"--strict\"/>\n    </exec>\n  </target>\n\n  <target name=\"phpunit:run\" description=\"Run unit tests\">\n    <mkdir dir=\"resources/coverage-html/\"/>\n    <exec executable=\"vendor/bin/phpunit\" passthru=\"true\" checkreturn=\"true\">\n      <env key=\"XDEBUG_MODE\" value=\"coverage\"/>\n      <arg line=\"--configuration=config/phpunit.xml\"/>\n      <arg value=\"--testdox\"/>\n    </exec>\n  </target>\n\n  <target name=\"changelog:links\" description=\"Update links in composer.json\">\n    <composer command=\"require\">\n      <arg value=\"symplify/changelog-linker:^v6\"/>\n    </composer>\n    <exec executable=\"vendor/bin/changelog-linker\">\n      <arg value=\"link\"/>\n    </exec>\n    <composer command=\"remove\">\n      <arg value=\"symplify/changelog-linker\"/>\n    </composer>\n  </target>\n\n  <target name=\"cc:get\" description=\"Download Code Climate\">\n    <property name=\"cc.dir\" value=\"bin\"/>\n    <property name=\"cc.filename\" value=\"cc-test-reporter\"/>\n    <if>\n      <not>\n        <available file=\"${cc.dir}/${cc.filename}\"/>\n      </not>\n      <then>\n        <mkdir dir=\"${cc.dir}\"/>\n        <httpget dir=\"${cc.dir}\" filename=\"${cc.filename}\" followRedirects=\"true\"\n                 url=\"https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64\"/>\n      </then>\n    </if>\n    <chmod file=\"${cc.dir}/${cc.filename}\" mode=\"0775\"/>\n    <exec executable=\"${cc.dir}/${cc.filename}\" passthru=\"true\" checkreturn=\"true\">\n      <arg value=\"--version\"/>\n    </exec>\n  </target>\n\n  <target name=\"cc:before-build\" description=\"To run before a build\">\n    <fail unless=\"env.CC_TEST_REPORTER_ID\" message=\"you must supply a CC_TEST_REPORTER_ID ENV variable\"/>\n    <exec executable=\"bin/cc-test-reporter\" passthru=\"true\" checkreturn=\"true\">\n      <arg value=\"before-build\"/>\n    </exec>\n  </target>\n\n  <target name=\"cc:after-build\" description=\"Handle &amp; upload coverage payload\">\n    <fail unless=\"env.CC_TEST_REPORTER_ID\" message=\"you must supply a CC_TEST_REPORTER_ID ENV variable\"/>\n    <exec executable=\"bin/cc-test-reporter\" passthru=\"true\" checkreturn=\"true\">\n      <arg value=\"after-build\"/>\n      <arg line=\"--coverage-input-type clover\"/>\n    </exec>\n  </target>\n\n  <target name=\"plantuml:puml-to-png\" description=\"Parse PlantUML files to image from docs dir\">\n    <composer command=\"require\">\n      <arg value=\"jawira/plantuml\"/>\n    </composer>\n    <exec executable=\"vendor/bin/plantuml\" passthru=\"true\" checkreturn=\"true\">\n      <arg value=\"-checkmetadata\"/>\n      <arg path=\"docs/**.puml\"/>\n    </exec>\n    <composer command=\"remove\">\n      <arg value=\"jawira/plantuml\"/>\n    </composer>\n  </target>\n\n  <target name=\"git:tags-listing\" description=\"List all git tags\">\n    <!-- https://stackoverflow.com/a/34239190/4345061 -->\n    <exec executable=\"git\" passthru=\"true\">\n      <arg value=\"log\"/>\n      <arg line=\"--graph --all --decorate --oneline --simplify-by-decoration\"/>\n    </exec>\n  </target>\n\n  <target name=\"phing:visualize\" description=\"Update build.png\">\n    <visualizer format=\"puml\" destination=\"docs/images\"/>\n  </target>\n\n  <target name=\"docs:serve\" description=\"Generate and open static site\" depends=\"docs:build\">\n    <parallel threadCount=\"2\">\n      <exec executable=\"mkdocs\" passthru=\"true\" checkreturn=\"true\">\n        <arg value=\"serve\"/>\n      </exec>\n      <exec executable=\"xdg-open\" passthru=\"true\" checkreturn=\"true\">\n        <!--Please F5 after some seconds-->\n        <arg value=\"http://127.0.0.1:8000/\"/>\n      </exec>\n    </parallel>\n  </target>\n\n  <target name=\"docs:build\" description=\"Builds static site\" depends=\"docs:refresh-images\">\n    <exec executable=\"mkdocs\" passthru=\"true\" checkreturn=\"true\">\n      <arg value=\"build\"/>\n    </exec>\n  </target>\n\n  <target name=\"phpstan:analyse\" description=\"Analyse at max level\">\n    <exec executable=\"vendor/bin/phpstan\" passthru=\"true\" checkreturn=\"true\">\n      <arg value=\"analyse\"/>\n      <arg line=\"--level=max\"/>\n      <arg value=\"--no-progress\"/>\n      <arg value=\"--no-interaction\"/>\n      <arg value=\"--ansi\"/>\n      <arg path=\"src\"/>\n    </exec>\n  </target>\n\n</project>\n"
  },
  {
    "path": "composer.json",
    "content": "{\n  \"name\": \"jawira/case-converter\",\n  \"type\": \"library\",\n  \"description\": \"Convert strings between 13 naming conventions: Snake case, Camel case, Pascal case, Kebab case, Ada case, Train case, Cobol case, Macro case, Upper case, Lower case, Sentence case, Title case and Dot notation.\",\n  \"keywords\": [\n    \"Ada case\",\n    \"Camel case\",\n    \"Cobol case\",\n    \"Dot notation\",\n    \"Kebab case\",\n    \"Lower case\",\n    \"Macro case\",\n    \"Pascal case\",\n    \"Sentence case\",\n    \"Snake case\",\n    \"Title case\",\n    \"Train case\",\n    \"Upper case\"\n  ],\n  \"homepage\": \"https://jawira.github.io/case-converter/\",\n  \"license\": \"MIT\",\n  \"authors\": [\n    {\n      \"name\": \"Jawira Portugal\",\n      \"email\": \"dev@tugal.be\"\n    }\n  ],\n  \"require\": {\n    \"php\": \">=7.4\",\n    \"ext-mbstring\": \"*\"\n  },\n  \"require-dev\": {\n    \"behat/behat\": \"^3.0\",\n    \"phpstan/phpstan\": \"^v2\",\n    \"phpunit/phpunit\": \"^9.0\"\n  },\n  \"suggest\": {\n    \"pds/skeleton\": \"PHP Package Development Standards\",\n    \"phing/phing\": \"PHP Build Tool\"\n  },\n  \"config\": {\n    \"preferred-install\": \"dist\",\n    \"sort-packages\": true\n  },\n  \"autoload\": {\n    \"psr-4\": {\n      \"Jawira\\\\CaseConverter\\\\\": \"src/\"\n    }\n  }\n}\n"
  },
  {
    "path": "config/behat.yml",
    "content": "default:\n  autoload:\n    '': '%paths.base%/../tests/behat/bootstrap/'\n  suites:\n    default:\n      paths:\n      - '%paths.base%/../tests/behat/'\n      contexts:\n      - FeatureContext\n  formatters:\n    pretty: true\n"
  },
  {
    "path": "config/phpunit.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<phpunit xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:noNamespaceSchemaLocation=\"https://schema.phpunit.de/9.3/phpunit.xsd\"\n         bootstrap=\"../vendor/autoload.php\"\n         forceCoversAnnotation=\"true\"\n         beStrictAboutCoversAnnotation=\"true\"\n         beStrictAboutOutputDuringTests=\"true\"\n         beStrictAboutTodoAnnotatedTests=\"true\"\n         colors=\"true\"\n         stopOnFailure=\"true\"\n         stopOnRisky=\"true\"\n         stopOnDefect=\"true\"\n         stopOnWarning=\"true\"\n         stopOnIncomplete=\"true\"\n         stopOnSkipped=\"true\"\n         stopOnError=\"true\">\n\n  <coverage processUncoveredFiles=\"true\">\n    <include>\n      <directory suffix=\".php\">../src</directory>\n    </include>\n    <report>\n      <clover outputFile=\"../clover.xml\"/>\n      <html outputDirectory=\"../resources/coverage-html/\"/>\n      <text outputFile=\"php://stdout\" showUncoveredFiles=\"true\"/>\n    </report>\n  </coverage>\n\n  <testsuite name=\"default\">\n    <directory>../tests/phpunit</directory>\n  </testsuite>\n\n  <logging/>\n\n</phpunit>\n"
  },
  {
    "path": "docs/api.md",
    "content": "API\n===\n\nList of public methods.\n\n`\\Jawira\\CaseConverter\\Convert`\n-------------------------------\n\n### String conversion\n\n| Method         | Description                             |\n|----------------|-----------------------------------------|\n| `toCamel()`    | Return string in _Camel case_ format    |\n| `toPascal()`   | Return string in _Pascal case_ format   |\n| `toSnake()`    | Return string in _Snake case_ format    |\n| `toAda()`      | Return string in _Ada case_ format      |\n| `toMacro()`    | Return string in _Macro case_ format    |\n| `toKebab()`    | Return string in _Kebab case_ format    |\n| `toTrain()`    | Return string in _Train case_ format    |\n| `toCobol()`    | Return string in _Cobol case_ format    |\n| `toLower()`    | Return string in _Lower case_ format    |\n| `toUpper()`    | Return string in _Upper case_ format    |\n| `toTitle()`    | Return string in _Title case_ format    |\n| `toSentence()` | Return string in _Sentence case_ format |\n| `toDot()`      | Return string in _Dot notation_         |\n\n### Explicit case detection\n\n| Method           | Description                                         |\n|------------------|-----------------------------------------------------|\n| `fromAuto()`     | (default) Auto-detect naming convention             |\n| `fromCamel()`    | Split input string using uppercase characters       |\n| `fromPascal()`   | Split input string using uppercase characters       |\n| `fromSnake()`    | Split input string using `_` (underscore character) |\n| `fromAda()`      | Split input string using `_` (underscore character) |\n| `fromMacro()`    | Split input string using `_` (underscore character) |\n| `fromKebab()`    | Split input string using `-` (dash character)       |\n| `fromTrain()`    | Split input string using `-` (dash character)       |\n| `fromCobol()`    | Split input string using `-` (dash character)       |\n| `fromLower()`    | Split input string using `␣` (space character)      |\n| `fromUpper()`    | Split input string using `␣` (space character)      |\n| `fromTitle()`    | Split input string using `␣` (space character)      |\n| `fromSentence()` | Split input string using `␣` (space character)      |\n| `fromDot()`      | Split input string using `.` (dot character)        |\n\nPlease note that some methods are equivalent and have the same effect:\n\n- `fromDot()`\n- `fromCamel()` ≈ `fromPascal()`\n- `fromSnake()` ≈ `fromAda()` ≈ `fromMacro()`\n- `fromKebab()` ≈ `fromTrain()` ≈ `fromCobol()`\n- `fromLower()` ≈ `fromUpper()` ≈ `fromTitle()` ≈ `fromSentence()`\n\nAll these methods exists only for sake of completeness.\n\n### Utility methods\n\n| Method                     | Description                                                                    |\n|----------------------------|--------------------------------------------------------------------------------|\n| `getSource()`              | Returns original input string                                                  |\n| `toArray()`                | Returns array with detected words                                              |\n| `forceSimpleCaseMapping()` | Output sting uses [Simple Case-Mapping] even if you are using PHP 7.3 or newer |\n\n`\\Jawira\\CaseConverter\\CaseConverter`\n-------------------------------------\n\n### Factory method\n\n| Method      | Description                |\n|-------------|----------------------------|\n| `convert()` | Creates a `Convert` object |\n\n[Simple Case-Mapping]: ./case-mapping.md\n\n"
  },
  {
    "path": "docs/case-mapping.md",
    "content": "Case-Mapping\n============\n\nIntroduction\n------------\n\n_Case-mapping_ or _case conversion_ is performed everytime a character is\nchanged from _upper case_ to _lower case_, or from _lower case_ to _upper case_.\n_Case converter_ performs _case-mapping_ everytime you use it.\n\nThere are two kind of case-mapping:\n\n1. _Simple case-mapping_\n2. _Full case-mapping_\n\n_Simple case-mapping_ is one-to-one character mapping, for example a single\ncharacter \"`A`\" is replaced with another single character \"`a`\".\n\nAs you can image, _Full case-mapping_ performs one-to-many character\nreplacements (more precisely one-to-many code-points).\nIn real world use-cases, it's rare to perform _full case-mapping_, this is\nbecause it only concerns a very small set of characters. For example in german\nlanguage, the letter \"`ß`\" is strictly lowercase and should be mapped to \"`SS`\"\nin uppercase words.\n\nCase-Converter behaviour\n------------------------\n\nBy default, Case-Converter will perform _full case-mapping_\n\n```php\n// Full case-mapping\n$ger = new Convert('Straße');\necho $ger->toUpper(); // output: STRASSE\n```\n\nIf you want to perform _simple case-mapping_ then you have to\ncall `->forceSimpleCaseMapping()`:\n\n```php\n// Simple case-mapping\n$ger = new Convert('Straße');\n$ger->forceSimpleCaseMapping();\necho $ger->toUpper(); // output: STRAßE\n```\n\nAs you can see, in _full case-mapping_ string length can change.\n\nCase-Mapping in PHP\n-------------------\n\nPHP 7.3 introduced _full case-mapping_, you can have one-to-many character\nmapping. In practice this means than you can have different results depending on\nyour PHP version.\n\nInternally Case-Converter uses _mb_convert_case()_ . This function works in\nconjunction with specific constants to tell what action to perform. For example:\n\n```php\nmb_convert_case('Foo', MB_CASE_UPPER); // FOO\n```\n\nPrior to PHP 7.3, these were the available constants and their use:\n\n| Constant      | Meaning                                     |\n|---------------|---------------------------------------------|\n| MB_CASE_UPPER | Performs simple upper-case fold conversion. |\n| MB_CASE_LOWER | Performs simple lower-case fold conversion. |\n| MB_CASE_TITLE | Performs simple title-case fold conversion. |\n\nBut from PHP 7.3, new constants were added and their meaning changed:\n\n| Constant             | Meaning                                     |\n|----------------------|---------------------------------------------|\n| MB_CASE_UPPER        | Performs a full upper-case folding.         |\n| MB_CASE_LOWER        | Performs a full lower-case folding.         |\n| MB_CASE_TITLE        | Performs a full title-case conversion.      |\n| MB_CASE_UPPER_SIMPLE | Performs simple upper-case fold conversion. |\n| MB_CASE_LOWER_SIMPLE | Performs simple lower-case fold conversion. |\n| MB_CASE_TITLE_SIMPLE | Performs simple title-case fold conversion. |\n\nLocale dependent mapping\n------------------------\n\nSome case-mapping are locale dependent. This is the case of Turkish where the\nsmall letter \"`i`\" should be replaced by a capital letter with a dot \"`İ`\".\nHowever, according to documentation:\n\n> Only unconditional, language agnostic full case-mapping is performed.\n\nThis means that locale dependent mapping are ignored and not performed.\n\nResources\n---------\n\n<dl>\n<dt>PHP 7.3 Full Case-Mapping and Case-Folding Support</dt>\n<dd><a href=\"https://www.php.net/manual/en/migration73.new-features.php#migration73.new-features.mbstring.case-mapping-folding\">https://www.php.net/manual/en/migration73.new-features.php#migration73.new-features.mbstring.case-mapping-folding</a></dd>\n<dt>mb_convert_case()</dt>\n<dd><a href=\"https://www.php.net/manual/en/function.mb-convert-case.php\">https://www.php.net/manual/en/function.mb-convert-case.php</a></dd>\n<dt>mbstring constant</dt>\n<dd><a href=\"https://www.php.net/manual/en/mbstring.constants.php\">https://www.php.net/manual/en/mbstring.constants.php</a></dd>\n</dl>\n"
  },
  {
    "path": "docs/detection-algorithm.md",
    "content": "Detection algorithm\n===================\n\nWhen `\\Jawira\\CaseConverter\\Convert` class is instantiated, the input string is\nanalyzed to detect the words in it.\n\nThis is also the algorithm used by `Convert::fromAuto` method.\n\n![Detection algorithm](images/detection-algorithm.png \"Detection algorithm\")\n\n1. If input string contains `_` (underscore character), then `_` is used to\n   split string.\n2. If input string contains `-` (dash character), then `-` is used to split\n   string.\n3. If input string contains `␣` (space character), then `␣` is used to split\n   string.\n4. If input string contains `.` (space character), then `.` is used to split\n   string.\n5. If input string contains uppercase characters only, then the input string is\n   considered to be a single word.\n6. Finally, uppercase characters are used to split string.\n"
  },
  {
    "path": "docs/dev.md",
    "content": "Development notes\n=================\n\nPhing targets\n-------------\n\n[![Phing targets](./images/build.png \"Phing targets\")](./images/build.png)\n\n- `$ phing setup`: Prepare project for development.\n- `$ phing qa`: Run quality tests, use this before every commit.\n\nDocumentation\n-------------\n\nDocumentation is built when a `release` is created.\n\nTo install **mkdocs** locally type:\n\n[comment]: <> (https://stackoverflow.com/a/41352413/4345061)\n\n```console\nsudo -H pip install mkdocs\n```\n\nUsing Phing behind a proxy\n--------------------------\n\nIf you are developing behind a proxy, you have to set the environment\nvariable `http_proxy`. This variable already is imported in `build.xml`, so you\nhave nothing to do.\n\nCreating new convention\n-----------------------\n\n1. Create new Gluer class.\n2. Create new Split class.\n3. Update `\\Jawira\\CaseConverter\\Convert::analyse` if needed.\n4. Register into `\\Jawira\\CaseConverter\\Convert::handleSplitterMethod`.\n5. Register into `\\Jawira\\CaseConverter\\Convert::handleGluerMethod`.\n6. Update docblock `\\Jawira\\CaseConverter\\Convert` to register new methods.\n7. Update documentation.\n\nRailroad diagram\n----------------\n\n- <https://tabatkins.github.io/railroad-diagrams/generator.html>\n\nClass diagrams\n--------------\n\n[![Phing targets](./images/uml-case-converter.png \"CaseConverter namespace\")](./images/uml-case-converter.png)\n\n[![Phing targets](./images/uml-glue.png \"Glue namespace\")](./images/uml-glue.png)\n\n[![Phing targets](./images/uml-split.png \"Split namespace\")](./images/uml-split.png)\n\n[git-flow]: https://github.com/petervanderdoes/gitflow-avh\n\n[Keep a changelog]: http://keepachangelog.com/en/1.0.0/\n\n[mkdocs]: https://www.mkdocs.org/#installation\n\n[mkdocs-material]: https://github.com/squidfunk/mkdocs-material\n\n[pds/skeleton]: https://github.com/php-pds/skeleton\n\n[Phive]: https://phar.io/\n\n[Semantic Versioning]: http://semver.org/\n\n[Composer]: https://getcomposer.org/\n"
  },
  {
    "path": "docs/images/build.puml",
    "content": "@startuml\n\ntitle\nCase Converter\nend title\n\ntop to bottom direction\n\nskinparam Shadowing false\nskinparam ArrowFontColor Black\nskinparam ArrowThickness 2\nskinparam UseCaseBackgroundColor #FFFECC\nskinparam UseCaseBorderColor #333333\nskinparam UseCaseBorderThickness 2\nskinparam UseCaseFontColor Black\n\n\n(setup) -[#1A85FF]-> (composer:install) : depend:1\n(setup) -[#1A85FF]-> (cc:get) : depend:2\n(qa) -[#1A85FF]-> (php:lint) : depend:1\n(qa) -[#1A85FF]-> (composer:validate) : depend:2\n(qa) -[#1A85FF]-> (phpunit:run) : depend:3\n(qa) -[#1A85FF]-> (behat:run) : depend:4\n(qa) -[#1A85FF]-> (phpstan:analyse) : depend:5\n(qa) -[#1A85FF]-> (psalm:run) : depend:6\n(docs:refresh-images) -[#1A85FF]-> (phing:visualize) : depend:1\n(docs:refresh-images) -[#1A85FF]-> (plantuml:puml-to-png) : depend:2\n(phpunit:open-coverage) -[#1A85FF]-> (phpunit:run) : depend:1\n(docs:serve) -[#1A85FF]-> (docs:build) : depend:1\n(docs:build) -[#1A85FF]-> (docs:refresh-images) : depend:1\n(help)\n(setup)\n(qa)\n(docs:refresh-images)\n(composer:validate)\n(composer:install)\n(phpunit:open-coverage)\n(php:lint)\n(behat:run)\n(phpunit:run)\n(changelog:links)\n(cc:get)\n(cc:before-build)\n(cc:after-build)\n(plantuml:puml-to-png)\n(git:tags-listing)\n(phing:visualize)\n(docs:serve)\n(docs:build)\n(psalm:run)\n(phpstan:analyse)\n\n\n@enduml\n"
  },
  {
    "path": "docs/images/detection-algorithm.puml",
    "content": "@startuml\nstart\nif ($input  contains \"**_**\") then (yes)\n  :Split using \"**_**\";\nelseif ($input contains \"**-**\") then (yes)\n  :Split using \"**-**\";\nelseif ($input contains \"**␣**\") then (yes)\n  :Split using \"**␣**\";\nelseif ($input contains \"**.**\") then (yes)\n  :Split using \"**.**\";\nelseif ($input is in uppercase only) then (yes)\n  :$input is a single word;\nelse (no)\n  :Split using capital letters;\nendif\nstop\n@enduml\n"
  },
  {
    "path": "docs/images/number-problem.puml",
    "content": "@startuml\nleft to right direction\nhide empty description\n\nstate \"hello-8-world\" as state1\nstate \"hello8World\" as state2\nstate \"hello8-world\" as state3\n\nstate1 --> state2: toCamel\nstate2 --> state3: toKebab\nstate3 --> state2: toCamel\n\ncenter footer It's impossible to go back to original string when a number is used as a word\n@enduml\n"
  },
  {
    "path": "docs/images/railroad.js",
    "content": "// https://tabatkins.github.io/railroad-diagrams/generator.html\nDiagram(\n  Choice(0,\n    Sequence(\n      Optional('->forceSimpleCaseMapping()', 'skip'),\n      Choice(7,\n        '->fromAda()',\n        '->fromAuto()',\n        '->fromCamel()',\n        '->fromCobol()',\n        '->fromDot()',\n        '->fromKebab()',\n        '->fromLower()',\n        Skip(),\n        '->fromMacro()',\n        '->fromPascal()',\n        '->fromSentence()',\n        '->fromSnake()',\n        '->fromTitle()',\n        '->fromTrain()',\n        '->fromUpper()'\n      ),\n      Choice(7,\n        '->toAda()',\n        '->toArray()',\n        '->toCamel()',\n        '->toCobol()',\n        '->toDot()',\n        '->toKebab()',\n        '->toLower()',\n        '->toMacro()',\n        '->toPascal()',\n        '->toSentence()',\n        '->toSnake()',\n        '->toTitle()',\n        '->toTrain()',\n        '->toUpper()'\n      )\n    ),\n    '->getSource()'\n  )\n);\n"
  },
  {
    "path": "docs/images/uml-case-converter.puml",
    "content": "@startuml\nnamespace Jawira.CaseConverter  {\n\texception CaseConverterException {\n\t}\n\tinterface CaseConverterInterface << interface >> {\n\t\t+convert()\n\t}\n\tclass CaseConverter << class >> {\n\t\t+convert()\n\t}\n\tclass Convert << class >> {\n\t\t#source\n\t\t#words\n\t\t#forceSimpleCaseMapping\n\t\t--\n\t\t+__construct()\n\t\t+fromAuto()\n\t\t#analyse()\n\t\t#contains()\n\t\t#isUppercaseWord()\n\t\t#extractWords()\n\t\t+getSource()\n\t\t+__call()\n\t\t#handleSplitterMethod()\n\t\t#createSplitter()\n\t\t#handleGluerMethod()\n\t\t#createGluer()\n\t\t+toArray()\n\t\t+forceSimpleCaseMapping()\n\t}\n}\n\texception Exception {\n\t}\nException <|-- Jawira.CaseConverter.CaseConverterException\nJawira.CaseConverter.CaseConverterInterface <|.. Jawira.CaseConverter.CaseConverter\n@enduml\n\n"
  },
  {
    "path": "docs/images/uml-glue.puml",
    "content": "@startuml\nnamespace Jawira.CaseConverter.Glue  {\n\tclass TitleCase << class >> {\n\t\t+glue()\n\t}\n\tabstract SpaceGluer << abstract >> {\n\t\t+DELIMITER\n\t}\n\tabstract Gluer << abstract >> {\n\t\t+ENCODING\n\t\t..\n\t\t#words\n\t\t#lowerCase\n\t\t#upperCase\n\t\t#titleCase\n\t\t--\n\t\t+__construct()\n\t\t#setSimpleCaseMappingConstants()\n\t\t+<i>glue</i>()\n\t\t#glueUsingRules()\n\t\t#changeWordsCase()\n\t\t#changeFirstWordCase()\n\t}\n\tabstract DashGluer << abstract >> {\n\t\t+DELIMITER\n\t}\n\tclass CamelCase << class >> {\n\t\t+glue()\n\t}\n\tabstract UppercaseGluer << abstract >> {\n\t\t+DELIMITER\n\t}\n\tclass AdaCase << class >> {\n\t\t+glue()\n\t}\n\tabstract UnderscoreGluer << abstract >> {\n\t\t+DELIMITER\n\t}\n\tclass SnakeCase << class >> {\n\t\t+glue()\n\t}\n\tclass KebabCase << class >> {\n\t\t+glue()\n\t}\n\tclass CobolCase << class >> {\n\t\t+glue()\n\t}\n\tclass DotNotation << class >> {\n\t\t+DELIMITER\n\t\t--\n\t\t+glue()\n\t}\n\tclass UpperCase << class >> {\n\t\t+glue()\n\t}\n\tclass TrainCase << class >> {\n\t\t+glue()\n\t}\n\tclass SentenceCase << class >> {\n\t\t+glue()\n\t}\n\tclass PascalCase << class >> {\n\t\t+glue()\n\t}\n\tclass MacroCase << class >> {\n\t\t+glue()\n\t}\n\tclass LowerCase << class >> {\n\t\t+glue()\n\t}\n}\nJawira.CaseConverter.Glue.SpaceGluer --|> Jawira.CaseConverter.Glue.Gluer\nJawira.CaseConverter.Glue.TitleCase --|> Jawira.CaseConverter.Glue.SpaceGluer\nJawira.CaseConverter.Glue.DashGluer --|> Jawira.CaseConverter.Glue.Gluer\nJawira.CaseConverter.Glue.UppercaseGluer --|> Jawira.CaseConverter.Glue.Gluer\nJawira.CaseConverter.Glue.CamelCase --|> Jawira.CaseConverter.Glue.UppercaseGluer\nJawira.CaseConverter.Glue.UnderscoreGluer --|> Jawira.CaseConverter.Glue.Gluer\nJawira.CaseConverter.Glue.AdaCase --|> Jawira.CaseConverter.Glue.UnderscoreGluer\nJawira.CaseConverter.Glue.SnakeCase --|> Jawira.CaseConverter.Glue.UnderscoreGluer\nJawira.CaseConverter.Glue.KebabCase --|> Jawira.CaseConverter.Glue.DashGluer\nJawira.CaseConverter.Glue.CobolCase --|> Jawira.CaseConverter.Glue.DashGluer\nJawira.CaseConverter.Glue.DotNotation --|> Jawira.CaseConverter.Glue.Gluer\nJawira.CaseConverter.Glue.UpperCase --|> Jawira.CaseConverter.Glue.SpaceGluer\nJawira.CaseConverter.Glue.TrainCase --|> Jawira.CaseConverter.Glue.DashGluer\nJawira.CaseConverter.Glue.SentenceCase --|> Jawira.CaseConverter.Glue.SpaceGluer\nJawira.CaseConverter.Glue.PascalCase --|> Jawira.CaseConverter.Glue.UppercaseGluer\nJawira.CaseConverter.Glue.MacroCase --|> Jawira.CaseConverter.Glue.UnderscoreGluer\nJawira.CaseConverter.Glue.LowerCase --|> Jawira.CaseConverter.Glue.SpaceGluer\n@enduml\n\n"
  },
  {
    "path": "docs/images/uml-split.puml",
    "content": "@startuml\nnamespace Jawira.CaseConverter.Split  {\n\tabstract Splitter << abstract >> {\n\t\t#inputString\n\t\t--\n\t\t+__construct()\n\t\t+<i>split</i>()\n\t\t#splitUsingPattern()\n\t}\n\tclass SpaceSplitter << class >> {\n\t\t+PATTERN\n\t\t--\n\t\t+split()\n\t}\n\tclass DotSplitter << class >> {\n\t\t+PATTERN\n\t\t--\n\t\t+split()\n\t}\n\tclass UppercaseSplitter << class >> {\n\t\t+PATTERN\n\t\t--\n\t\t+split()\n\t}\n\tclass UnderscoreSplitter << class >> {\n\t\t+PATTERN\n\t\t--\n\t\t+split()\n\t}\n\tclass DashSplitter << class >> {\n\t\t+PATTERN\n\t\t--\n\t\t+split()\n\t}\n}\nJawira.CaseConverter.Split.SpaceSplitter --|> Jawira.CaseConverter.Split.Splitter\nJawira.CaseConverter.Split.DotSplitter --|> Jawira.CaseConverter.Split.Splitter\nJawira.CaseConverter.Split.UppercaseSplitter --|> Jawira.CaseConverter.Split.Splitter\nJawira.CaseConverter.Split.UnderscoreSplitter --|> Jawira.CaseConverter.Split.Splitter\nJawira.CaseConverter.Split.DashSplitter --|> Jawira.CaseConverter.Split.Splitter\n@enduml\n\n"
  },
  {
    "path": "docs/index.md",
    "content": "Case Converter\n==============\n\nTo use **Case Converter** you have to instantiate `Convert` class, to do so you\ncan use the `new` keyword or the [CaseConverter factory] class.\n\nThe string you want to convert should be passed at instantiation. This cannot be\nchanged later since `Convert` class is immutable.\n\n```php\n$var = new Convert('string-to-convert');\n```\n\nTypically, you are going to call `Convert` methods this way:\n\n![Method call](./images/railroad.png)\n\nBasic usage\n-----------\n\nCode:\n\n```php\nuse Jawira\\CaseConverter\\Convert;\n\n$robot = new Convert('The-Terminator');\n\necho $robot->toPascal(); // TheTerminator\necho $robot->toCobol();  // THE-TERMINATOR\necho $robot->toSnake();  // the_terminator\n```\n\nExplicit case detection\n-----------------------\n\nIn some edge cases you have to explicitly set the format of input string to have\nthe desired output:\n\n```php\nuse Jawira\\CaseConverter\\Convert;\n\n$agency = new Convert('FBI');\n\n$agency->fromAda();\necho $agency->toCobol();   // FBI\necho $agency->toSnake();   // fbi\n\n$agency->fromCamel();\necho $agency->toCobol();   // F-B-I\necho $agency->toSnake();   // f_b_i\n\n$agency->fromAuto();\necho $agency->toCobol();   // FBI\necho $agency->toSnake();   // fbi\n```\n\nForce _Simple Case-Mapping_\n---------------------------\n\nYou can still use _Simple Case-Mapping_ even if you are using PHP 7.3 or newer:\n\n```php\n<?php declare(strict_types=1);\n\nrequire __DIR__ . '/vendor/autoload.php';\n\nuse Jawira\\CaseConverter\\Convert;\n\n$robot = new Convert('Straße');\n\n$robot->forceSimpleCaseMapping();\necho $robot->toMacro();     // output: STRAßE\n```\n\n[Learn more about Case-Mapping][Case-Mapping].\n\nUsing the factory\n-----------------\n\n[CaseConverter factory] is going to instantiate `Convert` class for you.\n\nIn the following code `$this->cc` is an instance of\n`\\Jawira\\CaseConverter\\CaseConverter` and implements\n`\\Jawira\\CaseConverter\\CaseConverterInterface`. This is useful because the\nfactory should be instantiated by the _Dependency Injection_ mechanism provided\nby your favorite framework.\n\n```php\n// Convert string to Pascal case\n$this->cc->convert('XML')->toPascal(); // Xml\n\n// Convert string to Snake case\n$this->cc->convert('v3.0.2')->toSnake(); // v3_0_2\n\n// Convert string to Camel case\n$this->cc->convert('first-name')->toCamel(); // firstName\n\n// Convert from Lower case to Dot case\n$this->cc->convert('non-SI units')->fromLower()->toDot(); // non-si.units\n\n// Get detected words\n$this->cc->convert('Mario Bros')->toArray(); // ['Mario', 'Bros']\n\n// Retrieve original string\n$this->cc->convert('use_the_force')->getSource(); // use_the_force\n```\n\nMore about [CaseConverter factory].\n\n[Case-Mapping]: ./case-mapping.md\n\n[CaseConverter factory]: ./using-the-factory.md\n"
  },
  {
    "path": "docs/known-issues.md",
    "content": "Known issues\n============\n\nNumber is not a word\n--------------------\n\nWhen using `case-converter` you cannot use a number as separator.\nIn practice this means a number is always identified as a lower case letter.\n\n![Phing targets](./images/number-problem.png \"Phing targets\")\n\nAs shown in the previous example, there is no way to go back to the original input string (i.e. `hello-8-world`), in _\nkebab case_ this sting is written as\n`hello8-world`.\n\nOther examples (with automatic case detection):\n\n```php\n(new Convert('REEL2REAL'))->toDot(); // r.e.e.l2.r.e.a.l\n(new Convert('reel2real'))->toDot(); // reel2real\n(new Convert('Reel2Real'))->toDot(); // reel2.real\n```\n\nI strongly suggest to avoid numbers in strings to be converted.\n\nMixed conventions\n-----------------\n\nIt might be evident, but _Case Converter_ cannot handle string with mixed naming conventions.\n\nFor example the string `XMLHttpRequest` will result in `x.m.l.http.request` when using _dot notation_:\n\n```php\n$xhr = new Convert('XMLHttpRequest');\n$xhr->toDot() // x.m.l.http.request\n```\n\nThe correct way to name this string (for Case Converter) should have been `XmlHttpRequest`.\n\nAnother example of mixed conventions:\n\n| ❌ Wrong         | ✅ Good           |\n|-----------------|------------------|\n| PHP_CodeSniffer | php_code_sniffer |\n| PHP_CodeSniffer | PhpCodeSniffer   |\n| PHPUnit         | PhpUnit          |\n| PHPUnit         | php-unit         |\n"
  },
  {
    "path": "docs/naming-conventions.md",
    "content": "Naming conventions\n==================\n\nNaming conventions are also referred as:\n\n- Naming style\n- Case convention\n\nReference matrix\n----------------\n\n|                    | Lower case   | Title case                | Upper case | Sentence case |\n|--------------------|--------------|---------------------------|------------|---------------|\n| `_` (underscore)   | Snake case   | Ada case                  | Macro case | -             |\n| `-` (dash)         | Kebab case   | Train case                | Cobol case | -             |\n| `.` (dot)          | Dot notation | -                         | -          | -             |\n| `␣` (space)        | Lower case   | Title case                | Upper case | Sentence case |\n| ` ` (empty string) | -            | Camel case<br>Pascal case | -          | -             |\n\nDescription table (wip)\n-----------------\n\n| Convention name | Description                                                 | Usage            | AKA                                           |\n|-----------------|-------------------------------------------------------------|------------------|-----------------------------------------------|\n| Camel case      |                                                             | PHP method names | Camel caps, Medial capitals, Lower camel case |\n| Pascal case     |                                                             | PHP class names  | Upper camel case                              |\n| Snake case      | All letters in lower-case separated with a `_` (underscore) | PHP functions    |                                               |\n| Ada case        |                                                             |                  |                                               |\n| Macro case      |                                                             | PHP constants    |                                               |\n| Kebab case      |                                                             |                  |                                               |\n| Train case      |                                                             |                  |                                               |\n| Cobol case      |                                                             |                  |                                               |\n| Dot notation    |                                                             |                  |                                               |\n| Lower case      |                                                             |                  |                                               |\n| Title case      |                                                             |                  |                                               |\n| Upper case      |                                                             |                  |                                               |\n| Sentence case   |                                                             |                  |                                               |\n\nResources\n---------\n\n- <https://dev.to/prahladyeri/underscores-camelcasing-and-pascalcasing-the-three-naming-conventions-every-programmer-should-be-aware-of-3aed>\n- <https://en.wikipedia.org/wiki/Letter_case>\n- <https://en.wikipedia.org/wiki/Naming_convention_(programming)>\n"
  },
  {
    "path": "docs/using-the-factory.md",
    "content": "Using the factory\n=================\n\nBesides `\\Jawira\\CaseConverter\\Convert` you also have at your disposal:\n\n- `\\Jawira\\CaseConverter\\CaseConverter`\n- `\\Jawira\\CaseConverter\\CaseConverterInterface`\n\nInstead of using `new Convert();` you can use the convenience method from\n`CaseConverter` class.\n\nIn concrete, you have to call `\\Jawira\\CaseConverter\\CaseConverter::convert` to\ncreate `Convert` objects.\n\nHere an example:\n\n```php\n<?php\nnamespace App;\n\nuse Jawira\\CaseConverter\\CaseConverterInterface;\n\nclass MySuperNameCreator\n{\n    protected $cc;\n\n    public function __construct(CaseConverterInterface $cc)\n    {\n        $this->cc = $cc;\n    }\n\n    public function variableName(string $slug): string\n    {\n        // `->convert()` returns a `Convert` object.\n        $myConvert = $this->cc->convert($slug);\n        return $myConvert->toCamel();\n    }\n\n    public function constantName(string $slug): string\n    {\n        // Of course you can also chain everything.\n        return $this->cc->convert($slug)->fromKebab()->toMacro();\n    }\n}\n```\n\nPlease note that an interface -`CaseConverterInterface`- is also provided. If\nyou are using _Symfony_ you can use this interface with [Symfony autowiring][]\nto automatically instantiate `CaseConverter`, otherwise if you are working in a\nstandalone project you should try [php-di project][].\n\nUsing `\\Jawira\\CaseConverter\\CaseConverter::convert` is preferred because:\n\n- Usually the `new` operator is considered harmful.\n- You can easily mock dependencies when writing tests.\n- [It's SOLID]\n\n[It's SOLID]: https://github.com/jawira/case-converter/issues/40\n\n[php-di project]: http://php-di.org/#autowiring\n\n[Symfony autowiring]: https://symfony.com/doc/current/service_container/autowiring.html\n"
  },
  {
    "path": "mkdocs.yml",
    "content": "site_name: !ENV [SITE_NAME, 'Case Converter']\nsite_author: 'Jawira Portugal'\nsite_url: 'https://jawira.github.io/case-converter/'\ncopyright: 'The MIT License (MIT)'\ntheme:\n  name: 'material'\n  highlightjs: true\n  titles_only: true\n  prev_next_buttons_location: 'both'\n  collapse_navigation: false\nuse_directory_urls: false\nstrict: true\nrepo_url: 'https://github.com/jawira/case-converter/'\nedit_uri: ''\nmarkdown_extensions:\n  - codehilite\nextra:\n  disqus: case-converter\n"
  },
  {
    "path": "src/CaseConverter.php",
    "content": "<?php declare(strict_types=1);\n\nnamespace Jawira\\CaseConverter;\n\n/**\n * Class CaseConverter\n *\n * Factory class which returns a Convert object.\n *\n * @author  Jawira Portugal <dev@tugal.be>\n */\nclass CaseConverter implements CaseConverterInterface\n{\n    /**\n     * Returns a Convert object\n     *\n     * @param string $source Input string to be converted\n     *\n     * @return \\Jawira\\CaseConverter\\Convert\n     * @throws \\Jawira\\CaseConverter\\CaseConverterException\n     */\n    public function convert(string $source): Convert\n    {\n        return new Convert($source);\n    }\n}\n"
  },
  {
    "path": "src/CaseConverterException.php",
    "content": "<?php\n\nnamespace Jawira\\CaseConverter;\n\nuse RuntimeException;\n\nclass CaseConverterException extends RuntimeException\n{\n}\n"
  },
  {
    "path": "src/CaseConverterInterface.php",
    "content": "<?php declare(strict_types=1);\n\nnamespace Jawira\\CaseConverter;\n\n/**\n * Interface CaseConverterInterface\n *\n * @author  Jawira Portugal <dev@tugal.be>\n */\ninterface CaseConverterInterface\n{\n    public function convert(string $source): Convert;\n}\n"
  },
  {
    "path": "src/Convert.php",
    "content": "<?php declare(strict_types=1);\n\nnamespace Jawira\\CaseConverter;\n\nuse Jawira\\CaseConverter\\Glue\\AdaCase;\nuse Jawira\\CaseConverter\\Glue\\CamelCase;\nuse Jawira\\CaseConverter\\Glue\\CobolCase;\nuse Jawira\\CaseConverter\\Glue\\DashGluer;\nuse Jawira\\CaseConverter\\Glue\\DotNotation;\nuse Jawira\\CaseConverter\\Glue\\Gluer;\nuse Jawira\\CaseConverter\\Glue\\KebabCase;\nuse Jawira\\CaseConverter\\Glue\\LowerCase;\nuse Jawira\\CaseConverter\\Glue\\MacroCase;\nuse Jawira\\CaseConverter\\Glue\\PascalCase;\nuse Jawira\\CaseConverter\\Glue\\SentenceCase;\nuse Jawira\\CaseConverter\\Glue\\SnakeCase;\nuse Jawira\\CaseConverter\\Glue\\SpaceGluer;\nuse Jawira\\CaseConverter\\Glue\\TitleCase;\nuse Jawira\\CaseConverter\\Glue\\TrainCase;\nuse Jawira\\CaseConverter\\Glue\\UnderscoreGluer;\nuse Jawira\\CaseConverter\\Glue\\UpperCase;\nuse Jawira\\CaseConverter\\Split\\DashSplitter;\nuse Jawira\\CaseConverter\\Split\\DotSplitter;\nuse Jawira\\CaseConverter\\Split\\SpaceSplitter;\nuse Jawira\\CaseConverter\\Split\\Splitter;\nuse Jawira\\CaseConverter\\Split\\UnderscoreSplitter;\nuse Jawira\\CaseConverter\\Split\\UppercaseSplitter;\nuse function is_subclass_of;\nuse function preg_match;\n\n/**\n * Convert string between different naming conventions.\n *\n * Handled formats:\n *\n * - Ada case\n * - Camel case\n * - Cobol case\n * - Kebab case\n * - Lower case\n * - Macro case\n * - Pascal case\n * - Sentence case\n * - Snake case\n * - Title case\n * - Train case\n * - Upper case\n *\n * @method self fromAda() Treat input string as _Ada case_\n * @method self fromCamel() Treat input string as _Camel case_\n * @method self fromCobol() Treat input string as _Cobol case_\n * @method self fromDot() Treat input string as _Dot notation_\n * @method self fromKebab() Treat input string as _Kebab case_\n * @method self fromLower() Treat input string as _Lower case_\n * @method self fromMacro() Treat input string as _Macro case_\n * @method self fromPascal() Treat input string as _Pascal case_\n * @method self fromSentence() Treat input string as _Sentence case_\n * @method self fromSnake() Treat input string as _Snake case_\n * @method self fromTitle() Treat input string as _Title case_\n * @method self fromTrain() Treat input string as _Train case_\n * @method self fromUpper() Treat input string as _Upper case_\n *\n * @method string toAda() Return string in _Ada case_ format\n * @method string toCamel() Return string in _Camel case_ format\n * @method string toCobol() Return string in _Cobol case_ format\n * @method string toDot() Return string in _Dot notation_\n * @method string toKebab() Return string in _Kebab case_ format\n * @method string toLower() Return string in _Lower case_ format\n * @method string toMacro() Return string in _Macro case_ format\n * @method string toPascal() Return string in _Pascal case_ format\n * @method string toSentence() Return string in _Sentence case_ format\n * @method string toSnake() Return string in _Snake case_ format\n * @method string toTitle() Return string in _Title case_ format\n * @method string toTrain() Return string in _Train case_ format\n * @method string toUpper() Return string in _Upper case_ format\n *\n * @see     https://softwareengineering.stackexchange.com/questions/322413/bothered-by-an-unknown-letter-case-name\n * @see     http://www.unicode.org/charts/case/\n * @author  Jawira Portugal <dev@tugal.be>\n */\nclass Convert\n{\n    /** @var string Input string to convert */\n    protected string $source;\n\n    /** @var string[] Words extracted from input string */\n    protected array $words;\n\n    protected bool $forceSimpleCaseMapping;\n\n    /**\n     * Constructor method\n     *\n     * @param string $source String to convert\n     *\n     * @throws \\Jawira\\CaseConverter\\CaseConverterException\n     */\n    public function __construct(string $source)\n    {\n        $this->source                 = $source;\n        $this->forceSimpleCaseMapping = false;\n        $this->fromAuto();\n    }\n\n    /**\n     * Handle `to*` methods and `from*` methods\n     *\n     * @param string  $methodName\n     * @param mixed[] $arguments\n     *\n     * @return string|\\Jawira\\CaseConverter\\Convert\n     * @throws \\Jawira\\CaseConverter\\CaseConverterException\n     */\n    public function __call(string $methodName, array $arguments)\n    {\n        $strStartsWith = static fn(string $haystack, string $needle): bool => 0 === mb_strpos($haystack, $needle);\n        if ($strStartsWith($methodName, 'from')) {\n            $result = $this->handleSplitterMethod($methodName);\n        } elseif ($strStartsWith($methodName, 'to')) {\n            $result = $this->handleGluerMethod($methodName);\n        } else {\n            throw new CaseConverterException(\"Unknown method: $methodName\");\n        }\n\n        return $result;\n    }\n\n    /**\n     * Auto-detect naming convention.\n     *\n     * This is the default method when you don't call a \"from*\" method.\n     *\n     * @param bool $digitsAreLowercase When `true`, digits in input string are considered to be equivalent to a lowercase character. When `false` digits are considered to be uppercase.\n     * @return \\Jawira\\CaseConverter\\Convert\n     */\n    public function fromAuto(bool $digitsAreLowercase = true): self\n    {\n        $splitter = $this->analyse($this->source, $digitsAreLowercase);\n        $this->extractWords($splitter);\n\n        return $this;\n    }\n\n    /**\n     * Returns original input string\n     *\n     * @return string Original input string\n     */\n    public function getSource(): string\n    {\n        return $this->source;\n    }\n\n    /**\n     * Detected words extracted from original string.\n     *\n     * @return string[]\n     */\n    public function toArray(): array\n    {\n        return $this->words;\n    }\n\n    /**\n     * Forces to use Simple Case-Mapping\n     *\n     * Call this method if you want to maintain the behaviour before PHP 7.3\n     *\n     * @see https://unicode.org/faq/casemap_charprop.html\n     * @return \\Jawira\\CaseConverter\\Convert\n     */\n    public function forceSimpleCaseMapping(): self\n    {\n        $this->forceSimpleCaseMapping = true;\n\n        return $this;\n    }\n\n    /**\n     * Detects word separator of $input string and tells you what strategy you should use.\n     *\n     * @param string $input              String to be analysed\n     * @param bool   $digitsAreLowercase When `true`, digits in input string are considered to be equivalent to a lowercase character. When `false` digits are considered to be uppercase.\n     *\n     * @return \\Jawira\\CaseConverter\\Split\\Splitter\n     * @throws \\Jawira\\CaseConverter\\CaseConverterException\n     */\n    protected function analyse(string $input, bool $digitsAreLowercase): Splitter\n    {\n        $strContains = static fn(string $input, string $needle): bool => is_int(mb_strpos($input, $needle));\n\n        switch (true) {\n            case $strContains($input, UnderscoreGluer::DELIMITER):\n                $splittingStrategy = new UnderscoreSplitter($input);\n                break;\n            case $strContains($input, DashGluer::DELIMITER):\n                $splittingStrategy = new DashSplitter($input);\n                break;\n            case $strContains($input, SpaceGluer::DELIMITER):\n                $splittingStrategy = new SpaceSplitter($input);\n                break;\n            case $strContains($input, DotNotation::DELIMITER):\n                $splittingStrategy = new DotSplitter($input);\n                break;\n            case $this->isUppercaseWord($input, $digitsAreLowercase):\n                $splittingStrategy = new UnderscoreSplitter($input);\n                break;\n            default:\n                $splittingStrategy = new UppercaseSplitter($input);\n                break;\n        }\n\n        return $splittingStrategy;\n    }\n\n    /**\n     * Returns true if $input string is a single word composed only by uppercase characters.\n     *\n     * ```\n     * isUppercaseWord('BRUSSELS'); // true\n     * isUppercaseWord('Brussels'); // false\n     * ```\n     *\n     * @see     https://www.regular-expressions.info/unicode.html#category\n     *\n     * @param string $input String to be tested.\n     *\n     * @return bool\n     * @throws \\Jawira\\CaseConverter\\CaseConverterException\n     */\n    protected function isUppercaseWord(string $input, bool $digitsAreLowercase): bool\n    {\n        $pattern = $digitsAreLowercase ? '#^\\p{Lu}+$#u' : '#^[\\p{Lu}\\p{Nd}]+$#u';\n        $match   = preg_match($pattern, $input);\n\n        if (false === $match) {\n            throw new CaseConverterException('Error executing regex'); // @codeCoverageIgnore\n        }\n\n        return 1 === $match;\n    }\n\n    /**\n     * Main function, receives input string and then it stores extracted words into an array.\n     *\n     * @param \\Jawira\\CaseConverter\\Split\\Splitter $splitter\n     *\n     * @return $this\n     */\n    protected function extractWords(Splitter $splitter): self\n    {\n        $this->words = $splitter->split();\n\n        return $this;\n    }\n\n    /**\n     * Methods to explicitly define naming conventions for input string\n     *\n     * @param string $methodName\n     *\n     * @return $this\n     * @throws \\Jawira\\CaseConverter\\CaseConverterException\n     */\n    protected function handleSplitterMethod(string $methodName): self\n    {\n        switch ($methodName) {\n            case 'fromCamel':\n            case 'fromPascal':\n                $splitterName = UppercaseSplitter::class;\n                break;\n            case 'fromSnake':\n            case 'fromAda':\n            case 'fromMacro':\n                $splitterName = UnderscoreSplitter::class;\n                break;\n            case 'fromKebab':\n            case 'fromTrain':\n            case 'fromCobol':\n                $splitterName = DashSplitter::class;\n                break;\n            case 'fromLower':\n            case 'fromUpper':\n            case 'fromTitle':\n            case 'fromSentence':\n                $splitterName = SpaceSplitter::class;\n                break;\n            case 'fromDot':\n                $splitterName = DotSplitter::class;\n                break;\n            default:\n                throw new CaseConverterException(\"Unknown method: $methodName\");\n        }\n\n        $splitter = $this->createSplitter($splitterName, $this->source);\n        $this->extractWords($splitter);\n\n        return $this;\n    }\n\n    /**\n     * @param string $className Class name in string format\n     * @param string $source    Input string to be split\n     *\n     * @return \\Jawira\\CaseConverter\\Split\\Splitter\n     */\n    protected function createSplitter(string $className, string $source): Splitter\n    {\n        assert(is_subclass_of($className, Splitter::class));\n\n        return new $className($source);\n    }\n\n    /**\n     * Handles all methods starting by `to*`\n     *\n     * @param string $methodName\n     *\n     * @return string\n     * @throws \\Jawira\\CaseConverter\\CaseConverterException\n     */\n    protected function handleGluerMethod(string $methodName): string\n    {\n\n        switch ($methodName) {\n            case 'toAda':\n                $className = AdaCase::class;\n                break;\n            case 'toCamel':\n                $className = CamelCase::class;\n                break;\n            case 'toCobol':\n                $className = CobolCase::class;\n                break;\n            case 'toKebab':\n                $className = KebabCase::class;\n                break;\n            case 'toLower':\n                $className = LowerCase::class;\n                break;\n            case 'toMacro':\n                $className = MacroCase::class;\n                break;\n            case 'toPascal':\n                $className = PascalCase::class;\n                break;\n            case 'toSentence':\n                $className = SentenceCase::class;\n                break;\n            case 'toSnake':\n                $className = SnakeCase::class;\n                break;\n            case 'toTitle':\n                $className = TitleCase::class;\n                break;\n            case 'toTrain':\n                $className = TrainCase::class;\n                break;\n            case 'toUpper':\n                $className = UpperCase::class;\n                break;\n            case 'toDot':\n                $className = DotNotation::class;\n                break;\n            default:\n                throw new CaseConverterException(\"Unknown method: $methodName\");\n        }\n\n        $gluer = $this->createGluer($className, $this->words, $this->forceSimpleCaseMapping);\n\n        return $gluer->glue();\n    }\n\n    /**\n     * @param string   $className              Class name in string format\n     * @param string[] $words                  Words to glue\n     * @param bool     $forceSimpleCaseMapping Should _Simple Case-Mapping_ be forced?\n     *\n     * @return \\Jawira\\CaseConverter\\Glue\\Gluer\n     */\n    protected function createGluer(string $className, array $words, bool $forceSimpleCaseMapping): Gluer\n    {\n        assert(is_subclass_of($className, Gluer::class));\n\n        return new $className($words, $forceSimpleCaseMapping);\n    }\n}\n"
  },
  {
    "path": "src/Glue/AdaCase.php",
    "content": "<?php declare(strict_types=1);\n\nnamespace Jawira\\CaseConverter\\Glue;\n\n/**\n * Class AdaCase\n *\n * Outputs string in _Ada case_ format: This_Is_Ada_Case\n */\nclass AdaCase extends UnderscoreGluer\n{\n    /**\n     * Format detected words in _Ada case_\n     *\n     * @return string\n     */\n    public function glue(): string\n    {\n        return $this->glueUsingRules(self::DELIMITER, $this->titleCase);\n    }\n}\n"
  },
  {
    "path": "src/Glue/CamelCase.php",
    "content": "<?php declare(strict_types=1);\n\nnamespace Jawira\\CaseConverter\\Glue;\n\n/**\n * Class CamelCase\n *\n * Outputs string in _Camel case_ format: thisIsCamelCase\n */\nclass CamelCase extends UppercaseGluer\n{\n    /**\n     * Format detected words in _Camel case_\n     *\n     * @return string\n     */\n    public function glue(): string\n    {\n        return $this->glueUsingRules(self::DELIMITER, $this->titleCase, $this->lowerCase);\n    }\n}\n"
  },
  {
    "path": "src/Glue/CobolCase.php",
    "content": "<?php declare(strict_types=1);\n\nnamespace Jawira\\CaseConverter\\Glue;\n\n/**\n * Class CobolCase\n *\n * Outputs string in _Cobol case_ format: THIS-IS-COBOL-CASE\n */\nclass CobolCase extends DashGluer\n{\n    /**\n     * Format detected words in _Cobol case_\n     *\n     * @return string\n     */\n    public function glue(): string\n    {\n        return $this->glueUsingRules(self::DELIMITER, $this->upperCase);\n    }\n}\n"
  },
  {
    "path": "src/Glue/DashGluer.php",
    "content": "<?php declare(strict_types=1);\n\nnamespace Jawira\\CaseConverter\\Glue;\n\nabstract class DashGluer extends Gluer\n{\n    /** @internal */\n    public const DELIMITER = '-';\n}\n"
  },
  {
    "path": "src/Glue/DotNotation.php",
    "content": "<?php declare(strict_types=1);\n\nnamespace Jawira\\CaseConverter\\Glue;\n\n/**\n * Class DotNotation\n */\nclass DotNotation extends Gluer\n{\n    /** @internal */\n    public const DELIMITER = '.';\n\n    /**\n     * Format detected words in _dot notation_\n     *\n     * @return string\n     */\n    public function glue(): string\n    {\n        return $this->glueUsingRules(self::DELIMITER, $this->lowerCase);\n    }\n}\n"
  },
  {
    "path": "src/Glue/Gluer.php",
    "content": "<?php declare(strict_types=1);\n\nnamespace Jawira\\CaseConverter\\Glue;\n\nuse function array_map;\nuse function implode;\nuse function mb_convert_case;\nuse const MB_CASE_LOWER;\nuse const MB_CASE_TITLE;\nuse const MB_CASE_UPPER;\n\n/**\n * Class Gluer\n *\n * A Gluer subclass allow to export an array of words in a single string\n *\n * @author Jawira Portugal <dev@tugal.be>\n */\nabstract class Gluer\n{\n    /**\n     * Encoding to be used by `mb_convert_case()` function.\n     *\n     * This value should never change.\n     */\n    protected const ENCODING = 'UTF-8';\n\n    /**\n     * @var string[] Words extracted from input string\n     */\n    protected array $words;\n\n    /**\n     * @var int MB_CASE_LOWER or MB_CASE_LOWER_SIMPLE\n     */\n    protected int $lowerCase;\n\n    /**\n     * @var int MB_CASE_UPPER or MB_CASE_UPPER_SIMPLE\n     */\n    protected int $upperCase;\n\n    /**\n     * @var int MB_CASE_TITLE or MB_CASE_TITLE_SIMPLE\n     */\n    protected int $titleCase;\n\n\n    /**\n     * Gluer constructor.\n     *\n     * @param string[] $words\n     * @param bool     $forceSimpleCaseMapping\n     */\n    final public function __construct(array $words, bool $forceSimpleCaseMapping)\n    {\n        $this->words     = $words;\n        $this->lowerCase = $forceSimpleCaseMapping ? MB_CASE_LOWER_SIMPLE : MB_CASE_LOWER;\n        $this->upperCase = $forceSimpleCaseMapping ? MB_CASE_UPPER_SIMPLE : MB_CASE_UPPER;\n        $this->titleCase = $forceSimpleCaseMapping ? MB_CASE_TITLE_SIMPLE : MB_CASE_TITLE;\n    }\n\n    /**\n     * Creates a string which respects concrete naming convention.\n     *\n     * @return string\n     */\n    abstract public function glue(): string;\n\n    /**\n     * Implode self::$words array using $glue.\n     *\n     * @param string   $glue          Character to glue words. Even if is assumed you are using underscore or dash character, this method should be capable to use any character as glue.\n     * @param int      $wordsMode     The mode of the conversion. It should be one of `Gluer::$lowerCase`, `Gluer::$upperCase` or  `Gluer::$titleCase`.\n     * @param null|int $firstWordMode Sometimes first word requires special treatment. It should be one of `Gluer::$lowerCase`,  `Gluer::$upperCase` or  `Gluer::$titleCase`.\n     *\n     * @return string Converted words.\n     */\n    protected function glueUsingRules(string $glue, int $wordsMode, ?int $firstWordMode = null): string\n    {\n        $convertedWords = $this->changeWordsCase($this->words, $wordsMode);\n\n        if (is_int($firstWordMode)) {\n            $convertedWords = $this->changeFirstWordCase($convertedWords, $firstWordMode);\n        }\n\n        return implode($glue, $convertedWords);\n    }\n\n    /**\n     * Changes the case of every $words element\n     *\n     * @param string[] $words    Words to modify\n     * @param int      $caseMode It should be one of `Gluer::$lowerCase`,  `Gluer::$upperCase` or  `Gluer::$titleCase`.\n     *\n     * @return string[]\n     */\n    protected function changeWordsCase(array $words, int $caseMode): array\n    {\n        if (empty($words)) {\n            return $words;\n        }\n\n        $convertCase = static fn(string $word): string => mb_convert_case($word, $caseMode, self::ENCODING);\n\n        return array_map($convertCase, $words);\n    }\n\n    /**\n     * Changes the case of first $words element\n     *\n     * @param string[] $words    Words to modify\n     * @param int      $caseMode It should be one of `Gluer::$lowerCase`,  `Gluer::$upperCase` or  `Gluer::$titleCase`.\n     *\n     * @return string[]\n     */\n    protected function changeFirstWordCase(array $words, int $caseMode): array\n    {\n        if (empty($words)) {\n            return $words;\n        }\n\n        $words[0] = mb_convert_case($words[0], $caseMode, self::ENCODING);\n\n        return $words;\n    }\n\n}\n"
  },
  {
    "path": "src/Glue/KebabCase.php",
    "content": "<?php declare(strict_types=1);\n\nnamespace Jawira\\CaseConverter\\Glue;\n\n/**\n * Class KebabCase\n *\n * Outputs string in _Cobol case_ format: this-is-kebab-case\n */\nclass KebabCase extends DashGluer\n{\n    /**\n     * Format detected words in _Kebab case_\n     *\n     * @return string\n     */\n    public function glue(): string\n    {\n        return $this->glueUsingRules(self::DELIMITER, $this->lowerCase);\n    }\n}\n"
  },
  {
    "path": "src/Glue/LowerCase.php",
    "content": "<?php declare(strict_types=1);\n\nnamespace Jawira\\CaseConverter\\Glue;\n\nclass LowerCase extends SpaceGluer\n{\n    public function glue(): string\n    {\n        return $this->glueUsingRules(self::DELIMITER, $this->lowerCase);\n    }\n}\n"
  },
  {
    "path": "src/Glue/MacroCase.php",
    "content": "<?php declare(strict_types=1);\n\nnamespace Jawira\\CaseConverter\\Glue;\n\nclass MacroCase extends UnderscoreGluer\n{\n    public function glue(): string\n    {\n        return $this->glueUsingRules(self::DELIMITER, $this->upperCase);\n    }\n}\n"
  },
  {
    "path": "src/Glue/PascalCase.php",
    "content": "<?php declare(strict_types=1);\n\nnamespace Jawira\\CaseConverter\\Glue;\n\nclass PascalCase extends UppercaseGluer\n{\n    public function glue(): string\n    {\n        return $this->glueUsingRules(self::DELIMITER, $this->titleCase);\n    }\n}\n"
  },
  {
    "path": "src/Glue/SentenceCase.php",
    "content": "<?php declare(strict_types=1);\n\nnamespace Jawira\\CaseConverter\\Glue;\n\nclass SentenceCase extends SpaceGluer\n{\n    public function glue(): string\n    {\n        return $this->glueUsingRules(self::DELIMITER, $this->lowerCase, $this->titleCase);\n    }\n}\n"
  },
  {
    "path": "src/Glue/SnakeCase.php",
    "content": "<?php declare(strict_types=1);\n\nnamespace Jawira\\CaseConverter\\Glue;\n\nclass SnakeCase extends UnderscoreGluer\n{\n    public function glue(): string\n    {\n        return $this->glueUsingRules(self::DELIMITER, $this->lowerCase);\n    }\n}\n"
  },
  {
    "path": "src/Glue/SpaceGluer.php",
    "content": "<?php declare(strict_types=1);\n\nnamespace Jawira\\CaseConverter\\Glue;\n\nabstract class SpaceGluer extends Gluer\n{\n    /** @internal */\n    public const DELIMITER = ' ';\n}\n"
  },
  {
    "path": "src/Glue/TitleCase.php",
    "content": "<?php declare(strict_types=1);\n\nnamespace Jawira\\CaseConverter\\Glue;\n\nclass TitleCase extends SpaceGluer\n{\n    public function glue(): string\n    {\n        return $this->glueUsingRules(self::DELIMITER, $this->titleCase);\n    }\n}\n"
  },
  {
    "path": "src/Glue/TrainCase.php",
    "content": "<?php declare(strict_types=1);\n\nnamespace Jawira\\CaseConverter\\Glue;\n\nclass TrainCase extends DashGluer\n{\n    public function glue(): string\n    {\n        return $this->glueUsingRules(self::DELIMITER, $this->titleCase);\n    }\n}\n"
  },
  {
    "path": "src/Glue/UnderscoreGluer.php",
    "content": "<?php declare(strict_types=1);\n\nnamespace Jawira\\CaseConverter\\Glue;\n\nabstract class UnderscoreGluer extends Gluer\n{\n    /** @internal */\n    public const DELIMITER = '_';\n}\n"
  },
  {
    "path": "src/Glue/UpperCase.php",
    "content": "<?php declare(strict_types=1);\n\nnamespace Jawira\\CaseConverter\\Glue;\n\nclass UpperCase extends SpaceGluer\n{\n    public function glue(): string\n    {\n        return $this->glueUsingRules(self::DELIMITER, $this->upperCase);\n    }\n}\n"
  },
  {
    "path": "src/Glue/UppercaseGluer.php",
    "content": "<?php declare(strict_types=1);\n\nnamespace Jawira\\CaseConverter\\Glue;\n\nabstract class UppercaseGluer extends Gluer\n{\n    /** @internal */\n    public const DELIMITER = '';\n}\n"
  },
  {
    "path": "src/Split/DashSplitter.php",
    "content": "<?php declare(strict_types=1);\n\nnamespace Jawira\\CaseConverter\\Split;\n\nuse Jawira\\CaseConverter\\Glue\\DashGluer;\n\nclass DashSplitter extends Splitter\n{\n    /** @internal */\n    public const PATTERN = '#' . DashGluer::DELIMITER . '+#u';\n\n    /**\n     * @return string[]\n     * @throws \\Jawira\\CaseConverter\\CaseConverterException\n     */\n    public function split(): array\n    {\n        return $this->splitUsingPattern($this->inputString, self::PATTERN);\n    }\n}\n"
  },
  {
    "path": "src/Split/DotSplitter.php",
    "content": "<?php declare(strict_types=1);\n\nnamespace Jawira\\CaseConverter\\Split;\n\nuse Jawira\\CaseConverter\\Glue\\DotNotation;\n\nclass DotSplitter extends Splitter\n{\n    /** @internal */\n    public const PATTERN = '#\\\\' . DotNotation::DELIMITER . '+#u';\n\n    /**\n     * @return string[]\n     * @throws \\Jawira\\CaseConverter\\CaseConverterException\n     */\n    public function split(): array\n    {\n        return $this->splitUsingPattern($this->inputString, self::PATTERN);\n    }\n}\n"
  },
  {
    "path": "src/Split/SpaceSplitter.php",
    "content": "<?php declare(strict_types=1);\n\nnamespace Jawira\\CaseConverter\\Split;\n\nuse Jawira\\CaseConverter\\Glue\\SpaceGluer;\n\nclass SpaceSplitter extends Splitter\n{\n    /** @internal */\n    public const PATTERN = '#' . SpaceGluer::DELIMITER . '+#u';\n\n    /**\n     * @return string[]\n     * @throws \\Jawira\\CaseConverter\\CaseConverterException\n     */\n    public function split(): array\n    {\n        return $this->splitUsingPattern($this->inputString, self::PATTERN);\n    }\n}\n"
  },
  {
    "path": "src/Split/Splitter.php",
    "content": "<?php declare(strict_types=1);\n\nnamespace Jawira\\CaseConverter\\Split;\n\nuse Jawira\\CaseConverter\\CaseConverterException;\n\n/**\n * Class Splitter\n *\n * A Splitter subclass allows to read the words contained in a string\n *\n * @author  Jawira Portugal <dev@tugal.be>\n */\nabstract class Splitter\n{\n    /**\n     * @var string Words extracted from input string\n     */\n    protected string $inputString;\n\n    final public function __construct(string $inputString)\n    {\n        $this->inputString = $inputString;\n    }\n\n    /**\n     * Tells how to split a string into valid words.\n     *\n     * @return string[]\n     */\n    abstract public function split(): array;\n\n    /**\n     * This is a utility method, typically this method is used by to split a string based on pattern.\n     *\n     * @param string $inputString\n     * @param string $pattern\n     *\n     * @return string[]\n     * @throws \\Jawira\\CaseConverter\\CaseConverterException\n     */\n    protected function splitUsingPattern(string $inputString, string $pattern): array\n    {\n        if (empty($pattern)) {\n            throw new CaseConverterException('Pattern must not be empty.'); // @codeCoverageIgnore\n        }\n\n        $words = preg_split($pattern, $inputString, 0, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);\n\n        if ($words === false) {\n            throw new CaseConverterException(\"Error while processing $this->inputString\"); // @codeCoverageIgnore\n        }\n\n        return $words;\n    }\n}\n"
  },
  {
    "path": "src/Split/UnderscoreSplitter.php",
    "content": "<?php declare(strict_types=1);\n\nnamespace Jawira\\CaseConverter\\Split;\n\nuse Jawira\\CaseConverter\\Glue\\UnderscoreGluer;\n\nclass UnderscoreSplitter extends Splitter\n{\n    public const PATTERN = '#' . UnderscoreGluer::DELIMITER . '+#u';\n\n    /**\n     * @return string[]\n     * @throws \\Jawira\\CaseConverter\\CaseConverterException\n     */\n    public function split(): array\n    {\n        return $this->splitUsingPattern($this->inputString, self::PATTERN);\n    }\n}\n"
  },
  {
    "path": "src/Split/UppercaseSplitter.php",
    "content": "<?php declare(strict_types=1);\n\nnamespace Jawira\\CaseConverter\\Split;\n\nclass UppercaseSplitter extends Splitter\n{\n    // language=PhpRegExp\n    public const PATTERN = '#(?=\\p{Lu}{1})#u';\n\n    /**\n     * Splits $words using Uppercase letters.\n     *\n     * @see https://www.regular-expressions.info/unicode.html#category\n     * @return string[] Words in $input\n     * @throws \\Jawira\\CaseConverter\\CaseConverterException\n     */\n    public function split(): array\n    {\n        return $this->splitUsingPattern($this->inputString, self::PATTERN);\n    }\n}\n"
  },
  {
    "path": "tests/behat/bootstrap/FeatureContext.php",
    "content": "<?php\n\nuse Behat\\Behat\\Context\\Context;\nuse Behat\\Behat\\Tester\\Exception\\PendingException;\nuse Jawira\\CaseConverter\\Convert;\n\n/**\n * Defines application features from the specific context.\n */\nclass FeatureContext implements Context\n{\n    /**\n     * @var string|array\n     */\n    protected $result;\n\n    /**\n     * @var \\Jawira\\CaseConverter\\Convert\n     */\n    protected $instance;\n\n    /**\n     * @var int Result of count()\n     */\n    protected $count;\n\n    /**\n     * @Given /^CaseConverter class is instantiated with \"([^\"]*)\"$/\n     * @param string $arg1 String to convert\n     *\n     * @throws \\Jawira\\CaseConverter\\CaseConverterException\n     */\n    public function caseConverterClassIsInstantiatedWith($arg1)\n    {\n        $this->instance = new Convert($arg1);\n    }\n\n    /**\n     * @When I cast object to string\n     */\n    public function iCastObjectToString()\n    {\n        $this->result = (string)($this->instance);\n    }\n\n    /**\n     * @When I call :methodName\n     *\n     * @param $methodName\n     */\n    public function iCall($methodName)\n    {\n        $this->result = ($this->instance)->$methodName();\n    }\n\n    /**\n     * @Then method should return string :returnString\n     *\n     * @param string $returnString Expected string\n     *\n     * @throws \\Exception\n     */\n    public function methodShouldReturnString($returnString)\n    {\n        if (!is_string($returnString)) {\n            throw new Exception('Result is not a string');\n        }\n\n        if ($this->result !== $returnString) {\n            $message = sprintf('Result \"%s\" is not equal to expected string \"%s\"', $this->result, $returnString);\n            throw new Exception($message);\n        }\n    }\n\n    /**\n     * @Then method should return array :returnArray\n     *\n     * @param array $returnArray\n     *\n     * @throws \\Exception\n     */\n    public function methodShouldReturnArray($returnArray)\n    {\n        if (!is_array($returnArray)) {\n            throw new Exception('Result is not array');\n        }\n\n        if ($this->result !== $returnArray) {\n            throw new Exception('Result is not the expected array');\n        }\n    }\n\n    /**\n     * Convert string to array.\n     *\n     * Array format is `[One;Two;Three]`.\n     *\n     * @Transform /^(\\[.*\\])$/\n     *\n     * @see https://behat.readthedocs.io/en/v2.5/guides/2.definitions.html#step-argument-transformations\n     *\n     * @param string $string The string to convert to array\n     *\n     * @return array\n     */\n    public function transformStringToArray($string): array\n    {\n        $trimmed = trim($string, '[]');\n        if ($trimmed === false) {\n            $trimmed = '';\n        }\n\n        $exploded = explode(';', $trimmed);\n\n        // Filtering since CaseConverter does the same.\n        return array_filter($exploded);\n    }\n\n    /**\n     * @When I use count function\n     */\n    public function iUseCountFunction()\n    {\n        $this->count = count($this->instance);\n    }\n\n    /**\n     * @Then functions should return :expectedCount\n     *\n     * @param $expectedCount\n     *\n     * @throws \\Exception\n     */\n    public function functionsShouldReturn($expectedCount)\n    {\n        if (!is_numeric($expectedCount)) {\n            throw new Exception('Count value is not numeric');\n        }\n\n        $expectedCount = (int)$expectedCount;\n\n        if ($this->count !== $expectedCount) {\n            throw new Exception('Invalid count');\n        }\n    }\n}\n"
  },
  {
    "path": "tests/behat/case-converter.feature",
    "content": "Feature: Convert Case\n  In order to change string case\n  Dev should be able to\n  change case\n\n\n  Scenario Outline: Change naming convention from string using explicit methods\n    Given CaseConverter class is instantiated with \"<input-string>\"\n    When I call \"<method>\"\n    Then method should return string \"<output-string>\"\n\n    Examples:\n      | method     | input-string         | output-string        |\n      | toCamel    |                      |                      |\n      | toCamel    | a                    | a                    |\n      | toCamel    | NASA                 | nasa                 |\n      | toCamel    | Fbi                  | fbi                  |\n      | toCamel    | B-C-D                | bCD                  |\n      | toCamel    | CamelCase            | camelCase            |\n      | toCamel    | dataTransfer         | dataTransfer         |\n      | toCamel    | eniac_computer       | eniacComputer        |\n      | toCamel    | FIBONACCI_NUMBER     | fibonacciNumber      |\n      | toCamel    | v5.3.0               | v530                 |\n      | toCamel    | Good_Morning_Vietnam | goodMorningVietnam   |\n      | toCamel    | Buenos Días          | buenosDías           |\n      | toCamel    | Jag_förstår_inte     | jagFörstårInte       |\n      | toCamel    | quicoYÑoño           | quicoYÑoño           |\n      | toCamel    | Πολύ-καλό            | πολύΚαλό             |\n      | toCamel    | ОЧЕНЬ_ПРИЯТНО        | оченьПриятно         |\n      | toCamel    | Ես-հայերեն-չգիտեմ    | եսՀայերենՉգիտեմ      |\n      | toPascal   |                      |                      |\n      | toPascal   | a                    | A                    |\n      | toPascal   | NASA                 | Nasa                 |\n      | toPascal   | Fbi                  | Fbi                  |\n      | toPascal   | B-C-D                | BCD                  |\n      | toPascal   | CamelCase            | CamelCase            |\n      | toPascal   | dataTransfer         | DataTransfer         |\n      | toPascal   | eniac_computer       | EniacComputer        |\n      | toPascal   | FIBONACCI_NUMBER     | FibonacciNumber      |\n      | toPascal   | v5.3.0               | V530                 |\n      | toPascal   | Good_Morning_Vietnam | GoodMorningVietnam   |\n      | toPascal   | Buenos Días          | BuenosDías           |\n      | toPascal   | Jag_förstår_inte     | JagFörstårInte       |\n      | toPascal   | quicoYÑoño           | QuicoYÑoño           |\n      | toPascal   | Πολύ-καλό            | ΠολύΚαλό             |\n      | toPascal   | ОЧЕНЬ_ПРИЯТНО        | ОченьПриятно         |\n      | toPascal   | Ես-հայերեն-չգիտեմ    | ԵսՀայերենՉգիտեմ      |\n      | toSnake    |                      |                      |\n      | toSnake    | a                    | a                    |\n      | toSnake    | NASA                 | nasa                 |\n      | toSnake    | Fbi                  | fbi                  |\n      | toSnake    | B-C-D                | b_c_d                |\n      | toSnake    | CamelCase            | camel_case           |\n      | toSnake    | dataTransfer         | data_transfer        |\n      | toSnake    | eniac_computer       | eniac_computer       |\n      | toSnake    | FIBONACCI_NUMBER     | fibonacci_number     |\n      | toSnake    | v5.3.0               | v5_3_0               |\n      | toSnake    | Good_Morning_Vietnam | good_morning_vietnam |\n      | toSnake    | Good_Morning_Vietnam | good_morning_vietnam |\n      | toSnake    | Buenos Días          | buenos_días          |\n      | toSnake    | quicoYÑoño           | quico_y_ñoño         |\n      | toSnake    | Πολύ-καλό            | πολύ_καλό            |\n      | toSnake    | ОЧЕНЬ_ПРИЯТНО        | очень_приятно        |\n      | toSnake    | Ես-հայերեն-չգիտեմ    | ես_հայերեն_չգիտեմ    |\n      | toMacro    |                      |                      |\n      | toMacro    | a                    | A                    |\n      | toMacro    | NASA                 | NASA                 |\n      | toMacro    | Fbi                  | FBI                  |\n      | toMacro    | B-C-D                | B_C_D                |\n      | toMacro    | CamelCase            | CAMEL_CASE           |\n      | toMacro    | dataTransfer         | DATA_TRANSFER        |\n      | toMacro    | eniac_computer       | ENIAC_COMPUTER       |\n      | toMacro    | FIBONACCI_NUMBER     | FIBONACCI_NUMBER     |\n      | toMacro    | v5.3.0               | V5_3_0               |\n      | toMacro    | Good_Morning_Vietnam | GOOD_MORNING_VIETNAM |\n      | toMacro    | Buenos Días          | BUENOS_DÍAS          |\n      | toMacro    | Jag_förstår_inte     | JAG_FÖRSTÅR_INTE     |\n      | toMacro    | quicoYÑoño           | QUICO_Y_ÑOÑO         |\n      | toMacro    | Πολύ-καλό            | ΠΟΛΎ_ΚΑΛΌ            |\n      | toMacro    | ОЧЕНЬ_ПРИЯТНО        | ОЧЕНЬ_ПРИЯТНО        |\n      | toMacro    | Ես-հայերեն-չգիտեմ    | ԵՍ_ՀԱՅԵՐԵՆ_ՉԳԻՏԵՄ    |\n      | toAda      |                      |                      |\n      | toAda      | a                    | A                    |\n      | toAda      | NASA                 | Nasa                 |\n      | toAda      | Fbi                  | Fbi                  |\n      | toAda      | B-C-D                | B_C_D                |\n      | toAda      | CamelCase            | Camel_Case           |\n      | toAda      | dataTransfer         | Data_Transfer        |\n      | toAda      | eniac_computer       | Eniac_Computer       |\n      | toAda      | FIBONACCI_NUMBER     | Fibonacci_Number     |\n      | toAda      | v5.3.0               | V5_3_0               |\n      | toAda      | Good_Morning_Vietnam | Good_Morning_Vietnam |\n      | toAda      | Buenos Días          | Buenos_Días          |\n      | toAda      | Jag_förstår_inte     | Jag_Förstår_Inte     |\n      | toAda      | quicoYÑoño           | Quico_Y_Ñoño         |\n      | toAda      | Πολύ-καλό            | Πολύ_Καλό            |\n      | toAda      | ОЧЕНЬ_ПРИЯТНО        | Очень_Приятно        |\n      | toAda      | Ես-հայերեն-չգիտեմ    | Ես_Հայերեն_Չգիտեմ    |\n      | toKebab    |                      |                      |\n      | toKebab    | a                    | a                    |\n      | toKebab    | NASA                 | nasa                 |\n      | toKebab    | Fbi                  | fbi                  |\n      | toKebab    | B-C-D                | b-c-d                |\n      | toKebab    | CamelCase            | camel-case           |\n      | toKebab    | dataTransfer         | data-transfer        |\n      | toKebab    | eniac_computer       | eniac-computer       |\n      | toKebab    | FIBONACCI_NUMBER     | fibonacci-number     |\n      | toKebab    | v5.3.0               | v5-3-0               |\n      | toKebab    | Good_Morning_Vietnam | good-morning-vietnam |\n      | toKebab    | Buenos Días          | buenos-días          |\n      | toKebab    | Jag_förstår_inte     | jag-förstår-inte     |\n      | toKebab    | quicoYÑoño           | quico-y-ñoño         |\n      | toKebab    | Πολύ-καλό            | πολύ-καλό            |\n      | toKebab    | ОЧЕНЬ_ПРИЯТНО        | очень-приятно        |\n      | toKebab    | Ես-հայերեն-չգիտեմ    | ես-հայերեն-չգիտեմ    |\n      | toCobol    |                      |                      |\n      | toCobol    | a                    | A                    |\n      | toCobol    | NASA                 | NASA                 |\n      | toCobol    | Fbi                  | FBI                  |\n      | toCobol    | B-C-D                | B-C-D                |\n      | toCobol    | CamelCase            | CAMEL-CASE           |\n      | toCobol    | dataTransfer         | DATA-TRANSFER        |\n      | toCobol    | eniac_computer       | ENIAC-COMPUTER       |\n      | toCobol    | FIBONACCI_NUMBER     | FIBONACCI-NUMBER     |\n      | toCobol    | v5.3.0               | V5-3-0               |\n      | toCobol    | Good_Morning_Vietnam | GOOD-MORNING-VIETNAM |\n      | toCobol    | Buenos Días          | BUENOS-DÍAS          |\n      | toCobol    | Jag_förstår_inte     | JAG-FÖRSTÅR-INTE     |\n      | toCobol    | quicoYÑoño           | QUICO-Y-ÑOÑO         |\n      | toCobol    | Πολύ-καλό            | ΠΟΛΎ-ΚΑΛΌ            |\n      | toCobol    | ОЧЕНЬ_ПРИЯТНО        | ОЧЕНЬ-ПРИЯТНО        |\n      | toCobol    | Ես-հայերեն-չգիտեմ    | ԵՍ-ՀԱՅԵՐԵՆ-ՉԳԻՏԵՄ    |\n      | toTrain    |                      |                      |\n      | toTrain    | a                    | A                    |\n      | toTrain    | NASA                 | Nasa                 |\n      | toTrain    | Fbi                  | Fbi                  |\n      | toTrain    | B-C-D                | B-C-D                |\n      | toTrain    | CamelCase            | Camel-Case           |\n      | toTrain    | dataTransfer         | Data-Transfer        |\n      | toTrain    | eniac_computer       | Eniac-Computer       |\n      | toTrain    | FIBONACCI_NUMBER     | Fibonacci-Number     |\n      | toTrain    | v5.3.0               | V5-3-0               |\n      | toTrain    | Good_Morning_Vietnam | Good-Morning-Vietnam |\n      | toTrain    | Buenos Días          | Buenos-Días          |\n      | toTrain    | Jag_förstår_inte     | Jag-Förstår-Inte     |\n      | toTrain    | quicoYÑoño           | Quico-Y-Ñoño         |\n      | toTrain    | Πολύ-καλό            | Πολύ-Καλό            |\n      | toTrain    | ОЧЕНЬ_ПРИЯТНО        | Очень-Приятно        |\n      | toTrain    | Ես-հայերեն-չգիտեմ    | Ես-Հայերեն-Չգիտեմ    |\n      | toLower    |                      |                      |\n      | toLower    | a                    | a                    |\n      | toLower    | NASA                 | nasa                 |\n      | toLower    | Fbi                  | fbi                  |\n      | toLower    | B-C-D                | b c d                |\n      | toLower    | CamelCase            | camel case           |\n      | toLower    | dataTransfer         | data transfer        |\n      | toLower    | eniac_computer       | eniac computer       |\n      | toLower    | FIBONACCI_NUMBER     | fibonacci number     |\n      | toLower    | v5.3.0               | v5 3 0               |\n      | toLower    | Good_Morning_Vietnam | good morning vietnam |\n      | toLower    | Buenos Días          | buenos días          |\n      | toLower    | Jag_förstår_inte     | jag förstår inte     |\n      | toLower    | quicoYÑoño           | quico y ñoño         |\n      | toLower    | Πολύ-καλό            | πολύ καλό            |\n      | toLower    | ОЧЕНЬ_ПРИЯТНО        | очень приятно        |\n      | toLower    | Ես-հայերեն-չգիտեմ    | ես հայերեն չգիտեմ    |\n      | toUpper    |                      |                      |\n      | toUpper    | a                    | A                    |\n      | toUpper    | NASA                 | NASA                 |\n      | toUpper    | Fbi                  | FBI                  |\n      | toUpper    | B-C-D                | B C D                |\n      | toUpper    | CamelCase            | CAMEL CASE           |\n      | toUpper    | dataTransfer         | DATA TRANSFER        |\n      | toUpper    | eniac_computer       | ENIAC COMPUTER       |\n      | toUpper    | v5.3.0               | V5 3 0               |\n      | toUpper    | FIBONACCI_NUMBER     | FIBONACCI NUMBER     |\n      | toUpper    | Good_Morning_Vietnam | GOOD MORNING VIETNAM |\n      | toUpper    | Buenos Días          | BUENOS DÍAS          |\n      | toUpper    | Jag_förstår_inte     | JAG FÖRSTÅR INTE     |\n      | toUpper    | quicoYÑoño           | QUICO Y ÑOÑO         |\n      | toUpper    | Πολύ-καλό            | ΠΟΛΎ ΚΑΛΌ            |\n      | toUpper    | ОЧЕНЬ_ПРИЯТНО        | ОЧЕНЬ ПРИЯТНО        |\n      | toUpper    | Ես-հայերեն-չգիտեմ    | ԵՍ ՀԱՅԵՐԵՆ ՉԳԻՏԵՄ    |\n      | toTitle    |                      |                      |\n      | toTitle    | a                    | A                    |\n      | toTitle    | NASA                 | Nasa                 |\n      | toTitle    | Fbi                  | Fbi                  |\n      | toTitle    | B-C-D                | B C D                |\n      | toTitle    | CamelCase            | Camel Case           |\n      | toTitle    | dataTransfer         | Data Transfer        |\n      | toTitle    | eniac_computer       | Eniac Computer       |\n      | toTitle    | FIBONACCI_NUMBER     | Fibonacci Number     |\n      | toTitle    | v5.3.0               | V5 3 0               |\n      | toTitle    | Good_Morning_Vietnam | Good Morning Vietnam |\n      | toTitle    | Buenos Días          | Buenos Días          |\n      | toTitle    | Jag_förstår_inte     | Jag Förstår Inte     |\n      | toTitle    | quicoYÑoño           | Quico Y Ñoño         |\n      | toTitle    | Πολύ-καλό            | Πολύ Καλό            |\n      | toTitle    | ОЧЕНЬ_ПРИЯТНО        | Очень Приятно        |\n      | toTitle    | Ես-հայերեն-չգիտեմ    | Ես Հայերեն Չգիտեմ    |\n      | toSentence |                      |                      |\n      | toSentence | a                    | A                    |\n      | toSentence | NASA                 | Nasa                 |\n      | toSentence | Fbi                  | Fbi                  |\n      | toSentence | B-C-D                | B c d                |\n      | toSentence | CamelCase            | Camel case           |\n      | toSentence | dataTransfer         | Data transfer        |\n      | toSentence | eniac_computer       | Eniac computer       |\n      | toSentence | FIBONACCI_NUMBER     | Fibonacci number     |\n      | toSentence | v5.3.0               | V5 3 0               |\n      | toSentence | Good_Morning_Vietnam | Good morning vietnam |\n      | toSentence | Buenos Días          | Buenos días          |\n      | toSentence | Jag_förstår_inte     | Jag förstår inte     |\n      | toSentence | quicoYÑoño           | Quico y ñoño         |\n      | toSentence | Πολύ-καλό            | Πολύ καλό            |\n      | toSentence | ОЧЕНЬ_ПРИЯТНО        | Очень приятно        |\n      | toSentence | Ես-հայերեն-չգիտեմ    | Ես հայերեն չգիտեմ    |\n      | toDot      |                      |                      |\n      | toDot      | a                    | a                    |\n      | toDot      | NASA                 | nasa                 |\n      | toDot      | Fbi                  | fbi                  |\n      | toDot      | B-C-D                | b.c.d                |\n      | toDot      | CamelCase            | camel.case           |\n      | toDot      | dataTransfer         | data.transfer        |\n      | toDot      | eniac_computer       | eniac.computer       |\n      | toDot      | FIBONACCI_NUMBER     | fibonacci.number     |\n      | toDot      | Good_Morning_Vietnam | good.morning.vietnam |\n      | toDot      | Buenos Días          | buenos.días          |\n      | toDot      | Jag_förstår_inte     | jag.förstår.inte     |\n      | toDot      | quicoYÑoño           | quico.y.ñoño         |\n      | toDot      | Πολύ-καλό            | πολύ.καλό            |\n      | toDot      | ОЧЕНЬ_ПРИЯТНО        | очень.приятно        |\n      | toDot      | Ես-հայերեն-չգիտեմ    | ես.հայերեն.չգիտեմ    |\n      | toDot      | XMLHttpRequest       | x.m.l.http.request   |\n      | toLower    | ĲSJE                 | ĳsje                 |\n      | toUpper    | ĳsje                 | ĲSJE                 |\n\n\n  Scenario Outline: Convert a string to array\n    Given CaseConverter class is instantiated with \"<input-string>\"\n    When I call \"<method>\"\n    Then method should return array \"<output-string>\"\n\n    Examples:\n      | method  | input-string    | output-string     |\n      | toArray |                 | []                |\n      | toArray | a               | [a]               |\n      | toArray | HugoPacoLuis    | [Hugo;Paco;Luis]  |\n      | toArray | loremIpsum      | [lorem;Ipsum]     |\n      | toArray | aBc_DeF_hIj_KlM | [aBc;DeF;hIj;KlM] |\n      | toArray | one___two___    | [one;two]         |\n      | toArray | Le Népal        | [Le;Népal]        |\n      | toArray | red.green.blue  | [red;green;blue]  |\n\n\n  Scenario Outline: Full case mapping (default behaviour)\n    Given CaseConverter class is instantiated with \"<input-string>\"\n    When I call \"<method>\"\n    Then method should return string \"<output-string>\"\n\n    Examples:\n      | method  | input-string | output-string |\n      | toUpper | Straße       | STRASSE       |\n      | toLower | İstanbul     | i̇stanbul     |\n      | toUpper | ŉ            | ʼN            |\n      | toUpper | ﬄ            | FFL           |\n\n\n  Scenario Outline: Simple case mapping\n    Given CaseConverter class is instantiated with \"<input-string>\"\n    When I call \"forceSimpleCaseMapping\"\n    And I call \"<method>\"\n    Then method should return string \"<output-string>\"\n\n    Examples:\n      | method  | input-string | output-string |\n      | toUpper | Straße       | STRAßE        |\n      | toLower | İstanbul     | istanbul      |\n      | toUpper | ŉ            | ŉ             |\n      | toUpper | ﬄ            | ﬄ             |\n\n\n  Scenario Outline: Using numbers in input strings\n    Given CaseConverter class is instantiated with \"<input-string>\"\n    When I call \"<method>\"\n    Then method should return string \"<output-string>\"\n\n    Examples:\n      | method     | input-string              | output-string          |\n      | toCamel    | I-have-99-problems        | iHave99Problems        |\n      | toPascal   | The Taking of Pelham 123  | TheTakingOfPelham123   |\n      | toSnake    | 3_idiots_2009             | 3_idiots_2009          |\n      | toMacro    | fantastic-4               | FANTASTIC_4            |\n      | toAda      | the6ThDay                 | The6_Th_Day            |\n      | toKebab    | 7samurai                  | 7samurai               |\n      | toCobol    | Super8                    | SUPER8                 |\n      | toTrain    | 8Mm                       | 8-Mm                   |\n      | toLower    | 8MM                       | 8 m m                  |\n      | toUpper    | DISTRICT_9                | DISTRICT 9             |\n      | toTitle    | session9                  | Session9               |\n      | toSentence | 9Songs                    | 9 songs                |\n      | toCamel    | STARTER-FOR-10            | starterFor10           |\n      | toPascal   | Ocean's 11                | Ocean's11              |\n      | toSnake    | 12_angry_men              | 12_angry_men           |\n      | toMacro    | Apollo13                  | APOLLO13               |\n      | toAda      | Friday-the-13th           | Friday_The_13Th        |\n      | toKebab    | 14BLADES                  | 14-b-l-a-d-e-s         |\n      | toCobol    | STALAG17-1953             | STALAG17-1953          |\n      | toTrain    | 21-JUMP-STREET            | 21-Jump-Street         |\n      | toLower    | TheNumber23               | the number23           |\n      | toUpper    | The 40-Year-Old Virgin    | THE 40 YEAR OLD VIRGIN |\n      | toTitle    | planet_51                 | Planet 51              |\n      | toSentence | Passenger 57              | Passenger 57           |\n      | toCamel    | 10-10-a-a-10-10           | 1010AA1010             |\n      | toPascal   | Hello5My5Name5Is5Bond     | Hello5My5Name5Is5Bond  |\n      | toSnake    | 48-HOLA-mundo-6           | 48_hola_mundo_6        |\n      | toMacro    | 0-0-0                     | 0_0_0                  |\n      | toAda      | Interstate 60             | Interstate_60          |\n      | toKebab    | Happy2-see-you            | happy2-see-you         |\n      | toCobol    | 123BC456BC789             | 123-B-C456-B-C789      |\n      | toTrain    | 21-test-test21-21Test     | 21-Test-Test21-21Test  |\n      | toLower    | TheNumber23               | the number23           |\n      | toUpper    | 88 Minutes                | 88 MINUTES             |\n      | toTitle    | United9                   | United9                |\n      | toSentence | 300                       | 300                    |\n      | toCamel    | the__0__is_the_best       | the0IsTheBest          |\n      | toPascal   | i-do--not--0like--number0 | IDoNot0LikeNumber0     |\n      | toSnake    | IDoNot0LikeNumber0        | i_do_not0_like_number0 |\n      | toMacro    | you-have-0-money          | YOU_HAVE_0_MONEY       |\n      | toDot      | se7en                     | se7en                  |\n      | toDot      | Red1Green2Blue3           | red1.green2.blue3      |\n      | toDot      | REEL2REAL                 | r.e.e.l2.r.e.a.l       |\n      | toDot      | reel2real                 | reel2real              |\n      | toDot      | Reel2Real                 | reel2.real             |\n\n\n  Scenario: Retrieving original string\n    Given CaseConverter class is instantiated with \"  A commissioned mirror swears.  \"\n    When I call \"getSource\"\n    Then method should return string \"  A commissioned mirror swears.  \"\n\n\n  Scenario Outline: Handling strings with mixed delimiters\n    Given CaseConverter class is instantiated with \"<input-string>\"\n    When I call \"<from-method>\"\n    And I call \"<to-method>\"\n    Then method should return string \"<output-string>\"\n\n    Examples:\n      | from-method | to-method | input-string        | output-string         |\n      | fromSnake   | toSnake   | C-3PO_and_R2-D2     | c-3po_and_r2-d2       |\n      | fromSnake   | toPascal  | C-3PO_and_R2-D2     | C-3PoAndR2-D2         |\n      | fromPascal  | toSnake   | C-3PoAndR2-D2       | c-3_po_and_r2-_d2     |\n      | fromUpper   | toDot     | non-SI units        | non-si.units          |\n      | fromCamel   | toDot     | EstosSon_losActores | estos.son_los.actores |\n\n\n  Scenario Outline: Manually set input string format (test from* methods)\n    Given CaseConverter class is instantiated with \"<input-string>\"\n    When I call \"<from-method>\"\n    And I call \"<to-method>\"\n    Then method should return string \"<output-string>\"\n\n    Examples:\n      | from-method | to-method | input-string | output-string |\n      | fromDot     | toSnake   | v5.0.2       | v5_0_2        |\n"
  },
  {
    "path": "tests/phpunit/AdaCaseTest.php",
    "content": "<?php\n\nuse Jawira\\CaseConverter\\Glue\\AdaCase;\nuse PHPUnit\\Framework\\TestCase;\n\n/**\n * Class AdaCaseTest\n */\nclass AdaCaseTest extends TestCase\n{\n    /**\n     * Testing that `glue` method is called and `titleCase` property is used\n     *\n     * @covers \\Jawira\\CaseConverter\\Glue\\AdaCase::glue\n     * @throws \\ReflectionException\n     */\n    public function testGlue()\n    {\n        // Disabling constructor with one stub method\n        $mock = $this->getMockBuilder(AdaCase::class)\n                     ->disableOriginalConstructor()\n                     ->setMethods(['glueUsingRules'])\n                     ->getMock();\n\n        // Setting proper titleCase property value\n        $reflectionObject   = new ReflectionObject($mock);\n        $titleCaseProperty = $reflectionObject->getProperty('titleCase');\n        $titleCaseProperty->setAccessible(true);\n        $titleCaseProperty->setValue($mock, 123);\n\n        // Configuring stub\n        $mock->expects($this->once())\n             ->method('glueUsingRules')\n             ->with(AdaCase::DELIMITER, 123)\n             ->willReturn('dummy-value-e1bfd');\n\n        /** @var \\Jawira\\CaseConverter\\Glue\\AdaCase $mock */\n        $returned = $mock->glue();\n        $this->assertSame('dummy-value-e1bfd', $returned, 'Returned value is not the expected');\n    }\n}\n"
  },
  {
    "path": "tests/phpunit/CamelCaseTest.php",
    "content": "<?php\n\nuse Jawira\\CaseConverter\\Glue\\CamelCase;\nuse PHPUnit\\Framework\\TestCase;\n\n/**\n * Class CamelCaseTest\n */\nclass CamelCaseTest extends TestCase\n{\n    /**\n     * Testing that `glue()` method is called and `titleCase` and `lowerCase`\n     * properties are being used.\n     *\n     * @covers \\Jawira\\CaseConverter\\Glue\\CamelCase::glue\n     * @throws \\ReflectionException\n     */\n    public function testGlue()\n    {\n        // Disabling constructor with one stub method\n        $mock = $this->getMockBuilder(CamelCase::class)\n                     ->disableOriginalConstructor()\n                     ->setMethods(['glueUsingRules'])\n                     ->getMock();\n\n        // Setting titleCase and lowerCase properties\n        $reflectionObject  = new ReflectionObject($mock);\n        $titleCaseProperty = $reflectionObject->getProperty('titleCase');\n        $titleCaseProperty->setAccessible(true);\n        $titleCaseProperty->setValue($mock, 123);\n        $lowerCaseProperty = $reflectionObject->getProperty('lowerCase');\n        $lowerCaseProperty->setAccessible(true);\n        $lowerCaseProperty->setValue($mock, 456);\n\n        // Configuring stub\n        $mock->expects($this->once())\n             ->method('glueUsingRules')\n             ->with(CamelCase::DELIMITER, 123, 456)\n             ->willReturn('dummy-value-32ea');\n\n        /** @var \\Jawira\\CaseConverter\\Glue\\CamelCase $mock */\n        $returned = $mock->glue();\n        $this->assertSame('dummy-value-32ea', $returned, 'Returned value is not the expected');\n    }\n}\n"
  },
  {
    "path": "tests/phpunit/CaseConverterTest.php",
    "content": "<?php\n\nuse Jawira\\CaseConverter\\CaseConverter;\nuse Jawira\\CaseConverter\\Convert;\nuse PHPUnit\\Framework\\TestCase;\n\nclass CaseConverterTest extends TestCase\n{\n    /**\n     * @covers \\Jawira\\CaseConverter\\CaseConverter::convert\n     *\n     * @covers \\Jawira\\CaseConverter\\Convert::__construct\n     * @covers \\Jawira\\CaseConverter\\Convert::analyse\n     * @covers \\Jawira\\CaseConverter\\Convert::extractWords\n     * @covers \\Jawira\\CaseConverter\\Convert::fromAuto\n     * @covers \\Jawira\\CaseConverter\\Convert::getSource\n     * @covers \\Jawira\\CaseConverter\\Split\\DashSplitter::split\n     * @covers \\Jawira\\CaseConverter\\Split\\Splitter::__construct\n     * @covers \\Jawira\\CaseConverter\\Split\\Splitter::splitUsingPattern\n     * @throws \\Jawira\\CaseConverter\\CaseConverterException\n     */\n    public function testConvert()\n    {\n        $cc = new CaseConverter();\n\n        $convertObject = $cc->convert('hello-world-484');\n\n        $this->assertInstanceOf(Convert::class, $convertObject);\n        $this->assertSame('hello-world-484', $convertObject->getSource());\n    }\n}\n"
  },
  {
    "path": "tests/phpunit/CobolCaseTest.php",
    "content": "<?php\n\nuse Jawira\\CaseConverter\\Glue\\CobolCase;\nuse PHPUnit\\Framework\\TestCase;\n\n/**\n * Class CobolCaseTest\n */\nclass CobolCaseTest extends TestCase\n{\n    /**\n     * Testing that `glue` method is called and `upperCase` property is used.\n     *\n     * @covers \\Jawira\\CaseConverter\\Glue\\CobolCase::glue\n     * @throws \\ReflectionException\n     */\n    public function testGlue()\n    {\n        // Disabling constructor with one stub method\n        $mock = $this->getMockBuilder(CobolCase::class)\n                     ->disableOriginalConstructor()\n                     ->setMethods(['glueUsingRules'])\n                     ->getMock();\n\n        // Setting `upperCase` property value\n        $reflectionObject  = new ReflectionObject($mock);\n        $titleCaseProperty = $reflectionObject->getProperty('upperCase');\n        $titleCaseProperty->setAccessible(true);\n        $titleCaseProperty->setValue($mock, 789);\n\n        // Configuring stub\n        $mock->expects($this->once())\n             ->method('glueUsingRules')\n             ->with(CobolCase::DELIMITER, 789)\n             ->willReturn('e1bfd762321e409cee4ac0b6e841963c');\n\n        /** @var \\Jawira\\CaseConverter\\CobolCase $mock */\n        $returned = $mock->glue();\n        $this->assertSame('e1bfd762321e409cee4ac0b6e841963c', $returned, 'Returned value is not the expected');\n    }\n}\n"
  },
  {
    "path": "tests/phpunit/ConvertTest.php",
    "content": "<?php\n\nuse Jawira\\CaseConverter\\CaseConverterException;\nuse Jawira\\CaseConverter\\Convert;\nuse Jawira\\CaseConverter\\Glue\\AdaCase;\nuse Jawira\\CaseConverter\\Glue\\CamelCase;\nuse Jawira\\CaseConverter\\Glue\\CobolCase;\nuse Jawira\\CaseConverter\\Glue\\DotNotation;\nuse Jawira\\CaseConverter\\Glue\\Gluer;\nuse Jawira\\CaseConverter\\Glue\\KebabCase;\nuse Jawira\\CaseConverter\\Glue\\LowerCase;\nuse Jawira\\CaseConverter\\Glue\\MacroCase;\nuse Jawira\\CaseConverter\\Glue\\PascalCase;\nuse Jawira\\CaseConverter\\Glue\\SentenceCase;\nuse Jawira\\CaseConverter\\Glue\\SnakeCase;\nuse Jawira\\CaseConverter\\Glue\\TitleCase;\nuse Jawira\\CaseConverter\\Glue\\TrainCase;\nuse Jawira\\CaseConverter\\Glue\\UpperCase;\nuse Jawira\\CaseConverter\\Split\\DashSplitter;\nuse Jawira\\CaseConverter\\Split\\DotSplitter;\nuse Jawira\\CaseConverter\\Split\\SpaceSplitter;\nuse Jawira\\CaseConverter\\Split\\Splitter;\nuse Jawira\\CaseConverter\\Split\\UnderscoreSplitter;\nuse Jawira\\CaseConverter\\Split\\UppercaseSplitter;\nuse PHPUnit\\Framework\\TestCase;\n\n/**\n * Unitary tests for \\Jawira\\CaseConverter\\Convert\n *\n * @see https://jtreminio.com/blog/unit-testing-tutorial-part-i-introduction-to-phpunit/\n */\nclass ConvertTest extends TestCase\n{\n\n    /**\n     * @covers       \\Jawira\\CaseConverter\\Convert::isUppercaseWord()\n     *\n     * @param string $inputString\n     * @param bool   $expectedResult\n     *\n     * @dataProvider isUppercaseWordProvider\n     *\n     * @throws \\ReflectionException\n     */\n    public function testIsUppercaseWord(string $inputString, bool $digitsAreLowercase, bool $expectedResult)\n    {\n        // Disabling constructor without stub methods\n        $stub = $this->getMockBuilder(Convert::class)\n                     ->disableOriginalConstructor()\n                     ->setMethods()\n                     ->getMock();\n\n        // Removing protected for analyze method\n        $reflection = new ReflectionObject($stub);\n        $method     = $reflection->getMethod('isUppercaseWord');\n        $method->setAccessible(true);\n\n        $output = $method->invoke($stub, $inputString, $digitsAreLowercase);\n\n        $this->assertSame($expectedResult, $output);\n    }\n\n    public function isUppercaseWordProvider()\n    {\n        return [\n            ['X', true, true],\n            ['YES', true, true],\n            ['HELLO', true, true],\n            ['', true, false],\n            ['x', true, false],\n            ['HELLOxWORLD', true, false],\n            ['HELLO-WORLD', true, false],\n            ['HELLO_WORLD', true, false],\n            ['HelloWorld', true, false],\n            ['CPU486', true, false],\n            ['CPU486', false, true],\n            ['IR35', true, false],\n            ['IR35', false, true],\n            ['ISO8601', true, false],\n            ['ISO8601', false, true],\n        ];\n    }\n\n    /**\n     * Testing \\Jawira\\CaseConverter\\Convert::analyse\n     *\n     * \\Jawira\\CaseConverter\\Convert::analyse should return Convert::SNAKE if $input contains '_'.\n     *\n     * @covers       \\Jawira\\CaseConverter\\Convert::analyse\n     * @covers       \\Jawira\\CaseConverter\\Split\\Splitter::__construct\n     *\n     * @dataProvider analyseProvider\n     *\n     * @param bool   $isUppercaseWordReturn Return value for `isUppercaseWord()`\n     * @param string $expected              Expected result\n     * @param string $inputString           Input string\n     *\n     * @throws \\ReflectionException\n     */\n    public function testAnalyse(bool $isUppercaseWordReturn, string $expected, string $inputString)\n    {\n        // Disabling constructor with one stub method\n        $stub = $this->getMockBuilder(Convert::class)\n                     ->disableOriginalConstructor()\n                     ->setMethods(['isUppercaseWord'])\n                     ->getMock();\n\n        // Configuring expectation\n        $stub->expects($this->any())\n             ->method('isUppercaseWord')\n             ->withAnyParameters()\n             ->willReturn($isUppercaseWordReturn);\n\n        // Removing protected for analyze method\n        $reflection = new ReflectionObject($stub);\n        $method     = $reflection->getMethod('analyse');\n        $method->setAccessible(true);\n\n        // Testing\n        $output = $method->invoke($stub, $inputString, true);\n        $this->assertInstanceOf($expected, $output);\n    }\n\n    public function analyseProvider()\n    {\n        return [\n            'Underscore 1' => [false, UnderscoreSplitter::class, 'hola_mundo'],\n            'Underscore 2' => [false, UnderscoreSplitter::class, 'HELLO_WORLD'],\n            'Underscore 3' => [true, UnderscoreSplitter::class, 'Ñ'],\n            'Underscore 4' => [true, UnderscoreSplitter::class, 'HELLO'],\n            'Underscore 5' => [false, UnderscoreSplitter::class, '_'],\n            'Underscore 6' => [false, UnderscoreSplitter::class, '_____'],\n            'Uppercase 1'  => [false, UppercaseSplitter::class, ''],\n            'Uppercase 2'  => [false, UppercaseSplitter::class, 'ñ'],\n            'Uppercase 3'  => [false, UppercaseSplitter::class, 'one'],\n            'Uppercase 4'  => [false, UppercaseSplitter::class, 'helloWorld'],\n            'Dash 1'       => [false, DashSplitter::class, 'hello-World'],\n            'Dash 2'       => [false, DashSplitter::class, 'my-name-is-bond'],\n            'Dash 3'       => [false, DashSplitter::class, '-my-name-is-bond-'],\n            'Dash 4'       => [false, DashSplitter::class, '-'],\n            'Dash 5'       => [false, DashSplitter::class, '------'],\n            'Space 1'      => [false, SpaceSplitter::class, 'Hola mundo'],\n            'Space 2'      => [false, SpaceSplitter::class, 'Mi nombre es bond'],\n            'Space 3'      => [false, SpaceSplitter::class, 'Formule courte spéciale été'],\n            'Space 4'      => [false, SpaceSplitter::class, ' '],\n            'Space 5'      => [false, SpaceSplitter::class, '      '],\n            'Dot 1'        => [false, DotSplitter::class, 'one.two'],\n            'Dot 2'        => [false, DotSplitter::class, '.hello.'],\n            'Dot 3'        => [false, DotSplitter::class, '.'],\n            'Dot 4'        => [false, DotSplitter::class, '........'],\n        ];\n    }\n\n    /**\n     * Test _converter methods_: _toCamel_, _toSnake_, ...\n     *\n     * This test do not force the use of _Simple Case-Mapping_ .\n     *\n     * @dataProvider handleGluerMethodProvider()\n     *\n     * @covers       \\Jawira\\CaseConverter\\Convert::handleGluerMethod\n     *\n     * @param string $methodName\n     * @param string $className\n     *\n     * @throws \\ReflectionException\n     */\n    public function testHandleGluerMethod(string $methodName, string $className)\n    {\n        // Gluer class\n        $gluerMock = $this->getMockBuilder($className)\n                          ->disableOriginalConstructor()\n                          ->setMethods(['glue'])\n                          ->getMock();\n        $gluerMock->expects($this->once())\n                  ->method('glue')\n                  ->willReturn('this is a dummy text');\n\n        // Convert class\n        $convertMock = $this->getMockBuilder(Convert::class)\n                            ->disableOriginalConstructor()\n                            ->setMethods(['createGluer'])\n                            ->getMock();\n        $convertMock->expects($this->once())\n                    ->method('createGluer')\n                    ->with($className, ['dummy', 'd455b'], false)\n                    ->willReturn($gluerMock);\n\n        // Setting detected words\n        $reflectionObject = new ReflectionObject($convertMock);\n        $wordsProperty    = $reflectionObject->getProperty('words');\n        $wordsProperty->setAccessible(true);\n        $wordsProperty->setValue($convertMock, ['dummy', 'd455b']);\n        // Setting _Simple Case-Mapping_ as false\n        $forceProperty = $reflectionObject->getProperty('forceSimpleCaseMapping');\n        $forceProperty->setAccessible(true);\n        $forceProperty->setValue($convertMock, false);\n\n        // Invoking protected method\n        $method = new ReflectionMethod($convertMock, 'handleGluerMethod');\n        $method->setAccessible(true);\n        $result = $method->invokeArgs($convertMock, [$methodName]);\n\n        $this->assertSame('this is a dummy text', $result);\n    }\n\n    /**\n     * Test _converter methods_: _toCamel_, _toSnake_, ...\n     *\n     * This test do not force the use of _Simple Case-Mapping_ .\n     *\n     * @covers       \\Jawira\\CaseConverter\\Convert::handleGluerMethod\n     *\n     * @throws \\ReflectionException\n     */\n    public function testHandleGluerMethodWithException()\n    {\n        // Preparing exception\n        $this->expectException(CaseConverterException::class);\n        $this->expectExceptionMessage('Unknown method: myDummyMethod');\n\n        // Convert class\n        $convertMock = $this->getMockBuilder(Convert::class)\n                            ->disableOriginalConstructor()\n                            ->setMethods()\n                            ->getMock();\n\n        // Invoking protected method\n        $method = new ReflectionMethod($convertMock, 'handleGluerMethod');\n        $method->setAccessible(true);\n        $method->invokeArgs($convertMock, ['myDummyMethod']);\n    }\n\n    /**\n     * Return and array with the name of all _converter methods_.\n     */\n    public function handleGluerMethodProvider()\n    {\n        return [\n            'toAda'      => ['toAda', AdaCase::class],\n            'toCamel'    => ['toCamel', CamelCase::class],\n            'toCobol'    => ['toCobol', CobolCase::class],\n            'toKebab'    => ['toKebab', KebabCase::class],\n            'toLower'    => ['toLower', LowerCase::class],\n            'toMacro'    => ['toMacro', MacroCase::class],\n            'toPascal'   => ['toPascal', PascalCase::class],\n            'toSentence' => ['toSentence', SentenceCase::class],\n            'toSnake'    => ['toSnake', SnakeCase::class],\n            'toTitle'    => ['toTitle', TitleCase::class],\n            'toTrain'    => ['toTrain', TrainCase::class],\n            'toUpper'    => ['toUpper', UpperCase::class],\n            'toDot'      => ['toDot', DotNotation::class],\n        ];\n    }\n\n    /**\n     * Returns all valid splitters that can be used by Convert::extractWords()\n     *\n     * @return array\n     */\n    public function extractWordsProviders()\n    {\n        return [\n            [SpaceSplitter::class],\n            [DashSplitter::class],\n            [UnderscoreSplitter::class],\n            [UppercaseSplitter::class],\n        ];\n    }\n\n    /**\n     * @covers \\Jawira\\CaseConverter\\Convert::toArray\n     *\n     * @throws \\ReflectionException\n     */\n    public function testToArray()\n    {\n        // Preparing Convert mock\n        $mock = $this->getMockBuilder(Convert::class)\n                     ->disableOriginalConstructor()\n                     ->setMethods()\n                     ->getMock();\n\n        // Setting value to protected property\n        $reflectionObject   = new ReflectionObject($mock);\n        $reflectionProperty = $reflectionObject->getProperty('words');\n        $reflectionProperty->setAccessible(true);\n        $reflectionProperty->setValue($mock, ['dummy', 'array']);\n\n        /** @var \\Jawira\\CaseConverter\\Convert $mock */\n        $currentArray = $mock->toArray();\n\n        $this->assertIsArray($currentArray);\n        $this->assertEquals(['dummy', 'array'], $currentArray);\n    }\n\n    /**\n     * @covers       \\Jawira\\CaseConverter\\Convert::createGluer\n     * @covers       \\Jawira\\CaseConverter\\Glue\\Gluer::__construct\n     * @dataProvider createGluerProvider\n     *\n     * @param string $className\n     *\n     * @throws \\ReflectionException\n     */\n    public function testCreateGluer(string $className)\n    {\n        // Preparing Convert mock\n        $mock = $this->getMockBuilder(Convert::class)\n                     ->disableOriginalConstructor()\n                     ->setMethods()\n                     ->getMock();\n\n        // Calling protected method\n        $reflectionObject = new ReflectionObject($mock);\n        $reflectionMethod = $reflectionObject->getMethod('createGluer');\n        $reflectionMethod->setAccessible(true);\n        $gluerObject = $reflectionMethod->invoke($mock, $className, ['dummy', 'array'], false);\n\n        $this->assertInstanceOf(Gluer::class, $gluerObject);\n    }\n\n    public function createGluerProvider()\n    {\n        return [\n            [AdaCase::class],\n            [CamelCase::class],\n            [CobolCase::class],\n            [KebabCase::class],\n            [LowerCase::class],\n            [MacroCase::class],\n            [PascalCase::class],\n            [SentenceCase::class],\n            [SnakeCase::class],\n            [TitleCase::class],\n            [TrainCase::class],\n            [UpperCase::class],\n        ];\n    }\n\n    /**\n     * @covers \\Jawira\\CaseConverter\\Convert::getSource\n     * @throws \\ReflectionException\n     */\n    public function testGetSource()\n    {\n        // Preparing Convert object\n        $convertMock = $this->getMockBuilder(Convert::class)\n                            ->disableOriginalConstructor()\n                            ->setMethods()\n                            ->getMock();\n\n        // Setting detected words\n        $reflectionObject = new ReflectionObject($convertMock);\n        $sourceProperty   = $reflectionObject->getProperty('source');\n        $sourceProperty->setAccessible(true);\n        $sourceProperty->setValue($convertMock, 'this is source string');\n\n        /** @var Convert $convertMock */\n        $result = $convertMock->getSource();\n        $this->assertSame('this is source string', $result);\n    }\n\n    /**\n     * @throws \\ReflectionException\n     * @covers \\Jawira\\CaseConverter\\Convert::fromAuto\n     */\n    public function testFromAuto()\n    {\n        // Preparing Convert object\n        $convertMock = $this->getMockBuilder(Convert::class)\n                            ->disableOriginalConstructor()\n                            ->setMethods(['analyse', 'extractWords'])\n                            ->getMock();\n\n        $strategyMock = $this->getMockBuilder(Splitter::class)\n                             ->disableOriginalConstructor()\n                             ->getMockForAbstractClass();\n\n        // set 'source' attribute\n        $reflectionObject = new ReflectionObject($convertMock);\n        $wordsProperty    = $reflectionObject->getProperty('source');\n        $wordsProperty->setAccessible(true);\n        $wordsProperty->setValue($convertMock, 'dummy-original-string');\n\n        // stub analyse\n        $convertMock->expects($this->once())\n                    ->method('analyse')\n                    ->with('dummy-original-string')\n                    ->willReturn($strategyMock);\n\n        // stub extractWords\n        $convertMock->expects($this->once())\n                    ->method('extractWords')\n                    ->with($strategyMock)\n                    ->willReturnSelf();\n\n        // Calling protected method\n        $reflectionObject = new ReflectionObject($convertMock);\n        $reflectionMethod = $reflectionObject->getMethod('fromAuto');\n        $reflectionMethod->setAccessible(true);\n        $result = $reflectionMethod->invoke($convertMock, 'fromAuto');\n\n        $this->assertInstanceOf(Convert::class, $result);\n        $this->assertSame($convertMock, $result);\n    }\n\n    /**\n     * @covers       \\Jawira\\CaseConverter\\Convert::handleSplitterMethod\n     *\n     * @param string $methodName\n     * @param string $splitterName\n     *\n     * @dataProvider handleSplitterMethodProvider\n     *\n     * @throws \\ReflectionException\n     */\n    public function testHandleSplitterMethod(string $methodName = 'fromKebab', string $splitterName = DashSplitter::class, string $sourceString = 'dummy-azer12')\n    {\n        // Splitter class\n        $splitterMock = $this->getMockBuilder($splitterName)\n                             ->disableOriginalConstructor()\n                             ->getMock();\n\n        // Convert class\n        $convertMock = $this->getMockBuilder(Convert::class)\n                            ->disableOriginalConstructor()\n                            ->setMethods(['createSplitter', 'extractWords'])\n                            ->getMock();\n        $convertMock->expects($this->once())\n                    ->method('createSplitter')\n                    ->with($splitterName, $sourceString)\n                    ->willReturn($splitterMock);\n        $convertMock->expects($this->once())\n                    ->method('extractWords')\n                    ->with($splitterMock);\n\n        // Add source attribute\n        $reflectionObject = new ReflectionObject($convertMock);\n        $wordsProperty    = $reflectionObject->getProperty('source');\n        $wordsProperty->setAccessible(true);\n        $wordsProperty->setValue($convertMock, $sourceString);\n\n        // Invoking protected method\n        $method = new ReflectionMethod($convertMock, 'handleSplitterMethod');\n        $method->setAccessible(true);\n        $result = $method->invoke($convertMock, $methodName);\n\n        $this->assertSame($convertMock, $result);\n    }\n\n    public function handleSplitterMethodProvider()\n    {\n        return [\n            ['fromCamel', UppercaseSplitter::class],\n            ['fromPascal', UppercaseSplitter::class],\n            ['fromSnake', UnderscoreSplitter::class],\n            ['fromAda', UnderscoreSplitter::class],\n            ['fromMacro', UnderscoreSplitter::class],\n            ['fromKebab', DashSplitter::class],\n            ['fromTrain', DashSplitter::class],\n            ['fromCobol', DashSplitter::class],\n            ['fromLower', SpaceSplitter::class],\n            ['fromUpper', SpaceSplitter::class],\n            ['fromTitle', SpaceSplitter::class],\n            ['fromSentence', SpaceSplitter::class],\n            ['fromDot', DotSplitter::class],\n        ];\n    }\n\n    /**\n     * @covers \\Jawira\\CaseConverter\\Convert::handleSplitterMethod\n     *\n     * @throws \\ReflectionException\n     */\n    public function testHandleSplitterMethodWithException()\n    {\n        // Preparing exception\n        $this->expectException(CaseConverterException::class);\n        $this->expectExceptionMessage('Unknown method: myDummyMethod');\n\n        // Convert class\n        $convertMock = $this->getMockBuilder(Convert::class)\n                            ->disableOriginalConstructor()\n                            ->setMethods()\n                            ->getMock();\n\n        // Invoking protected method\n        $method = new ReflectionMethod($convertMock, 'handleSplitterMethod');\n        $method->setAccessible(true);\n        $method->invokeArgs($convertMock, ['myDummyMethod']);\n    }\n\n    /**\n     * @covers       \\Jawira\\CaseConverter\\Convert::createSplitter\n     * @covers       \\Jawira\\CaseConverter\\Split\\Splitter::__construct\n     *\n     * @dataProvider createSplitterProvider\n     *\n     * @param string $className\n     *\n     * @throws \\ReflectionException\n     */\n    public function testCreateSplitter(string $className)\n    {\n        // Preparing Convert mock\n        $mock = $this->getMockBuilder(Convert::class)\n                     ->disableOriginalConstructor()\n                     ->setMethods()\n                     ->getMock();\n\n        // Calling protected method\n        $reflectionObject = new ReflectionObject($mock);\n        $reflectionMethod = $reflectionObject->getMethod('createSplitter');\n        $reflectionMethod->setAccessible(true);\n        $splitterObject = $reflectionMethod->invoke($mock, $className, 'dummy-string', false);\n\n        $this->assertInstanceOf($className, $splitterObject);\n    }\n\n    public function createSplitterProvider()\n    {\n        return [\n            [DashSplitter::class, 'dummy-string'],\n            [SpaceSplitter::class, 'dummy-string'],\n            [UnderscoreSplitter::class, 'dummy-string'],\n            [UppercaseSplitter::class, 'dummy-string'],\n        ];\n    }\n\n    /**\n     * Testing magic function call\n     *\n     * @covers       \\Jawira\\CaseConverter\\Convert::__call\n     * @dataProvider callProvider\n     *\n     * @param string $methodName\n     * @param array  $arguments\n     * @param int    $splitterCount\n     * @param int    $gluerCount\n     * @param mixed  $handlerResult\n     */\n    public function testCall(string $methodName, $arguments, int $splitterCount, int $gluerCount, string $handlerResult = '')\n    {\n        // Preparing Convert mock\n        $convertMock = $this->getMockBuilder(Convert::class)\n                            ->disableOriginalConstructor()\n                            ->setMethods(['handleSplitterMethod', 'handleGluerMethod'])\n                            ->getMock();\n\n        $convertMock->expects($this->exactly($splitterCount))\n                    ->method('handleSplitterMethod')\n                    ->with($methodName)\n                    ->willReturnSelf();\n\n        $convertMock->expects($this->exactly($gluerCount))\n                    ->method('handleGluerMethod')\n                    ->with($methodName)\n                    ->willReturn($handlerResult);\n\n        /** @var string|\\Jawira\\CaseConverter\\Split\\Splitter $result */\n        $result = $convertMock->__call($methodName, $arguments);\n\n        if ($splitterCount) {\n            $this->assertInstanceOf(Convert::class, $result);\n        }\n        if ($gluerCount) {\n            $this->assertSame($handlerResult, $result);\n        }\n    }\n\n    public function callProvider()\n    {\n        return [\n            ['toDummy', [], 0, 1, '46d54fd5'],\n            ['toAda', [], 0, 1, 'ed65f'],\n            ['toCamel', [], 0, 1, '65hg4jk45'],\n            ['toCobol', [], 0, 1, 'a1g94a1'],\n            ['toKebab', [], 0, 1, 'd4g2q1df'],\n            ['toLower', [], 0, 1, 'ff4f1f'],\n            ['toMacro', [], 0, 1, 'fe64df'],\n            ['toPascal', [], 0, 1, 'ff1f1f1f'],\n            ['toSentence', [], 0, 1, 'de9e1d'],\n            ['toSnake', [], 0, 1, 'fdq1nh1jl'],\n            ['toTitle', [], 0, 1, 'hjy94'],\n            ['toTrain', [], 0, 1, 'g4i4ol'],\n            ['toUpper', [], 0, 1, 'gu1ioo1'],\n            ['fromDummy', [], 1, 0],\n            ['fromAda', [], 1, 0],\n            ['fromCamel', [], 1, 0],\n            ['fromCobol', [], 1, 0],\n            ['fromKebab', [], 1, 0],\n            ['fromLower', [], 1, 0],\n            ['fromMacro', [], 1, 0],\n            ['fromPascal', [], 1, 0],\n            ['fromSentence', [], 1, 0],\n            ['fromSnake', [], 1, 0],\n            ['fromTitle', [], 1, 0],\n            ['fromTrain', [], 1, 0],\n            ['fromUpper', [], 1, 0],\n        ];\n    }\n\n    /**\n     * @covers       \\Jawira\\CaseConverter\\Convert::__call\n     */\n    public function testCallException()\n    {\n        $this->expectException(Jawira\\CaseConverter\\CaseConverterException::class);\n        $this->expectExceptionMessage('Unknown method: invalidMethod');\n\n        // Preparing Convert mock\n        $convertMock = $this->getMockBuilder(Convert::class)\n                            ->disableOriginalConstructor()\n                            ->setMethods()\n                            ->getMock();\n\n        /** @var string|\\Jawira\\CaseConverter\\Split\\Splitter $result */\n        $convertMock->__call('invalidMethod', []);\n    }\n}\n"
  },
  {
    "path": "tests/phpunit/DashSplitterTest.php",
    "content": "<?php\n\nuse Jawira\\CaseConverter\\Split\\DashSplitter;\nuse PHPUnit\\Framework\\TestCase;\n\nclass DashSplitterTest extends TestCase\n{\n    /**\n     * @covers \\Jawira\\CaseConverter\\Split\\DashSplitter::split\n     * @throws \\ReflectionException\n     * @throws \\Jawira\\CaseConverter\\CaseConverterException\n     */\n    public function testSplit()\n    {\n        // Disabling constructor with one stub method\n        $mock = $this->getMockBuilder(DashSplitter::class)\n                     ->disableOriginalConstructor()\n                     ->setMethods(['splitUsingPattern'])\n                     ->getMock();\n\n        // Setting value to protected property\n        $reflectionObject   = new ReflectionObject($mock);\n        $reflectionProperty = $reflectionObject->getProperty('inputString');\n        $reflectionProperty->setAccessible(true);\n        $reflectionProperty->setValue($mock, 'dummyString');\n\n        // Configuring stub\n        $mock->expects($this->once())\n             ->method('splitUsingPattern')\n             ->with('dummyString', '#-+#u')\n             ->willReturn(['dummy', 'array']);\n\n        /** @var \\Jawira\\CaseConverter\\Split\\DashSplitter $mock */\n        $returned = $mock->split();\n        $this->assertSame(['dummy', 'array'], $returned);\n    }\n}\n"
  },
  {
    "path": "tests/phpunit/DotNotationTest.php",
    "content": "<?php\n\nuse Jawira\\CaseConverter\\Glue\\DotNotation;\nuse PHPUnit\\Framework\\TestCase;\n\nclass DotNotationTest extends TestCase\n{\n    /**\n     * @covers \\Jawira\\CaseConverter\\Glue\\DotNotation::glue\n     * @throws \\ReflectionException\n     */\n    public function testGlue()\n    {\n        // Disabling constructor with one stub method\n        $mock = $this->getMockBuilder(DotNotation::class)\n                     ->disableOriginalConstructor()\n                     ->setMethods(['glueUsingRules'])\n                     ->getMock();\n\n        // Setting `upperCase` property value\n        $reflectionObject  = new ReflectionObject($mock);\n        $titleCaseProperty = $reflectionObject->getProperty('lowerCase');\n        $titleCaseProperty->setAccessible(true);\n        $titleCaseProperty->setValue($mock, 789);\n\n        // Configuring stub\n        $mock->expects($this->once())\n             ->method('glueUsingRules')\n             ->with(DotNotation::DELIMITER, 789)\n             ->willReturn('e1bfd762321e409cee4ac0b6e841963c');\n\n        /** @var \\Jawira\\CaseConverter\\Glue\\MacroCase $mock */\n        $returned = $mock->glue();\n        $this->assertSame('e1bfd762321e409cee4ac0b6e841963c', $returned, 'Returned value is not the expected');\n    }\n}\n"
  },
  {
    "path": "tests/phpunit/DotSplitterTest.php",
    "content": "<?php\n\nuse Jawira\\CaseConverter\\Split\\DotSplitter;\nuse PHPUnit\\Framework\\TestCase;\n\nclass DotSplitterTest extends TestCase\n{\n    /**\n     * @covers       \\Jawira\\CaseConverter\\Split\\DotSplitter::split\n     *\n     * @throws \\Jawira\\CaseConverter\\CaseConverterException\n     * @throws \\ReflectionException\n     */\n    public function testSplit()\n    {\n        // Disabling constructor with one stub method\n        $mock = $this->getMockBuilder(DotSplitter::class)\n                     ->disableOriginalConstructor()\n                     ->setMethods(['splitUsingPattern'])\n                     ->getMock();\n\n        // Setting value to protected property\n        $reflectionObject   = new ReflectionObject($mock);\n        $reflectionProperty = $reflectionObject->getProperty('inputString');\n        $reflectionProperty->setAccessible(true);\n        $reflectionProperty->setValue($mock, 'dummyString');\n\n        // Configuring stub\n        $mock->expects($this->once())\n             ->method('splitUsingPattern')\n             ->with('dummyString', '#\\\\.+#u')\n             ->willReturn(['dummy', 'array']);\n\n        /** @var \\Jawira\\CaseConverter\\Split\\UnderscoreSplitter $mock */\n        $returned = $mock->split();\n        $this->assertSame(['dummy', 'array'], $returned);\n    }\n}\n"
  },
  {
    "path": "tests/phpunit/GluerTest.php",
    "content": "<?php\n\nuse Jawira\\CaseConverter\\Glue\\Gluer;\nuse PHPUnit\\Framework\\TestCase;\n\nclass GluerTest extends TestCase\n{\n    /**\n     * @covers       \\Jawira\\CaseConverter\\Glue\\Gluer::glueUsingRules\n     * @dataProvider glueUsingRulesProvider\n     *\n     * @param array  $words\n     * @param string $glue\n     * @param int    $wordsMode\n     * @param        $firstWordMode\n     * @param string $expected\n     *\n     * @throws \\ReflectionException\n     */\n    public function testGlueUsingRules(array $words, string $glue, int $wordsMode, $firstWordMode, string $expected)\n    {\n        // Disabling constructor without stub methods\n        $mock = $this->getMockBuilder(Gluer::class)\n                     ->disableOriginalConstructor()\n                     ->setMethods(['changeWordsCase', 'changeFirstWordCase'])\n                     ->getMockForAbstractClass();\n\n        // Making \"words\" property accessible and setting a value\n        $reflection = new ReflectionObject($mock);\n        $property   = $reflection->getProperty('words');\n        $property->setAccessible(true);\n        $property->setValue($mock, $words);\n\n        // Making public a protected method\n        $reflection = new ReflectionObject($mock);\n        $method     = $reflection->getMethod('glueUsingRules');\n        $method->setAccessible(true);\n\n        // Expectations for changeWordsCase\n        $mock->expects($this->once())\n             ->method('changeWordsCase')\n             ->with($this->equalTo($words, $wordsMode))\n             ->will($this->returnValue($words));\n\n        // Only checking that method is called\n        $expectation = ($firstWordMode) ? $this->once() : $this->never();\n        $mock->expects($expectation)\n             ->method('changeFirstWordCase')\n             ->with($words, $firstWordMode)\n             ->will($this->returnValue($words));\n\n        // Testing\n        $output = $method->invoke($mock, $glue, $wordsMode, $firstWordMode);\n        $this->assertSame($expected, $output);\n    }\n\n    /**\n     * @return array\n     */\n    public function glueUsingRulesProvider()\n    {\n        return [\n            [['fOo', 'bAr'], '§', MB_CASE_LOWER, null, 'fOo§bAr'],\n            [['fOo', 'bAr'], '§', MB_CASE_LOWER, MB_CASE_LOWER, 'fOo§bAr'],\n            [['fOo', 'bAr'], 'X', MB_CASE_LOWER, null, 'fOoXbAr'],\n            [['fOo', 'bAr'], 'X', MB_CASE_LOWER, MB_CASE_LOWER, 'fOoXbAr'],\n        ];\n    }\n\n    /**\n     * @covers       \\Jawira\\CaseConverter\\Glue\\Gluer::changeWordsCase\n     * @dataProvider changeWordsCaseProvider\n     *\n     * @param array $words\n     * @param int   $caseMode\n     * @param array $expected\n     *\n     * @throws \\ReflectionException\n     */\n    public function testChangeWordsCase($words, $caseMode, $expected)\n    {\n        // Disabling constructor without stub methods\n        $mock = $this->getMockBuilder(Gluer::class)\n                     ->disableOriginalConstructor()\n                     ->setMethods(['glueUsingRules'])\n                     ->getMockForAbstractClass();\n\n        // Making public a protected method\n        $reflection = new ReflectionObject($mock);\n        $method     = $reflection->getMethod('changeWordsCase');\n        $method->setAccessible(true);\n        $result = $method->invoke($mock, $words, $caseMode);\n\n        $this->assertSame($expected, $result);\n    }\n\n    public function changeWordsCaseProvider()\n    {\n        return [\n            [[], MB_CASE_LOWER, []],\n            [['hola', 'mundo'], MB_CASE_LOWER, ['hola', 'mundo']],\n            [['hola', 'mundo'], MB_CASE_UPPER, ['HOLA', 'MUNDO']],\n            [['hola', 'mundo'], MB_CASE_TITLE, ['Hola', 'Mundo']],\n            [['HoLa', 'MuNdO'], MB_CASE_LOWER, ['hola', 'mundo']],\n            [['HoLa', 'MuNdO'], MB_CASE_UPPER, ['HOLA', 'MUNDO']],\n            [['HoLa', 'MuNdO'], MB_CASE_TITLE, ['Hola', 'Mundo']],\n        ];\n    }\n\n    /**\n     * @covers       \\Jawira\\CaseConverter\\Glue\\Gluer::changeFirstWordCase\n     * @dataProvider changeFirstWordCaseProvider\n     *\n     * @param array $words\n     * @param int   $caseMode\n     * @param array $expected\n     *\n     * @throws \\ReflectionException\n     */\n    public function testChangeFirstWordsCase($words, $caseMode, $expected)\n    {\n        // Disabling constructor without stub methods\n        $mock = $this->getMockBuilder(Gluer::class)\n                     ->disableOriginalConstructor()\n                     ->setMethods(['glueUsingRules'])\n                     ->getMockForAbstractClass();\n\n        // Making public a protected method\n        $reflection = new ReflectionObject($mock);\n        $method     = $reflection->getMethod('changeFirstWordCase');\n        $method->setAccessible(true);\n        $result = $method->invoke($mock, $words, $caseMode);\n\n        $this->assertSame($expected, $result);\n    }\n\n    public function changeFirstWordCaseProvider()\n    {\n        return [\n            [[], MB_CASE_LOWER, []],\n            [['hola', 'mundo'], MB_CASE_LOWER, ['hola', 'mundo']],\n            [['hola', 'mundo'], MB_CASE_UPPER, ['HOLA', 'mundo']],\n            [['hola', 'mundo'], MB_CASE_TITLE, ['Hola', 'mundo']],\n            [['HoLa', 'MuNdO'], MB_CASE_LOWER, ['hola', 'MuNdO']],\n            [['HoLa', 'MuNdO'], MB_CASE_UPPER, ['HOLA', 'MuNdO']],\n            [['HoLa', 'MuNdO'], MB_CASE_TITLE, ['Hola', 'MuNdO']],\n        ];\n    }\n\n    public function constructorProvider()\n    {\n        return [\n            [['dummy', 'f4s2Q'], false, 0],\n            [['dummy', '7e7e7'], true, 1],\n        ];\n    }\n}\n"
  },
  {
    "path": "tests/phpunit/KebabCaseTest.php",
    "content": "<?php\n\nuse Jawira\\CaseConverter\\Glue\\KebabCase;\nuse PHPUnit\\Framework\\TestCase;\n\nclass KebabCaseTest extends TestCase\n{\n    /**\n     * Testing that `glue` method is called and `lowerCase` property is used.\n     *\n     * @covers \\Jawira\\CaseConverter\\Glue\\KebabCase::glue\n     * @throws \\ReflectionException\n     */\n    public function testGlue()\n    {\n        // Disabling constructor with one stub method\n        $mock = $this->getMockBuilder(KebabCase::class)\n                     ->disableOriginalConstructor()\n                     ->setMethods(['glueUsingRules'])\n                     ->getMock();\n\n        // Setting lowerCase properties\n        $reflectionObject  = new ReflectionObject($mock);\n        $titleCaseProperty = $reflectionObject->getProperty('lowerCase');\n        $titleCaseProperty->setAccessible(true);\n        $titleCaseProperty->setValue($mock, 456);\n\n        // Configuring stub\n        $mock->expects($this->once())\n             ->method('glueUsingRules')\n             ->with(KebabCase::DELIMITER, 456)\n             ->willReturn('e1bfd762321e409cee4ac0b6e841963c');\n\n        /** @var \\Jawira\\CaseConverter\\Glue\\KebabCase $mock */\n        $returned = $mock->glue();\n        $this->assertSame('e1bfd762321e409cee4ac0b6e841963c', $returned, 'Returned value is not the expected');\n    }\n}\n"
  },
  {
    "path": "tests/phpunit/LowerCaseTest.php",
    "content": "<?php\n\nuse Jawira\\CaseConverter\\Glue\\LowerCase;\nuse PHPUnit\\Framework\\TestCase;\n\nclass LowerCaseTest extends TestCase\n{\n    /**\n     * @covers \\Jawira\\CaseConverter\\Glue\\LowerCase::glue\n     */\n    public function testGlue()\n    {\n        // Disabling constructor with one stub method\n        $mock = $this->getMockBuilder(LowerCase::class)\n                     ->disableOriginalConstructor()\n                     ->setMethods(['glueUsingRules'])\n                     ->getMock();\n\n        // Setting lowerCase properties\n        $reflectionObject  = new ReflectionObject($mock);\n        $titleCaseProperty = $reflectionObject->getProperty('lowerCase');\n        $titleCaseProperty->setAccessible(true);\n        $titleCaseProperty->setValue($mock, 456);\n\n        // Configuring stub\n        $mock->expects($this->once())\n             ->method('glueUsingRules')\n             ->with(LowerCase::DELIMITER, 456)\n             ->willReturn('e1bfd762321e409cee4ac0b6e841963c');\n\n        /** @var \\Jawira\\CaseConverter\\Glue\\LowerCase $mock */\n        $returned = $mock->glue();\n        $this->assertSame('e1bfd762321e409cee4ac0b6e841963c', $returned, 'Returned value is not the expected');\n    }\n}\n"
  },
  {
    "path": "tests/phpunit/MacroCaseTest.php",
    "content": "<?php\n\nuse Jawira\\CaseConverter\\Glue\\MacroCase;\nuse PHPUnit\\Framework\\TestCase;\n\nclass MacroCaseTest extends TestCase\n{\n    /**\n     * @covers \\Jawira\\CaseConverter\\Glue\\MacroCase::glue\n     */\n    public function testGlue()\n    {\n        // Disabling constructor with one stub method\n        $mock = $this->getMockBuilder(MacroCase::class)\n                     ->disableOriginalConstructor()\n                     ->setMethods(['glueUsingRules'])\n                     ->getMock();\n\n        // Setting `upperCase` property value\n        $reflectionObject  = new ReflectionObject($mock);\n        $titleCaseProperty = $reflectionObject->getProperty('upperCase');\n        $titleCaseProperty->setAccessible(true);\n        $titleCaseProperty->setValue($mock, 789);\n\n        // Configuring stub\n        $mock->expects($this->once())\n             ->method('glueUsingRules')\n             ->with(MacroCase::DELIMITER, 789)\n             ->willReturn('e1bfd762321e409cee4ac0b6e841963c');\n\n        /** @var \\Jawira\\CaseConverter\\Glue\\MacroCase $mock */\n        $returned = $mock->glue();\n        $this->assertSame('e1bfd762321e409cee4ac0b6e841963c', $returned, 'Returned value is not the expected');\n    }\n}\n"
  },
  {
    "path": "tests/phpunit/PascalCaseTest.php",
    "content": "<?php\n\nuse Jawira\\CaseConverter\\Glue\\PascalCase;\nuse PHPUnit\\Framework\\TestCase;\n\nclass PascalCaseTest extends TestCase\n{\n    /**\n     * @covers \\Jawira\\CaseConverter\\Glue\\PascalCase::glue\n     */\n    public function testGlue()\n    {\n        // Disabling constructor with one stub method\n        $mock = $this->getMockBuilder(PascalCase::class)\n                     ->disableOriginalConstructor()\n                     ->setMethods(['glueUsingRules'])\n                     ->getMock();\n\n        // Setting titleCase and lowerCase properties\n        $reflectionObject  = new ReflectionObject($mock);\n        $titleCaseProperty = $reflectionObject->getProperty('titleCase');\n        $titleCaseProperty->setAccessible(true);\n        $titleCaseProperty->setValue($mock, 789);\n\n        // Configuring stub\n        $mock->expects($this->once())\n             ->method('glueUsingRules')\n             ->with(PascalCase::DELIMITER, 789)\n             ->willReturn('e1bfd762321e409cee4ac0b6e841963c');\n\n        /** @var \\Jawira\\CaseConverter\\Glue\\PascalCase $mock */\n        $returned = $mock->glue();\n        $this->assertSame('e1bfd762321e409cee4ac0b6e841963c', $returned, 'Returned value is not the expected');\n    }\n}\n"
  },
  {
    "path": "tests/phpunit/SentenceCaseTest.php",
    "content": "<?php\n\nuse Jawira\\CaseConverter\\Glue\\SentenceCase;\nuse PHPUnit\\Framework\\TestCase;\n\nclass SentenceCaseTest extends TestCase\n{\n    /**\n     * @covers \\Jawira\\CaseConverter\\Glue\\SentenceCase::glue\n     */\n    public function testGlue()\n    {\n        // Disabling constructor with one stub method\n        $mock = $this->getMockBuilder(SentenceCase::class)\n                     ->disableOriginalConstructor()\n                     ->setMethods(['glueUsingRules'])\n                     ->getMock();\n\n        // Setting titleCase and lowerCase properties\n        $reflectionObject  = new ReflectionObject($mock);\n        $titleCaseProperty = $reflectionObject->getProperty('titleCase');\n        $titleCaseProperty->setAccessible(true);\n        $titleCaseProperty->setValue($mock, 123);\n        $lowerCaseProperty = $reflectionObject->getProperty('lowerCase');\n        $lowerCaseProperty->setAccessible(true);\n        $lowerCaseProperty->setValue($mock, 456);\n\n        // Configuring stub\n        $mock->expects($this->once())\n             ->method('glueUsingRules')\n             ->with(SentenceCase::DELIMITER, 456, 123)\n             ->willReturn('e1bfd762321e409cee4ac0b6e841963c');\n\n        /** @var \\Jawira\\CaseConverter\\Glue\\SentenceCase $mock */\n        $returned = $mock->glue();\n        $this->assertSame('e1bfd762321e409cee4ac0b6e841963c', $returned, 'Returned value is not the expected');\n    }\n}\n"
  },
  {
    "path": "tests/phpunit/SnakeCaseTest.php",
    "content": "<?php\n\nuse Jawira\\CaseConverter\\Glue\\SnakeCase;\nuse PHPUnit\\Framework\\TestCase;\n\nclass SnakeCaseTest extends TestCase\n{\n    /**\n     * @covers \\Jawira\\CaseConverter\\Glue\\SnakeCase::glue\n     */\n    public function testGlue()\n    {\n        // Disabling constructor with one stub method\n        $mock = $this->getMockBuilder(SnakeCase::class)\n                     ->disableOriginalConstructor()\n                     ->setMethods(['glueUsingRules'])\n                     ->getMock();\n\n        // Setting titleCase and lowerCase properties\n        $reflectionObject  = new ReflectionObject($mock);\n        $lowerCaseProperty = $reflectionObject->getProperty('lowerCase');\n        $lowerCaseProperty->setAccessible(true);\n        $lowerCaseProperty->setValue($mock, 456);\n\n        // Configuring stub\n        $mock->expects($this->once())\n             ->method('glueUsingRules')\n             ->with(SnakeCase::DELIMITER, 456)\n             ->willReturn('e1bfd762321e409cee4ac0b6e841963c');\n\n        /** @var \\Jawira\\CaseConverter\\Glue\\SnakeCase $mock */\n        $returned = $mock->glue();\n        $this->assertSame('e1bfd762321e409cee4ac0b6e841963c', $returned, 'Returned value is not the expected');\n    }\n}\n"
  },
  {
    "path": "tests/phpunit/SpaceSplitterTest.php",
    "content": "<?php\n\nuse Jawira\\CaseConverter\\Split\\SpaceSplitter;\nuse PHPUnit\\Framework\\TestCase;\n\nclass SpaceSplitterTest extends TestCase\n{\n    /**\n     * @covers \\Jawira\\CaseConverter\\Split\\SpaceSplitter::split\n     * @throws \\ReflectionException\n     * @throws \\Jawira\\CaseConverter\\CaseConverterException\n     */\n    public function testSplit()\n    {\n        // Disabling constructor with one stub method\n        $mock = $this->getMockBuilder(SpaceSplitter::class)\n                     ->disableOriginalConstructor()\n                     ->setMethods(['splitUsingPattern'])\n                     ->getMock();\n\n        // Setting value to protected property\n        $reflectionObject   = new ReflectionObject($mock);\n        $reflectionProperty = $reflectionObject->getProperty('inputString');\n        $reflectionProperty->setAccessible(true);\n        $reflectionProperty->setValue($mock, 'dummyString');\n\n        // Configuring stub\n        $mock->expects($this->once())\n             ->method('splitUsingPattern')\n             ->with('dummyString', '# +#u')\n             ->willReturn(['dummy', 'array']);\n\n        /** @var \\Jawira\\CaseConverter\\Split\\SpaceSplitter $mock */\n        $returned = $mock->split();\n        $this->assertSame(['dummy', 'array'], $returned);\n    }\n}\n"
  },
  {
    "path": "tests/phpunit/SplitterTest.php",
    "content": "<?php\n\nuse Jawira\\CaseConverter\\Split\\SpaceSplitter;\nuse PHPUnit\\Framework\\TestCase;\n\nclass SplitterTest extends TestCase\n{\n    /**\n     * @covers       \\Jawira\\CaseConverter\\Split\\SpaceSplitter::splitUsingPattern\n     * @dataProvider splitUsingPatternProvider\n     *\n     * @param $inputString\n     * @param $pattern\n     * @param $expected\n     *\n     * @throws \\ReflectionException\n     */\n    public function testSplitUsingPattern($inputString, $pattern, $expected)\n    {\n        // Disabling constructor with one stub method\n        $mock = $this->getMockBuilder(SpaceSplitter::class)\n                     ->disableOriginalConstructor()\n                     ->setMethods([])\n                     ->getMock();\n\n        // Invoking protected method\n        $method = new ReflectionMethod($mock, 'splitUsingPattern');\n        $method->setAccessible(true);\n        $result = $method->invokeArgs($mock, [$inputString, $pattern]);\n\n        $this->assertSame($expected, $result);\n    }\n\n    public function splitUsingPatternProvider()\n    {\n        return [\n            ['', '#-+#u', []],\n            ['hola-mundo', '#-+#u', ['hola', 'mundo']],\n            ['-hola-mundo-', '#-+#u', ['hola', 'mundo']],\n            ['---hola-----mundo---', '#-+#u', ['hola', 'mundo']],\n            ['0', '#-+#u', ['0']],\n            ['---0---0---', '#-+#u', ['0', '0']],\n            ['---000---000---', '#-+#u', ['000', '000']],\n        ];\n    }\n}\n"
  },
  {
    "path": "tests/phpunit/TitleCaseTest.php",
    "content": "<?php\n\nuse Jawira\\CaseConverter\\Glue\\TitleCase;\nuse PHPUnit\\Framework\\TestCase;\n\nclass TitleCaseTest extends TestCase\n{\n    /**\n     * @covers \\Jawira\\CaseConverter\\Glue\\TitleCase::glue\n     */\n    public function testGlue()\n    {\n        // Disabling constructor with one stub method\n        $mock = $this->getMockBuilder(TitleCase::class)\n                     ->disableOriginalConstructor()\n                     ->setMethods(['glueUsingRules'])\n                     ->getMock();\n\n        // Setting titleCase and lowerCase properties\n        $reflectionObject  = new ReflectionObject($mock);\n        $titleCaseProperty = $reflectionObject->getProperty('titleCase');\n        $titleCaseProperty->setAccessible(true);\n        $titleCaseProperty->setValue($mock, 123);\n\n        // Configuring stub\n        $mock->expects($this->once())\n             ->method('glueUsingRules')\n             ->with(TitleCase::DELIMITER, 123)\n             ->willReturn('e1bfd762321e409cee4ac0b6e841963c');\n\n        /** @var \\Jawira\\CaseConverter\\TitleCase $mock */\n        $returned = $mock->glue();\n        $this->assertSame('e1bfd762321e409cee4ac0b6e841963c', $returned, 'Returned value is not the expected');\n    }\n}\n"
  },
  {
    "path": "tests/phpunit/TrainCaseTest.php",
    "content": "<?php\n\nuse Jawira\\CaseConverter\\Glue\\TrainCase;\nuse PHPUnit\\Framework\\TestCase;\n\nclass TrainCaseTest extends TestCase\n{\n    /**\n     * @covers \\Jawira\\CaseConverter\\Glue\\TrainCase::glue\n     */\n    public function testGlue()\n    {\n        // Disabling constructor with one stub method\n        $mock = $this->getMockBuilder(TrainCase::class)\n                     ->disableOriginalConstructor()\n                     ->setMethods(['glueUsingRules'])\n                     ->getMock();\n\n        // Setting titleCase and lowerCase properties\n        $reflectionObject  = new ReflectionObject($mock);\n        $titleCaseProperty = $reflectionObject->getProperty('titleCase');\n        $titleCaseProperty->setAccessible(true);\n        $titleCaseProperty->setValue($mock, 123);\n\n        // Configuring stub\n        $mock->expects($this->once())\n             ->method('glueUsingRules')\n             ->with(TrainCase::DELIMITER, 123)\n             ->willReturn('e1bfd762321e409cee4ac0b6e841963c');\n\n        /** @var \\Jawira\\CaseConverter\\Glue\\TrainCase $mock */\n        $returned = $mock->glue();\n        $this->assertSame('e1bfd762321e409cee4ac0b6e841963c', $returned, 'Returned value is not the expected');\n    }\n}\n"
  },
  {
    "path": "tests/phpunit/UnderscoreSplitterTest.php",
    "content": "<?php\n\nuse Jawira\\CaseConverter\\Split\\UnderscoreSplitter;\nuse PHPUnit\\Framework\\TestCase;\n\nclass UnderscoreSplitterTest extends TestCase\n{\n    /**\n     * @covers \\Jawira\\CaseConverter\\Split\\UnderscoreSplitter::split\n     * @throws \\ReflectionException\n     * @throws \\Jawira\\CaseConverter\\CaseConverterException\n     */\n    public function testSplit()\n    {\n        // Disabling constructor with one stub method\n        $mock = $this->getMockBuilder(UnderscoreSplitter::class)\n                     ->disableOriginalConstructor()\n                     ->setMethods(['splitUsingPattern'])\n                     ->getMock();\n\n        // Setting value to protected property\n        $reflectionObject   = new ReflectionObject($mock);\n        $reflectionProperty = $reflectionObject->getProperty('inputString');\n        $reflectionProperty->setAccessible(true);\n        $reflectionProperty->setValue($mock, 'dummyString');\n\n        // Configuring stub\n        $mock->expects($this->once())\n             ->method('splitUsingPattern')\n             ->with('dummyString', '#_+#u')\n             ->willReturn(['dummy', 'array']);\n\n        /** @var \\Jawira\\CaseConverter\\Split\\UnderscoreSplitter $mock */\n        $returned = $mock->split();\n        $this->assertSame(['dummy', 'array'], $returned);\n    }\n}\n"
  },
  {
    "path": "tests/phpunit/UpperCaseTest.php",
    "content": "<?php\n\nuse Jawira\\CaseConverter\\Glue\\UpperCase;\nuse PHPUnit\\Framework\\TestCase;\n\nclass UpperCaseTest extends TestCase\n{\n    /**\n     * @covers \\Jawira\\CaseConverter\\Glue\\UpperCase::glue\n     */\n    public function testGlue()\n    {\n        // Disabling constructor with one stub method\n        $mock = $this->getMockBuilder(UpperCase::class)\n                     ->disableOriginalConstructor()\n                     ->setMethods(['glueUsingRules'])\n                     ->getMock();\n\n        // Setting titleCase and lowerCase properties\n        $reflectionObject  = new ReflectionObject($mock);\n        $titleCaseProperty = $reflectionObject->getProperty('upperCase');\n        $titleCaseProperty->setAccessible(true);\n        $titleCaseProperty->setValue($mock, 789);\n\n        // Configuring stub\n        $mock->expects($this->once())\n             ->method('glueUsingRules')\n             ->with(UpperCase::DELIMITER, 789)\n             ->willReturn('e1bfd762321e409cee4ac0b6e841963c');\n\n        /** @var \\Jawira\\CaseConverter\\Glue\\UpperCase $mock */\n        $returned = $mock->glue();\n        $this->assertSame('e1bfd762321e409cee4ac0b6e841963c', $returned, 'Returned value is not the expected');\n    }\n}\n"
  },
  {
    "path": "tests/phpunit/UppercaseSplitterTest.php",
    "content": "<?php\n\nuse Jawira\\CaseConverter\\Split\\UppercaseSplitter;\nuse PHPUnit\\Framework\\TestCase;\n\nclass UppercaseSplitterTest extends TestCase\n{\n    /**\n     * @covers       \\Jawira\\CaseConverter\\Split\\UppercaseSplitter::split\n     *\n     * @throws \\Jawira\\CaseConverter\\CaseConverterException\n     * @throws \\ReflectionException\n     */\n    public function testSplit()\n    {\n        // Disabling constructor with one stub method\n        $mock = $this->getMockBuilder(UppercaseSplitter::class)\n                     ->disableOriginalConstructor()\n                     ->setMethods(['splitUsingPattern'])\n                     ->getMock();\n\n        // Setting value to protected property\n        $reflectionObject   = new ReflectionObject($mock);\n        $reflectionProperty = $reflectionObject->getProperty('inputString');\n        $reflectionProperty->setAccessible(true);\n        $reflectionProperty->setValue($mock, 'dummyString');\n\n        // Configuring stub\n        $mock->expects($this->once())\n             ->method('splitUsingPattern')\n             ->with('dummyString', '#(?=\\p{Lu}{1})#u')\n             ->willReturn(['dummy', 'array']);\n\n        /** @var \\Jawira\\CaseConverter\\Split\\UnderscoreSplitter $mock */\n        $returned = $mock->split();\n        $this->assertSame(['dummy', 'array'], $returned);\n    }\n}\n"
  }
]