[
  {
    "path": ".gitattributes",
    "content": "*.doc  diff=astextplain\r\n*.DOC\tdiff=astextplain\r\n*.docx\tdiff=astextplain\r\n*.DOCX\tdiff=astextplain\r\n*.dot\tdiff=astextplain\r\n*.DOT\tdiff=astextplain\r\n*.pdf\tdiff=astextplain\r\n*.PDF\tdiff=astextplain\r\n*.rtf\tdiff=astextplain\r\n*.RTF\tdiff=astextplain\r\n\r\n*.jpg  \tbinary\r\n*.png \tbinary\r\n*.gif \tbinary\r\n\r\n*.cs text diff=csharp \r\n*.vb text\r\n*.c text\r\n*.cpp text\r\n*.cxx text\r\n*.h text\r\n*.hxx text\r\n*.py text\r\n*.rb text\r\n*.java text\r\n*.html text\r\n*.htm text\r\n*.css text\r\n*.scss text\r\n*.sass text\r\n*.less text\r\n*.js text\r\n*.lisp text\r\n*.clj text\r\n*.sql text\r\n*.php text\r\n*.lua text\r\n*.m text\r\n*.asm text\r\n*.erl text\r\n*.fs text\r\n*.fsx text\r\n*.hs text\r\n\r\n*.csproj text merge=union \r\n*.vbproj text merge=union \r\n*.fsproj text merge=union \r\n*.dbproj text merge=union \r\n*.sln text eol=crlf merge=union"
  },
  {
    "path": ".github/CODEOWNERS",
    "content": "* @darrelmiller\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n- package-ecosystem: nuget\n  directory: \"/\"\n  schedule:\n    interval: daily\n  open-pull-requests-limit: 10\n- package-ecosystem: github-actions\n  directory: \"/\"\n  schedule:\n    interval: daily\n  open-pull-requests-limit: 10\n- package-ecosystem: gitsubmodule\n  directory: \"/\"\n  schedule:\n    interval: daily\n  open-pull-requests-limit: 10\n"
  },
  {
    "path": ".github/workflows/auto-merge-dependabot.yml",
    "content": "name: Auto-merge dependabot updates\n\non:\n  pull_request:\n    branches: [ main ]\n\npermissions:\n  pull-requests: write\n  contents: write\n\njobs:\n\n  dependabot-merge:\n\n    runs-on: ubuntu-latest\n\n    if: ${{ github.actor == 'dependabot[bot]' }}\n\n    steps:\n      - name: Dependabot metadata\n        id: metadata\n        uses: dependabot/fetch-metadata@v2.4.0\n        with:\n          github-token: \"${{ secrets.GITHUB_TOKEN }}\"\n\n      - name: Enable auto-merge for Dependabot PRs\n        # Only if version bump is not a major version change\n        if: ${{steps.metadata.outputs.update-type != 'version-update:semver-major'}}\n        run: gh pr merge --auto --merge \"$PR_URL\"\n        env:\n          PR_URL: ${{github.event.pull_request.html_url}}\n          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}\n"
  },
  {
    "path": ".github/workflows/buildAndDeploy.yml",
    "content": "name: Build and Test\n\non:\n  workflow_dispatch:\n  push:\n    branches: [ main ]\n  pull_request:\n    branches: [ main ]\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    env:\n      solutionName: UriTemplates.sln\n      outputFolder: ./buildArtifacts\n\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v4\n        with:\n          submodules: true\n\n      - name: Setup .NET\n        uses: actions/setup-dotnet@v4\n        with:\n          dotnet-version: 6.0.x\n\n      - name: Restore dependencies\n        run: dotnet restore ${{ env.solutionName }}\n\n      - name: Check formatting\n        run: dotnet format --verify-no-changes --verbosity diagnostic\n\n      - name: Build\n        run: dotnet build ${{ env.solutionName }} --no-restore -c Release\n\n      - name: Test\n        run: dotnet test ${{ env.solutionName }} --no-build --verbosity normal -c Release /p:CollectCoverage=true /p:CoverletOutput=./TestResults/ /p:CoverletOutputFormat=opencover\n\n      - name: Pack\n        run: dotnet pack ${{ env.solutionName }} /p:IncludeSymbols=true /p:SymbolPackageFormat=snupkg --no-build --output ${{ env.outputFolder }} -c Release\n\n      - name: Upload Nuget Package and Symbols\n        uses: actions/upload-artifact@v4\n        with:\n          name: drop\n          path: |\n            ${{ env.outputFolder }}/*.nupkg\n            ${{ env.outputFolder }}/*.snupkg\n\n  deploy:\n    if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}\n    environment:\n      name: production\n    runs-on: ubuntu-latest\n    needs: [build]\n    steps:\n      - name: Setup .NET\n        uses: actions/setup-dotnet@v4\n        with:\n          dotnet-version: 6.0.x\n          \n      - name: Download artifacts\n        uses: actions/download-artifact@v4\n        with:\n          name: drop\n      \n      - name: Nuget push\n        run: dotnet nuget push \"*.nupkg\" --skip-duplicate -s https://api.nuget.org/v3/index.json -k ${{ secrets.PUBLISH_GH_TOKEN }}\n      \n"
  },
  {
    "path": ".github/workflows/codeql-analysis.yml",
    "content": "# For most projects, this workflow file will not need changing; you simply need\n# to commit it to your repository.\n#\n# You may wish to alter this file to override the set of languages analyzed,\n# or to provide custom queries or build logic.\n#\n# ******** NOTE ********\n# We have attempted to detect the languages in your repository. Please check\n# the `language` matrix defined below to confirm you have the correct set of\n# supported CodeQL languages.\n#\nname: \"CodeQL\"\n\non:\n  workflow_dispatch:\n  push:\n    branches: [main]\n  pull_request:\n    # The branches below must be a subset of the branches above\n  schedule:\n    - cron: \"20 9 * * 5\"\n\njobs:\n  analyze:\n    name: Analyze\n    runs-on: ubuntu-latest\n\n    strategy:\n      fail-fast: false\n      matrix:\n        language: [\"csharp\"]\n        # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]\n        # Learn more:\n        # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed\n\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v4\n        with:\n          submodules: true\n\n      - name: Setup .NET\n        uses: actions/setup-dotnet@v4\n        with:\n          dotnet-version: 6.0.x\n\n      # Initializes the CodeQL tools for scanning.\n      - name: Initialize CodeQL\n        uses: github/codeql-action/init@v3\n        with:\n          languages: ${{ matrix.language }}\n          # If you wish to specify custom queries, you can do so here or in a config file.\n          # By default, queries listed here will override any specified in a config file.\n          # Prefix the list here with \"+\" to use these queries and those in the config file.\n          # queries: ./path/to/local/query, your-org/your-repo/queries@main\n\n      # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).\n      # If this step fails, then you should remove it and run the build manually (see below)\n      # - name: Autobuild\n      #   uses: github/codeql-action/autobuild@v2\n\n      # ℹ️ Command-line programs to run using the OS shell.\n      # 📚 https://git.io/JvXDl\n\n      # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines\n      #    and modify them (or add more) to build your code if your project\n      #    uses a compiled language\n\n      - name: Restore workloads\n        run: |\n          dotnet workload restore\n          dotnet workload install wasm-tools\n      - name: Restore dependencies\n        run: dotnet restore\n      - name: Build\n        run: dotnet build --no-restore -c Release\n\n      - name: Perform CodeQL Analysis\n        uses: github/codeql-action/analyze@v3\n"
  },
  {
    "path": ".gitignore",
    "content": "\n#ignore thumbnails created by windows\nThumbs.db\n#Ignore files build by Visual Studio\n*.obj\n*.exe\n*.pdb\n*.user\n*.aps\n*.pch\n*.vspscc\n*_i.c\n*_p.c\n*.ncb\n*.suo\n*.tlb\n*.tlh\n*.bak\n*.cache\n*.ilk\n*.log\nClientBin\n[Bb]in\n[Dd]ebug*/\n*.lib\n*.sbr\nobj/\n[Rr]elease*/\n_ReSharper*/\n[Tt]est[Rr]esult*\nExamples*/\nSerializerTest*/\n*.csproj.user\n*.resharper*\nDownload/\n#Ignore files from MonoDevelop\n*.pidb\n*.userprefs\npackages\nartifacts\n*.nupkg\npublish-tavis*.ps1\nproject.lock.json\n.vs/\n.idea"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"lib/uritemplate-test\"]\n\tpath = lib/uritemplate-test\n\turl = https://github.com/uri-templates/uritemplate-test.git\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: csharp\nsudo: false   # use the new container-based Travis infrastructure\ninstall:\n  - nuget restore UriTemplates.sln\nscript:\n  - xbuild /p:Configuration=Release /t:Compile build/Build.proj\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n## [Unreleased]\n\n### Added\n\n- Added support for netstandard 2.1 [#69](https://github.com/tavis-software/Tavis.UriTemplates/issues/69)\n\n### Changed\n\n## [2.0.0]\n\n- [Breaking] Updated Target Framework Moniker to `netstandard2.0` and drops `net35`, `net40`, `net45` and `netstandard1.0`.\n\n## [1.1.2]\n\n- Added Type converter support.\n\n## [1.1.1]\n\n- Fixed bug parsing query parameter with comma delimited values\n\n## [1.1.0]\n\n- Updated Target Framework Moniker from dotnet to netstandard1.0\n\n## [1.0.0]\n\n- This project has lived too long as a 0.x release.  I believe it has seen enough production use to be considered a 1.0 release.\n\n## [0.6.6]\n\n- Bugfix\n\n## [0.6.5]\n\n- Added ability to retrieve URITemplates from UriTemplateTable\n- Parameter matching\n\n## [0.6.4]\n\n- Added .net4 version of assembly\n- Updated nuget to put portable lib in dotnet folder to enable coreclr support\n- Made Resolve() thread safe by ensuring it does not share any state from one invocation to the next.\n- Added support for profile92 to allow including in Portable libraries that target .net4\n- Added support for case insensitive parameter names.\n\n## [0.6.3]\n\n- Added ToString() overload to allow retrieving unresolved template\n\n## [0.6.2]\n\n- URI Template Extension AddParameters now uses IDictionary instead of Dictionary\n\n## [0.6.1]\n\n- Added ClearParameter to unset a template parameter\n- Added MakeTemplate URI extension for creating a Uri template based on the query string parameters of a URI\n- Added GetQueryStringParameters URI extension for building dictionary of query parameters and values\n- Added AddParameters overload that accepts a dictionary of template parameters\n\n## [0.6.0]\n\n- Added the ability to partially resolve templates using a new constructor parameter.\n- Added new fluent interface using extension methods for quickly creating a template and resolving it.\n- Created a .net 45 project\n- Restructured folders to comply with recommendations made by David Fowler\n- Added many more usage tests with real world scenarios\n- Added support for Windows 8.1\n- Fixed Unicode encoding problem\n- Added support for passing non-string lists as parameters\n- Added support for passing parameters values that are non-string.\n"
  },
  {
    "path": "LICENSE",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n\n   Copyright 2012 Tavis Software Inc.\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License."
  },
  {
    "path": "README.md",
    "content": "# Uri Templates # \n\n[![Build and deploy](https://github.com/tavis-software/Tavis.UriTemplates/actions/workflows/buildAndDeploy.yml/badge.svg)](https://github.com/tavis-software/Tavis.UriTemplates/actions/workflows/buildAndDeploy.yml) [![CodeQL](https://github.com/tavis-software/Tavis.UriTemplates/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/tavis-software/Tavis.UriTemplates/actions/workflows/codeql-analysis.yml) [![NuGet](https://img.shields.io/nuget/v/Tavis.UriTemplates.svg?label=NuGet)](https://www.nuget.org/packages/Tavis.UriTemplates/)\n\n.NET implementation of the [URI Template Spec RFC6570](http://tools.ietf.org/html/rfc6570). \n\nLibrary implements Level 4 compliance and is tested against test cases from [UriTemplate test suite](https://github.com/uri-templates/uritemplate-test).\n\n\nHere are some basic usage examples:\n\nReplacing a path segment parameter,\n\n```csharp\n[Fact]\npublic void UpdatePathParameter()\n{\n    var url = new UriTemplate(\"http://example.org/{tenant}/customers\")\n        .AddParameter(\"tenant\", \"acmé\")\n        .Resolve();\n\n    Assert.Equal(\"http://example.org/acm%C3%A9/customers\", url);\n}\n```\n\nSetting query string parameters,\n\n```csharp\n[Fact]\npublic void ShouldResolveUriTemplateWithNonStringParameter()\n{\n    var url = new UriTemplate(\"http://example.org/location{?lat,lng}\")\n        .AddParameters(new { lat = 31.464, lng = 74.386 })\n        .Resolve();\n\n    Assert.Equal(\"http://example.org/location?lat=31.464&lng=74.386\", url);\n}\n```\n\n\nResolving a URI when parameters are not set will simply remove the parameters,\n\n```csharp\n[Fact]\npublic void SomeParametersFromAnObject()\n{\n    var url = new UriTemplate(\"http://example.org{/environment}{/version}/customers{?active,country}\")\n        .AddParameters(new\n        {\n            version = \"v2\",\n            active = \"true\"\n        })\n        .Resolve();\n\n    Assert.Equal(\"http://example.org/v2/customers?active=true\", url);\n}\n```\n\nYou can even pass lists as parameters\n\n```csharp\n[Fact]\npublic void ApplyParametersObjectWithAListofInts()\n{\n    var url = new UriTemplate(\"http://example.org/customers{?ids,order}\")\n        .AddParameters(new\n        {\n            order = \"up\",\n            ids = new[] {21, 75, 21}\n        })\n        .Resolve();\n\n    Assert.Equal(\"http://example.org/customers?ids=21,75,21&order=up\", url);\n}\n```\n\nAnd dictionaries,\n\n```csharp\n[Fact]\npublic void ApplyDictionaryToQueryParameters()\n{\n    var url = new UriTemplate(\"http://example.org/foo{?coords*}\")\n        .AddParameter(\"coords\", new Dictionary<string, string>\n        {\n            {\"x\", \"1\"},\n            {\"y\", \"2\"},\n        })\n        .Resolve();\n\n    Assert.Equal(\"http://example.org/foo?x=1&y=2\", url);\n}\n```\n\nWe also handle all the complex URI encoding rules automatically.\n\n```csharp\n[Fact]\npublic void TestExtremeEncoding()\n{\n    var url = new UriTemplate(\"http://example.org/sparql{?query}\")\n            .AddParameter(\"query\", \"PREFIX dc: <http://purl.org/dc/elements/1.1/> SELECT ?book ?who WHERE { ?book dc:creator ?who }\")\n            .Resolve();\n    Assert.Equal(\"http://example.org/sparql?query=PREFIX%20dc%3A%20%3Chttp%3A%2F%2Fpurl.org%2Fdc%2Felements%2F1.1%2F%3E%20SELECT%20%3Fbook%20%3Fwho%20WHERE%20%7B%20%3Fbook%20dc%3Acreator%20%3Fwho%20%7D\", url);\n}\n```\n\nThere is a [blogpost](http://bizcoder.com/constructing-urls-the-easy-way) that discusses these examples and more in detail.\n\nAs well as having a set of regular usage tests, this library also executes tests based on a standard test suite.  This test suite is pulled in as a Git Submodule, therefore when cloning this repo, you will need use the `--recursive` switch,\n\n        git clone --recursive git@github.com:tavis-software/Tavis.UriTemplates.git\n\n\nCurrent this library does not pass all of the failure tests.  I.e. If you pass an invalid URI Template, you may not get an exception.\n"
  },
  {
    "path": "UriTemplates.sln",
    "content": "﻿\r\nMicrosoft Visual Studio Solution File, Format Version 12.00\r\n# Visual Studio 15\r\nVisualStudioVersion = 15.0.28307.489\r\nMinimumVisualStudioVersion = 10.0.40219.1\r\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Solution Items\", \"Solution Items\", \"{0C994FD2-43CE-4412-BCCD-628DBD8130DF}\"\r\n\tProjectSection(SolutionItems) = preProject\r\n\t\tLicense.txt = License.txt\r\n\t\tReadme.md = Readme.md\r\n\tEndProjectSection\r\nEndProject\r\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Build\", \"Build\", \"{718037C6-0F82-456A-B7EB-531694D035B4}\"\r\n\tProjectSection(SolutionItems) = preProject\r\n\t\tReleaseNotes.md = ReleaseNotes.md\r\n\tEndProjectSection\r\nEndProject\r\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"UriTemplates\", \"src\\UriTemplates\\UriTemplates.csproj\", \"{F60E3FBC-22FE-43A7-A46B-93C30B74B72A}\"\r\nEndProject\r\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"UriTemplateTests\", \"src\\UriTemplateTests\\UriTemplateTests.csproj\", \"{2D43074E-C8E2-4D1D-B64D-7A267908840B}\"\r\nEndProject\r\nGlobal\r\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\r\n\t\tDebug|Any CPU = Debug|Any CPU\r\n\t\tDebug|Mixed Platforms = Debug|Mixed Platforms\r\n\t\tDebug|x86 = Debug|x86\r\n\t\tRelease|Any CPU = Release|Any CPU\r\n\t\tRelease|Mixed Platforms = Release|Mixed Platforms\r\n\t\tRelease|x86 = Release|x86\r\n\tEndGlobalSection\r\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\r\n\t\t{F60E3FBC-22FE-43A7-A46B-93C30B74B72A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\r\n\t\t{F60E3FBC-22FE-43A7-A46B-93C30B74B72A}.Debug|Any CPU.Build.0 = Debug|Any CPU\r\n\t\t{F60E3FBC-22FE-43A7-A46B-93C30B74B72A}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU\r\n\t\t{F60E3FBC-22FE-43A7-A46B-93C30B74B72A}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU\r\n\t\t{F60E3FBC-22FE-43A7-A46B-93C30B74B72A}.Debug|x86.ActiveCfg = Debug|Any CPU\r\n\t\t{F60E3FBC-22FE-43A7-A46B-93C30B74B72A}.Debug|x86.Build.0 = Debug|Any CPU\r\n\t\t{F60E3FBC-22FE-43A7-A46B-93C30B74B72A}.Release|Any CPU.ActiveCfg = Release|Any CPU\r\n\t\t{F60E3FBC-22FE-43A7-A46B-93C30B74B72A}.Release|Any CPU.Build.0 = Release|Any CPU\r\n\t\t{F60E3FBC-22FE-43A7-A46B-93C30B74B72A}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU\r\n\t\t{F60E3FBC-22FE-43A7-A46B-93C30B74B72A}.Release|Mixed Platforms.Build.0 = Release|Any CPU\r\n\t\t{F60E3FBC-22FE-43A7-A46B-93C30B74B72A}.Release|x86.ActiveCfg = Release|Any CPU\r\n\t\t{F60E3FBC-22FE-43A7-A46B-93C30B74B72A}.Release|x86.Build.0 = Release|Any CPU\r\n\t\t{2D43074E-C8E2-4D1D-B64D-7A267908840B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\r\n\t\t{2D43074E-C8E2-4D1D-B64D-7A267908840B}.Debug|Any CPU.Build.0 = Debug|Any CPU\r\n\t\t{2D43074E-C8E2-4D1D-B64D-7A267908840B}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU\r\n\t\t{2D43074E-C8E2-4D1D-B64D-7A267908840B}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU\r\n\t\t{2D43074E-C8E2-4D1D-B64D-7A267908840B}.Debug|x86.ActiveCfg = Debug|Any CPU\r\n\t\t{2D43074E-C8E2-4D1D-B64D-7A267908840B}.Debug|x86.Build.0 = Debug|Any CPU\r\n\t\t{2D43074E-C8E2-4D1D-B64D-7A267908840B}.Release|Any CPU.ActiveCfg = Release|Any CPU\r\n\t\t{2D43074E-C8E2-4D1D-B64D-7A267908840B}.Release|Any CPU.Build.0 = Release|Any CPU\r\n\t\t{2D43074E-C8E2-4D1D-B64D-7A267908840B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU\r\n\t\t{2D43074E-C8E2-4D1D-B64D-7A267908840B}.Release|Mixed Platforms.Build.0 = Release|Any CPU\r\n\t\t{2D43074E-C8E2-4D1D-B64D-7A267908840B}.Release|x86.ActiveCfg = Release|Any CPU\r\n\t\t{2D43074E-C8E2-4D1D-B64D-7A267908840B}.Release|x86.Build.0 = Release|Any CPU\r\n\tEndGlobalSection\r\n\tGlobalSection(SolutionProperties) = preSolution\r\n\t\tHideSolutionNode = FALSE\r\n\tEndGlobalSection\r\n\tGlobalSection(ExtensibilityGlobals) = postSolution\r\n\t\tSolutionGuid = {C814C2F5-05C7-4FAD-A250-2EB1FC0364A0}\r\n\tEndGlobalSection\r\nEndGlobal\r\n"
  },
  {
    "path": "build.cmd",
    "content": "@echo Off\nset config=%1\nif \"%config%\" == \"\" (\n   set config=debug\n)\nmd artifacts\ndotnet build --configuration %config% --verbosity normal /m /v:M /fl /flp:LogFile=msbuild.log; /nr:false\ndotnet pack --no-build --configuration %config% --output %cd%\\artifacts"
  },
  {
    "path": "src/UriTemplateTests/App.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\" ?>\r\n<configuration>\r\n    <uri>\r\n      <idn enabled=\"All\" />\r\n      <iriParsing enabled=\"true\" />\r\n    </uri>\r\n</configuration>"
  },
  {
    "path": "src/UriTemplateTests/ParameterMatchingTests.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Text.RegularExpressions;\nusing Tavis.UriTemplates;\nusing Xunit;\nusing Xunit.Extensions;\n\nnamespace UriTemplateTests\n{\n    public class ParameterMatchingTests\n    {\n\n\n        [Fact]\n        public void MatchUriToTemplate()\n        {\n            var uri = new Uri(\"http://example.com/foo/bar\");\n\n            var sTemplate = \"http://example.com/{p1}/{p2}\";\n\n            var x = UriTemplate.CreateMatchingRegex(sTemplate);\n\n            var match = Regex.IsMatch(uri.AbsoluteUri, x);\n            Assert.True(match);\n        }\n\n        [Fact]\n        public void GetParameters()\n        {\n            var uri = new Uri(\"http://example.com/foo/bar\");\n\n            var sTemplate = \"http://example.com/{p1}/{p2}\";\n\n            var x = UriTemplate.CreateMatchingRegex(sTemplate);\n            var regex = new Regex(x);\n\n            var match = regex.Match(uri.AbsoluteUri);\n\n            Assert.Equal(\"foo\", match.Groups[\"p1\"].Value);\n            Assert.Equal(\"bar\", match.Groups[\"p2\"].Value);\n        }\n\n        [Fact]\n        public void GetParametersWithOperators()\n        {\n            var uri = new Uri(\"http://example.com/foo/bar\");\n\n            var template = new UriTemplate(\"http://example.com/{+p1}/{p2*}\");\n\n            var parameters = template.GetParameters(uri);\n\n            Assert.Equal(2, parameters.Count);\n            Assert.Equal(\"foo\", parameters[\"p1\"]);\n            Assert.Equal(\"bar\", parameters[\"p2\"]);\n        }\n\n        [Fact]\n        public void GetParametersFromQueryString()\n        {\n            var uri = new Uri(\"http://example.com/foo/bar?blur=45\");\n\n            var template = new UriTemplate(\"http://example.com/{+p1}/{p2*}{?blur}\");\n\n            var parameters = template.GetParameters(uri);\n\n            Assert.Equal(3, parameters.Count);\n\n            Assert.Equal(\"foo\", parameters[\"p1\"]);\n            Assert.Equal(\"bar\", parameters[\"p2\"]);\n            Assert.Equal(\"45\", parameters[\"blur\"]);\n        }\n\n        [Fact]\n        public void GetParametersFromMultipleQueryString()\n        {\n            var uri = new Uri(\"http://example.com/foo/bar?blur=45\");\n\n            var template = new UriTemplate(\"http://example.com/{+p1}/{p2*}{?blur,blob}\");\n\n            var parameters = template.GetParameters(uri);\n\n            Assert.Equal(3, parameters.Count);\n            Assert.Equal(\"foo\", parameters[\"p1\"]);\n            Assert.Equal(\"bar\", parameters[\"p2\"]);\n            Assert.Equal(\"45\", parameters[\"blur\"]);\n\n        }\n        [Fact]\n        public void GetParametersFromMultipleQueryStringWithTwoParamValues()\n        {\n            var uri = new Uri(\"http://example.com/foo/bar?blur=45&blob=23\");\n\n            var template = new UriTemplate(\"http://example.com/{+p1}/{p2*}{?blur,blob}\");\n\n            var parameters = template.GetParameters(uri);\n\n            Assert.Equal(4, parameters.Count);\n            Assert.Equal(\"foo\", parameters[\"p1\"]);\n            Assert.Equal(\"bar\", parameters[\"p2\"]);\n            Assert.Equal(\"45\", parameters[\"blur\"]);\n            Assert.Equal(\"23\", parameters[\"blob\"]);\n\n        }\n\n        [Fact]\n        public void GetParameterFromArrayParameter()\n        {\n            var uri = new Uri(\"http://example.com?blur=45,23\");\n\n            var template = new UriTemplate(\"http://example.com{?blur}\");\n\n            var parameters = template.GetParameters(uri);\n\n            Assert.Single(parameters);\n            Assert.Equal(\"45,23\", parameters[\"blur\"]);\n\n        }\n\n        [Fact]\n        public void GetParametersFromMultipleQueryStringWithOptionalAndMandatoryParameters()\n        {\n            var uri = new Uri(\"http://example.com/foo/bar?blur=45&blob=23\");\n\n            var template = new UriTemplate(\"http://example.com/{+p1}/{p2*}{?blur}{&blob}\");\n\n            var parameters = template.GetParameters(uri);\n\n            Assert.Equal(4, parameters.Count);\n            Assert.Equal(\"foo\", parameters[\"p1\"]);\n            Assert.Equal(\"bar\", parameters[\"p2\"]);\n            Assert.Equal(\"45\", parameters[\"blur\"]);\n            Assert.Equal(\"23\", parameters[\"blob\"]);\n\n        }\n\n        [Fact]\n        public void GetParametersFromMultipleQueryStringWithOptionalParameters()\n        {\n            var uri = new Uri(\"http://example.com/foo/bar\");\n\n            var template = new UriTemplate(\"http://example.com/{+p1}/{p2*}{?blur,blob}\");\n\n            var parameters = template.GetParameters(uri);\n\n            Assert.Equal(\"foo\", parameters[\"p1\"]);\n            Assert.Equal(\"bar\", parameters[\"p2\"]);\n\n        }\n\n\n        [Fact]\n        public void TestGlimpseUrl()\n        {\n            var uri = new Uri(\"http://example.com/Glimpse.axd?n=glimpse_ajax&parentRequestId=123232323&hash=23ADE34FAE&callback=http%3A%2F%2Fexample.com%2Fcallback\");\n\n            var template = new UriTemplate(\"http://example.com/Glimpse.axd?n=glimpse_ajax&parentRequestId={parentRequestId}{&hash,callback}\");\n\n            var parameters = template.GetParameters(uri);\n\n            Assert.Equal(3, parameters.Count);\n            Assert.Equal(\"123232323\", parameters[\"parentRequestId\"]);\n            Assert.Equal(\"23ADE34FAE\", parameters[\"hash\"]);\n            Assert.Equal(\"http://example.com/callback\", parameters[\"callback\"]);\n\n        }\n\n        [Fact]\n        public void TestUrlWithQuestionMarkAsFirstCharacter()\n        {\n\n            var parameters = new UriTemplate(\"?hash={hash}\").GetParameters(new Uri(\"http://localhost:5000/glimpse/metadata?hash=123\")); ;\n\n            Assert.Single(parameters);\n            Assert.Equal(\"123\", parameters[\"hash\"]);\n\n        }\n\n\n\n        [Fact]\n        public void TestExactParameterCount()\n        {\n            var uri = new Uri(\"http://example.com/foo?bar=10\");\n\n            var template = new UriTemplate(\"http://example.com/foo{?bar}\");\n\n            var parameters = template.GetParameters(uri);\n\n            Assert.Single(parameters);\n\n        }\n\n        [Fact]\n        public void SimplePerfTest()\n        {\n            var uri = new Uri(\"http://example.com/Glimpse.axd?n=glimpse_ajax&parentRequestId=123232323&hash=23ADE34FAE&callback=http%3A%2F%2Fexample.com%2Fcallback\");\n\n            var template = new UriTemplate(\"http://example.com/Glimpse.axd?n=glimpse_ajax&parentRequestId={parentRequestId}{&hash,callback}\");\n\n            for (int i = 0; i < 100000; i++)\n            {\n                var parameters = template.GetParameters(uri);\n\n            }\n\n\n        }\n\n\n        [Fact]\n        public void Level1Decode()\n        {\n            var uri = new Uri(\"/Hello%20World\", UriKind.RelativeOrAbsolute);\n\n            var template = new UriTemplate(\"/{p1}\");\n\n            var parameters = template.GetParameters(uri);\n\n            Assert.Equal(\"Hello World\", parameters[\"p1\"]);\n\n        }\n\n\n        //[Fact]\n        //public void Level2Decode()\n        //{\n        //    var uri = new Uri(\"/foo?path=Hello/World\", UriKind.RelativeOrAbsolute);\n\n        //    var template = new UriTemplate(\"/foo?path={+p1}\");\n\n        //    var parameters = template.GetParameters(uri);\n\n        //    Assert.Equal(\"Hello/World\", parameters[\"p1\"]);\n\n        //}\n\n        [Fact]\n        public void FragmentParam()\n        {\n            var uri = new Uri(\"/foo#Hello%20World!\", UriKind.RelativeOrAbsolute);\n\n            var template = new UriTemplate(\"/foo{#p1}\");\n\n            var parameters = template.GetParameters(uri);\n\n            Assert.Equal(\"Hello World!\", parameters[\"p1\"]);\n\n        }\n\n\n        [Fact]\n        public void FragmentParams()\n        {\n            var uri = new Uri(\"/foo#Hello%20World!,blurg\", UriKind.RelativeOrAbsolute);\n\n            var template = new UriTemplate(\"/foo{#p1,p2}\");\n\n            var parameters = template.GetParameters(uri);\n\n            Assert.Equal(\"Hello World!\", parameters[\"p1\"]);\n            Assert.Equal(\"blurg\", parameters[\"p2\"]);\n\n        }\n\n        [Fact]\n        public void OptionalPathParam()\n        {\n            var uri = new Uri(\"/foo/yuck/bob\", UriKind.RelativeOrAbsolute);\n\n            var template = new UriTemplate(\"/foo{/bar}/bob\");\n\n            var parameters = template.GetParameters(uri);\n\n            Assert.Equal(\"yuck\", parameters[\"bar\"]);\n\n        }\n\n        [Fact]\n        public void OptionalPathParamWithMultipleValues()\n        {\n            var uri = new Uri(\"/foo/yuck/yob/bob\", UriKind.RelativeOrAbsolute);\n\n            var template = new UriTemplate(\"/foo{/bar,baz}/bob\");\n\n            var parameters = template.GetParameters(uri);\n            Assert.Equal(2, parameters.Count); // This current fails\n            Assert.Equal(\"yuck\", parameters[\"bar\"]);\n            Assert.Equal(\"yob\", parameters[\"baz\"]);\n        }\n\n\n    }\n}\n"
  },
  {
    "path": "src/UriTemplateTests/SpecTests.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing Newtonsoft.Json.Linq;\nusing Tavis.UriTemplates;\nusing Xunit;\nusing Xunit.Extensions;\n\nnamespace UriTemplateTests\n{\n    public class UriTemplateTests2\n    {\n\n        [Theory, MemberData(nameof(SpecSamples))]\n        public void SpecSamplesTest(string template, string[] results, TestSet.TestCase testCase)\n        {\n            var uriTemplate = new UriTemplate(template);\n\n            foreach (var variable in testCase.TestSet.Variables)\n            {\n                uriTemplate.SetParameter(variable.Key, variable.Value);\n            }\n\n            string result = null;\n            result = uriTemplate.Resolve();\n\n            Assert.Contains(results, x => x == result);\n        }\n\n\n        [Theory, MemberData(nameof(ExtendedSamples))]\n        public void ExtendedSamplesTest(string template, string[] results, TestSet.TestCase testCase)\n        {\n            var uriTemplate = new UriTemplate(template);\n\n            foreach (var variable in testCase.TestSet.Variables)\n            {\n                uriTemplate.SetParameter(variable.Key, variable.Value);\n            }\n\n            string result = null;\n            ArgumentException aex = null;\n\n            try\n            {\n                result = uriTemplate.Resolve();\n\n            }\n            catch (ArgumentException ex)\n            {\n                aex = ex;\n            }\n\n            if (results[0] == \"False\")\n            {\n                Assert.NotNull(aex);\n            }\n            else\n            {\n                Assert.Contains(results, x => x == result);\n            }\n\n        }\n\n\n        [Theory(Skip = \"Disabled for the moment.\"), MemberData(nameof(FailureSamples))]\n        public void FailureSamplesTest(string template, string[] results, TestSet.TestCase testCase)\n        {\n            var uriTemplate = new UriTemplate(template);\n\n            foreach (var variable in testCase.TestSet.Variables)\n            {\n                uriTemplate.SetParameter(variable.Key, variable.Value);\n            }\n\n            string result = null;\n            ArgumentException aex = null;\n\n            try\n            {\n                result = uriTemplate.Resolve();\n\n            }\n            catch (ArgumentException ex)\n            {\n                aex = ex;\n            }\n\n            if (results[0] == \"False\")\n            {\n                Assert.NotNull(aex);\n            }\n            else\n            {\n                Assert.Contains(results, x => x == result);\n            }\n\n\n        }\n\n\n        public static IEnumerable<object[]> SpecSamples\n        {\n            get\n            {\n                Stream stream = null;\n\n                var suites = new List<Dictionary<string, TestSet>>();\n\n                stream =\n                    typeof(UriTemplateTests2).Assembly.GetManifestResourceStream(\"UriTemplateTests.spec-examples.json\");\n                suites.Add(CreateTestSuite(new StreamReader(stream).ReadToEnd()));\n\n                stream = typeof(UriTemplateTests2).Assembly.GetManifestResourceStream(\"UriTemplateTests.spec-examples-by-section.json\");\n                suites.Add(CreateTestSuite(new StreamReader(stream).ReadToEnd()));\n\n\n                foreach (var suite in suites)\n                {\n\n                    foreach (var testset in suite.Values)\n                    {\n                        foreach (var testCase in testset.TestCases)\n                        {\n                            yield return new object[] { testCase.Template, testCase.Result, testCase };\n\n                        }\n                    }\n\n                }\n\n            }\n        }\n\n\n        public static IEnumerable<object[]> ExtendedSamples\n        {\n            get\n            {\n                Stream stream = null;\n\n                var suites = new List<Dictionary<string, TestSet>>();\n\n                stream =\n                    typeof(UriTemplateTests2).Assembly.GetManifestResourceStream(\"UriTemplateTests.extended-tests.json\");\n                suites.Add(CreateTestSuite(new StreamReader(stream).ReadToEnd()));\n\n                foreach (var suite in suites)\n                {\n\n                    foreach (var testset in suite.Values)\n                    {\n                        foreach (var testCase in testset.TestCases)\n                        {\n                            yield return new object[] { testCase.Template, testCase.Result, testCase };\n\n                        }\n                    }\n\n                }\n\n            }\n        }\n\n        public static IEnumerable<object[]> FailureSamples\n        {\n            get\n            {\n                Stream stream = null;\n\n                var suites = new List<Dictionary<string, TestSet>>();\n\n\n                stream =\n                    typeof(UriTemplateTests2).Assembly.GetManifestResourceStream(\"UriTemplateTests.negative-tests.json\");\n                suites.Add(CreateTestSuite(new StreamReader(stream).ReadToEnd()));\n\n\n                foreach (var suite in suites)\n                {\n\n                    foreach (var testset in suite.Values)\n                    {\n                        foreach (var testCase in testset.TestCases)\n                        {\n                            yield return new object[] { testCase.Template, testCase.Result, testCase };\n\n                        }\n                    }\n\n                }\n\n            }\n        }\n\n\n        private static Dictionary<string, TestSet> CreateTestSuite(string json)\n        {\n            JObject token = JObject.Parse(json);\n\n            var testSuite = new Dictionary<string, TestSet>();\n            foreach (JProperty levelSet in token.Children())\n            {\n                testSuite.Add(levelSet.Name, CreateTestSet(levelSet.Name, levelSet.Value));\n\n            }\n            return testSuite;\n        }\n\n        private static TestSet CreateTestSet(string name, JToken token)\n        {\n            var testSet = new TestSet();\n            testSet.Name = name;\n\n            var variables = token[\"variables\"];\n\n            foreach (JProperty variable in variables)\n            {\n                ParseVariable(variable, testSet.Variables);\n            }\n\n            var testcases = token[\"testcases\"];\n\n            foreach (var testcase in testcases)\n            {\n                testSet.TestCases.Add(CreateTestCase(testSet, testcase));\n            }\n\n            return testSet;\n        }\n\n        private static void ParseVariable(JProperty variable, Dictionary<string, object> dictionary)\n        {\n            if (variable.Value.Type == JTokenType.Array)\n            {\n                var array = (JArray)variable.Value;\n                if (array.Count == 0)\n                {\n                    dictionary.Add(variable.Name, new List<string>());\n                }\n                else\n                {\n                    dictionary.Add(variable.Name, array.Values<string>());\n                }\n            }\n            else if (variable.Value.Type == JTokenType.Object)\n            {\n                var jvalue = (JObject)variable.Value;\n                var dict = new Dictionary<string, string>();\n                foreach (var prop in jvalue.Properties())\n                {\n                    dict[prop.Name] = prop.Value.ToString();\n                }\n                dictionary.Add(variable.Name, dict);\n            }\n            else\n            {\n                if (((JValue)variable.Value).Value == null)\n                {\n                    dictionary.Add(variable.Name, null);\n                }\n                else\n                {\n                    dictionary.Add(variable.Name, variable.Value.ToString());\n                }\n\n            }\n        }\n\n        private static TestSet.TestCase CreateTestCase(TestSet testSet, JToken testcase)\n        {\n            var testCase = new TestSet.TestCase(testSet);\n\n            testCase.Template = testcase[0].Value<string>();\n\n            if (testcase[1].Type == JTokenType.Array)\n            {\n                var results = (JArray)testcase[1];\n                testCase.Result = results.Select(jv => jv.Value<string>()).ToArray();\n            }\n            else\n            {\n                testCase.Result = new string[1];\n                testCase.Result[0] = testcase[1].Value<string>();\n            }\n            return testCase;\n        }\n\n        public class TestSet\n        {\n            public string Name { get; set; }\n            public int level { get; set; }\n            public Dictionary<string, object> Variables = new Dictionary<string, object>();\n            public List<TestCase> TestCases = new List<TestCase>();\n\n            public class TestCase\n            {\n                private readonly TestSet _testSet;\n\n                public TestCase(TestSet testSet)\n                {\n                    _testSet = testSet;\n                }\n\n                public TestSet TestSet\n                {\n                    get { return _testSet; }\n                }\n\n                public string Template { get; set; }\n                public string[] Result { get; set; }\n            }\n\n\n        }\n\n\n\n    }\n}\n"
  },
  {
    "path": "src/UriTemplateTests/UriExtensionTests.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Tavis.UriTemplates;\nusing Xunit;\n\nnamespace UriTemplateTests\n{\n    public class UriExtensionTests\n    {\n        [Fact]\n        public void Change_an_existing_parameter_within_multiple()\n        {\n            var target = new Uri(\"http://example/customer?view=False&foo=bar\");\n\n            var parameters = target.GetQueryStringParameters();\n            parameters[\"view\"] = true;\n\n            var template = target.MakeTemplate(parameters);\n\n            Assert.Equal(\"http://example/customer?view=True&foo=bar\", template.Resolve());\n        }\n\n        [Fact]\n        public void Change_an_existing_parameter()\n        {\n            var target = new Uri(\"http://example/customer?view=False&foo=bar\");\n\n            var template = target.MakeTemplate();\n            template.SetParameter(\"view\", true);\n\n            Assert.Equal(\"http://example/customer?view=True&foo=bar\", template.Resolve());\n        }\n\n        [Fact]\n        public void Remove_an_existing_parameter()\n        {\n            var target = new Uri(\"http://example/customer?view=False&foo=bar\");\n\n            var template = target.MakeTemplate();\n            template.ClearParameter(\"view\");\n\n            Assert.Equal(\"http://example/customer?foo=bar\", template.Resolve());\n        }\n\n        [Fact]\n        public void Remove_a_query_parameters2()\n        {\n\n            var target = new Uri(\"http://example.org/customer?format=xml&id=23\");\n\n            var template = target.MakeTemplate();\n            template.ClearParameter(\"format\");\n\n\n            Assert.Equal(\"http://example.org/customer?id=23\", template.Resolve());\n        }\n\n        [Fact]\n        public void Add_multiple_parameters_to_uri()\n        {\n            var target = new Uri(\"http://example/customer\");\n\n            var template = target.MakeTemplate(new Dictionary<string, object>\n            {\n                {\"id\", 99},\n                {\"view\", false}\n            });\n\n            Assert.Equal(\"http://example/customer?id=99&view=False\", template.Resolve());\n        }\n\n        [Fact]\n        public void Add_parameters_to_uri_with_query_string_ignoring_path_parameter()\n        {\n            var target = new Uri(\"http://example/customer/{id}?view=true\");\n\n\n            var template = target.MakeTemplate(target.GetQueryStringParameters()\n                .Union(new Dictionary<string, object> { { \"context\", \"detail\" } })\n                .ToDictionary(k => k.Key, v => v.Value));\n            template.AddParameter(\"id\", 99);\n\n            Assert.Equal(\"http://example/customer/99?view=true&context=detail\", template.Resolve());\n        }\n    }\n}\n"
  },
  {
    "path": "src/UriTemplateTests/UriTemplateConverterTests.cs",
    "content": "﻿using System.ComponentModel;\nusing Tavis.UriTemplates;\nusing Xunit;\n\nnamespace UriTemplateTests\n{\n    public class UriTemplateConverterTests\n    {\n        [Theory]\n        [InlineData(\"http://example.org/{tenant}/customers\")]\n        [InlineData(\"http://example.org/{environment}/{version}/customers{?active,country}\")]\n        [InlineData(\"http://example.org/foo{?coords*}\")]\n        public void ConvertFromString(string rawTemplate)\n        {\n            var converter = TypeDescriptor.GetConverter(typeof(UriTemplate));\n            var template = converter.ConvertFromString(rawTemplate);\n\n            Assert.NotNull(template);\n            Assert.Equal(rawTemplate, template.ToString());\n        }\n    }\n}\n"
  },
  {
    "path": "src/UriTemplateTests/UriTemplateExtensionsTests.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Tavis;\nusing Tavis.UriTemplates;\nusing Xunit;\n\nnamespace UriTemplateTests\n{\n    public class UriTemplateExtensionsTests\n    {\n        [Fact]\n        public void UpdatePathParameter()\n        {\n            var url = new UriTemplate(\"http://example.org/{tenant}/customers\")\n                .AddParameter(\"tenant\", \"acmé\")\n                .Resolve();\n\n            Assert.Equal(\"http://example.org/acm%C3%A9/customers\", url);\n        }\n\n\n        [Fact]\n        public void QueryParametersTheOldWay()\n        {\n            var url = new UriTemplate(\"http://example.org/customers?active={activeflag}\")\n                .AddParameter(\"activeflag\", \"true\")\n                .Resolve();\n\n            Assert.Equal(\"http://example.org/customers?active=true\", url);\n        }\n\n        [Fact]\n        public void QueryParametersTheNewWay()\n        {\n            var url = new UriTemplate(\"http://example.org/customers{?active}\")\n                .AddParameter(\"active\", \"true\")\n                .Resolve();\n\n            Assert.Equal(\"http://example.org/customers?active=true\", url);\n        }\n\n        [Fact]\n        public void QueryParametersTheNewWayWithoutValue()\n        {\n\n            var url = new UriTemplate(\"http://example.org/customers{?active}\")\n                .AddParameters(null)\n                .Resolve();\n\n            Assert.Equal(\"http://example.org/customers\", url);\n        }\n\n        [Fact]\n        public void ShouldResolveUriTemplateWithNonStringParameter()\n        {\n            var url = new UriTemplate(\"http://example.org/location{?lat,lng}\")\n                .AddParameters(new { lat = 31.464, lng = 74.386 })\n                .Resolve();\n\n            Assert.Equal(\"http://example.org/location?lat=31.464&lng=74.386\", url);\n        }\n\n\n        [Fact]\n        public void ParametersFromAnObject()\n        {\n            var url = new UriTemplate(\"http://example.org/{environment}/{version}/customers{?active,country}\")\n                .AddParameters(new\n                {\n                    environment = \"dev\",\n                    version = \"v2\",\n                    active = \"true\",\n                    country = \"CA\"\n                })\n                .Resolve();\n\n            Assert.Equal(\"http://example.org/dev/v2/customers?active=true&country=CA\", url);\n        }\n\n        [Fact]\n        public void SomeParametersFromAnObject()\n        {\n            var url = new UriTemplate(\"http://example.org{/environment}{/version}/customers{?active,country}\")\n                .AddParameters(new\n                {\n                    version = \"v2\",\n                    active = \"true\"\n                })\n                .Resolve();\n\n            Assert.Equal(\"http://example.org/v2/customers?active=true\", url);\n        }\n\n        [Fact]\n        public void ApplyDictionaryToQueryParameters()\n        {\n            var url = new UriTemplate(\"http://example.org/foo{?coords*}\")\n                .AddParameter(\"coords\", new Dictionary<string, string>\n                {\n                    {\"x\", \"1\"},\n                    {\"y\", \"2\"},\n                })\n                .Resolve();\n\n            Assert.Equal(\"http://example.org/foo?x=1&y=2\", url);\n        }\n\n        [Fact]\n        public void ApplyParametersObjectToPathSegment()\n        {\n            var url = new UriTemplate(\"http://example.org/foo/{bar}/baz\")\n                .AddParameters(new { bar = \"yo\" })\n                .Resolve();\n\n            Assert.Equal(\"http://example.org/foo/yo/baz\", url);\n        }\n\n        [Fact]\n        public void ExtremeEncoding()\n        {\n            var url = new UriTemplate(\"http://example.org/sparql{?query}\")\n                    .AddParameter(\"query\", \"PREFIX dc: <http://purl.org/dc/elements/1.1/> SELECT ?book ?who WHERE { ?book dc:creator ?who }\")\n                    .Resolve();\n            Assert.Equal(\"http://example.org/sparql?query=PREFIX%20dc%3A%20%3Chttp%3A%2F%2Fpurl.org%2Fdc%2Felements%2F1.1%2F%3E%20SELECT%20%3Fbook%20%3Fwho%20WHERE%20%7B%20%3Fbook%20dc%3Acreator%20%3Fwho%20%7D\", url);\n        }\n\n        [Fact]\n        public void ApplyParametersObjectWithAList()\n        {\n            var url = new UriTemplate(\"http://example.org/customers{?ids,order}\")\n                .AddParameters(new\n                {\n                    order = \"up\",\n                    ids = new List<string> { \"21\", \"75\", \"21\" }\n                }).Resolve();\n\n            Assert.Equal(\"http://example.org/customers?ids=21,75,21&order=up\", url);\n        }\n\n        [Fact]\n        public void ApplyParametersObjectWithAListofInts()\n        {\n            var url = new UriTemplate(\"http://example.org/customers{?ids,order}\")\n                .AddParameters(new\n                {\n                    order = \"up\",\n                    ids = new[] { 21, 75, 21 }\n                })\n                .Resolve();\n\n            Assert.Equal(\"http://example.org/customers?ids=21,75,21&order=up\", url);\n        }\n\n        [Fact]\n        public void ApplyParametersObjectWithAListofIntsExploded()\n        {\n            var url = new UriTemplate(\"http://example.org/customers{?ids*,order}\")\n                .AddParameters(new\n                {\n                    order = \"up\",\n                    ids = new[] { 21, 75, 21 }\n                })\n                .Resolve();\n\n            Assert.Equal(\"http://example.org/customers?ids=21&ids=75&ids=21&order=up\", url);\n        }\n\n        [Fact]\n        public void ApplyFoldersToPath()\n        {\n            var url = new UriTemplate(\"http://example.org/files{/folders*}{?filename}\")\n                .AddParameters(new\n                {\n                    folders = new[] { \"customer\", \"project\" },\n                    filename = \"proposal.pdf\"\n                })\n                .Resolve();\n\n            Assert.Equal(\"http://example.org/files/customer/project?filename=proposal.pdf\", url);\n        }\n\n        [Fact]\n        public void ParametersFromAnObjectFromInvalidUrl()\n        {\n\n            var url = new UriTemplate(\"http://{environment}.example.org/{version}/customers{?active,country}\")\n            .AddParameters(new\n            {\n                environment = \"dev\",\n                version = \"v2\",\n                active = \"true\",\n                country = \"CA\"\n            })\n            .Resolve();\n\n            Assert.Equal(\"http://dev.example.org/v2/customers?active=true&country=CA\", url);\n        }\n\n\n        [Fact]\n        public void ApplyFoldersToPathFromStringNotUrl()\n        {\n\n            var url = new UriTemplate(\"http://example.org{/folders*}{?filename}\")\n                .AddParameters(new\n                {\n                    folders = new[] { \"files\", \"customer\", \"project\" },\n                    filename = \"proposal.pdf\"\n                })\n                .Resolve();\n\n            Assert.Equal(\"http://example.org/files/customer/project?filename=proposal.pdf\", url);\n        }\n\n\n        [Fact]\n        public void ReplaceBaseAddress()\n        {\n\n            var url = new UriTemplate(\"{+baseUrl}api/customer/{id}\")\n                .AddParameters(new\n                {\n                    baseUrl = \"http://example.org/\",\n                    id = \"22\"\n                })\n                .Resolve();\n\n            Assert.Equal(\"http://example.org/api/customer/22\", url);\n        }\n\n        [Fact]\n        public void ReplaceBaseAddressButNotId()\n        {\n\n            var url = new UriTemplate(\"{+baseUrl}api/customer/{id}\", resolvePartially: true)\n                .AddParameters(new\n                {\n                    baseUrl = \"http://example.org/\"\n                })\n                .Resolve();\n\n            Assert.Equal(\"http://example.org/api/customer/{id}\", url);\n        }\n\n        [Fact]\n        public void PartiallyParametersFromAnObjectFromInvalidUrl()\n        {\n\n            var url = new UriTemplate(\"http://{environment}.example.org/{version}/customers{?active,country}\", resolvePartially: true)\n            .AddParameters(new\n            {\n                environment = \"dev\",\n                version = \"v2\"\n            })\n            .Resolve();\n\n            Assert.Equal(\"http://dev.example.org/v2/customers{?active,country}\", url);\n        }\n\n\n        [Fact]\n        public void PartiallyApplyFoldersToPathFromStringNotUrl()\n        {\n\n            var url = new UriTemplate(\"http://example.org{/folders*}{?filename}\", true)\n                .AddParameters(new\n                {\n                    filename = \"proposal.pdf\"\n                })\n                .Resolve();\n\n            Assert.Equal(\"http://example.org{/folders*}?filename=proposal.pdf\", url);\n        }\n\n        [Fact]\n        public void UseArbitraryClassAsParameter()\n        {\n\n\n            var url = new UriTemplate(\"/{test}\", true)\n                .AddParameters(new\n                {\n                    test = new Something()\n                })\n                .Resolve();\n\n            Assert.Equal(\"/something\", url);\n        }\n\n\n        [Fact]\n        public void AddMultipleParametersToLink()\n        {\n            var template = new UriTemplate(\"http://localhost/api/{dataset}/customer{?foo,bar,baz}\");\n\n            template.AddParameters(new Dictionary<string, object>\n            {\n                {\"foo\", \"bar\"},\n                {\"baz\", \"99\"},\n                {\"dataset\", \"bob\"}\n            });\n\n            var uri = template.Resolve();\n\n            Assert.Equal(\"http://localhost/api/bob/customer?foo=bar&baz=99\", uri);\n        }\n\n    }\n\n    class Something\n    {\n        public override string ToString()\n        {\n            return \"something\";\n        }\n    }\n}\n"
  },
  {
    "path": "src/UriTemplateTests/UriTemplateTableTests.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Tavis.UriTemplates;\nusing Xunit;\n\n\nnamespace UriTemplateTests\n{\n    public class UriTemplateTableTests\n    {\n\n\n\n\n\n        [Theory,\n        InlineData(\"/\", \"root\"),\n        InlineData(\"/baz/fod/burg\", \"\"),\n        InlineData(\"/baz/kit\", \"kit\"),\n        InlineData(\"/baz/fod\", \"baz\"),\n        InlineData(\"/baz/fod/blob\", \"blob\"),\n        InlineData(\"/glah/flid/blob\", \"goo\")]\n        public void FindPathTemplates(string url, string key)\n        {\n            var table = new UriTemplateTable();  // Shorter paths and literal path segments should be added to the table first.\n            table.Add(\"root\", new UriTemplate(\"/\"));\n            table.Add(\"foo\", new UriTemplate(\"/foo/{bar}\"));\n            table.Add(\"kit\", new UriTemplate(\"/baz/kit\"));\n            table.Add(\"baz\", new UriTemplate(\"/baz/{bar}\"));\n            table.Add(\"blob\", new UriTemplate(\"/baz/{bar}/blob\"));\n            table.Add(\"goo\", new UriTemplate(\"/{goo}/{bar}/blob\"));\n\n\n            var result = table.Match(new Uri(url, UriKind.RelativeOrAbsolute));\n\n            if (string.IsNullOrEmpty(key))\n            {\n                Assert.Null(result);\n            }\n            else\n            {\n                Assert.Equal(key, result.Key);\n            }\n\n            Assert.NotNull(table[\"goo\"]);\n            Assert.Null(table[\"goo1\"]);\n        }\n\n        [Theory,\n     InlineData(\"/games\", \"games\"),\n     InlineData(\"/games/monopoly/Setup/23\", \"gamessetup\"),\n     InlineData(\"/games/monopoly/Resources/foo/23\", \"resource\"),\n     InlineData(\"/games/monopoly/22/Chat/33\", \"chat\"),\n     InlineData(\"/games/monopoly/22/State/33\", \"state\"),\n    ]\n        public void FindTemplatesInGamesApi(string url, string key)\n        {\n            var table = new UriTemplateTable();\n            table.Add(\"games\", new UriTemplate(\"/games\"));\n            table.Add(\"gamessetup\", new UriTemplate(\"/games/{gametitle}/Setup/{gamesid}\"));\n            table.Add(\"resource\", new UriTemplate(\"/games/{gametitle}/Resources/{resourcetype}/{resourceid}\"));\n            table.Add(\"chat\", new UriTemplate(\"/games/{gametitle}/{gameid}/Chat/{chatid}\"));\n            table.Add(\"state\", new UriTemplate(\"/games/{gametitle}/{gameid}/State/{stateid}\"));\n\n\n            var result = table.Match(new Uri(url, UriKind.RelativeOrAbsolute));\n\n            if (string.IsNullOrEmpty(key))\n            {\n                Assert.Null(result);\n            }\n            else\n            {\n                Assert.Equal(key, result.Key);\n            }\n        }\n\n\n        [Theory,\nInlineData(\"/foo?x=1&y=2\", \"fooxy3\"),\nInlineData(\"/foo?x=1\", \"fooxy2\"),\nInlineData(\"/foo?x=a,b,c,d\", \"fooxy2\"),\nInlineData(\"/foo?y=2\", \"fooxy\"),\n\nInlineData(\"/foo\", \"fooxy\"),\n]\n        public void FindTemplatesWithQueryStrings(string url, string key)\n        {\n            var table = new UriTemplateTable();   // More restrictive templates should have priority over less restrictive ones\n            table.Add(\"fooxy3\", new UriTemplate(\"/foo?x={x}&y={y}\"));\n            table.Add(\"fooxy2\", new UriTemplate(\"/foo?x={x}{&y}\"));\n            table.Add(\"fooxy4\", new UriTemplate(\"/foo?x={x}{&z}\"));\n            table.Add(\"fooxy\", new UriTemplate(\"/foo{?x,y}\"));\n            table.Add(\"foo\", new UriTemplate(\"/foo\"));\n\n\n            var result = table.Match(new Uri(url, UriKind.RelativeOrAbsolute));\n\n            if (string.IsNullOrEmpty(key))\n            {\n                Assert.Null(result);\n            }\n            else\n            {\n                Assert.Equal(key, result.Key);\n            }\n        }\n\n        [Fact]\n        public void FindTemplatesWithArrayQueryParameters()\n        {\n            var table = new UriTemplateTable();   // More restrictive templates should have priority over less restrictive ones\n            table.Add(\"fooxy3\", new UriTemplate(\"/foo?x={x}&y={y}\"));\n            table.Add(\"fooxy2\", new UriTemplate(\"/foo?x={x}{&y}\"));\n            table.Add(\"fooxy4\", new UriTemplate(\"/foo?x={x}{&z}\"));\n            table.Add(\"fooxy\", new UriTemplate(\"/foo{?x,y}\"));\n            table.Add(\"foo\", new UriTemplate(\"/foo\"));\n\n\n            var result = table.Match(new Uri(\"/foo?x=a,b,c,d\", UriKind.RelativeOrAbsolute));\n\n            Assert.Equal(\"fooxy2\", result.Key);\n\n        }\n\n\n        [Fact]\n        public void MatchTemplateWithDifferentOrderOfQueryStringParameters()\n        {\n            var table = new UriTemplateTable();   // More restrictive templates should have priority over less restrictive ones\n            table.Add(\"fooxy3\", new UriTemplate(\"/foo?x={x}&y={y}\"));\n\n            var result = table.Match(new Uri(\"/foo?y=a&x=b\", UriKind.RelativeOrAbsolute), QueryStringParameterOrder.Any);\n\n            Assert.Equal(\"fooxy3\", result.Key);\n        }\n\n    }\n\n}\n\n\n\n"
  },
  {
    "path": "src/UriTemplateTests/UriTemplateTests.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>net6.0</TargetFramework>\n\n    <IsPackable>false</IsPackable>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <EmbeddedResource Include=\"..\\..\\lib\\uritemplate-test\\extended-tests.json\" Link=\"extended-tests.json\" />\n    <EmbeddedResource Include=\"..\\..\\lib\\uritemplate-test\\negative-tests.json\" Link=\"negative-tests.json\" />\n    <EmbeddedResource Include=\"..\\..\\lib\\uritemplate-test\\spec-examples-by-section.json\" Link=\"spec-examples-by-section.json\" />\n    <EmbeddedResource Include=\"..\\..\\lib\\uritemplate-test\\spec-examples.json\" Link=\"spec-examples.json\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"Microsoft.NET.Test.Sdk\" Version=\"17.13.0\" />\n    <PackageReference Include=\"xunit\" Version=\"2.9.3\" />\n    <PackageReference Include=\"xunit.runner.visualstudio\" Version=\"3.0.2\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\UriTemplates\\UriTemplates.csproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "src/UriTemplateTests/UsageTests.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Net.Http;\nusing System.Text;\nusing Tavis.UriTemplates;\nusing Xunit;\n\nnamespace UriTemplateTests\n{\n    public class UsageTests\n    {\n        [Fact]\n        public void TestHexEscape()\n        {\n            for (int i = 20; i < 128; i++)\n            {\n                Assert.Equal(Uri.HexEscape((char)i), Result.HexEscape((char)i));\n            }\n\n        }\n\n\n        [Fact]\n        public void ShouldAllowUriTemplateWithPathSegmentParameter()\n        {\n            var template = new UriTemplate(\"http://example.org/foo/{bar}/baz\");\n            template.SetParameter(\"bar\", \"yo\");\n            var uriString = template.Resolve();\n            Assert.Equal(\"http://example.org/foo/yo/baz\", uriString);\n        }\n\n\n        [Fact]\n        public void ShouldAllowUriTemplateWithMultiplePathSegmentParameter()\n        {\n            var template = new UriTemplate(\"http://example.org/foo/{bar}/baz/{blar}\");\n            template.SetParameter(\"bar\", \"yo\");\n            template.SetParameter(\"blar\", \"yuck\");\n            var uriString = template.Resolve();\n            Assert.Equal(\"http://example.org/foo/yo/baz/yuck\", uriString);\n        }\n\n        [Fact]\n        public void ShouldResolveUriTemplateWithNonStringParameter()\n        {\n            var template = new UriTemplate(\"http://example.org/foo/{bar}/baz{?lat,lng}\");\n\n            double lat = 31.464, lng = 74.386;\n\n            template.SetParameter(\"bar\", \"yo\");\n            template.SetParameter(\"lat\", lat);\n            template.SetParameter(\"lng\", lng);\n\n            var uriString = template.Resolve();\n            Assert.Equal(\"http://example.org/foo/yo/baz?lat=31.464&lng=74.386\", uriString);\n        }\n\n\n        [Fact]\n        public void ShouldResolveMatrixParameter()\n        {\n            var template = new UriTemplate(\"http://example.org/foo{;lat,lng}\");\n\n            double lat = 31.464, lng = 74.386;\n\n            template.SetParameter(\"lat\", lat);\n            template.SetParameter(\"lng\", lng);\n\n            var uriString = template.Resolve();\n            Assert.Equal(\"http://example.org/foo;lat=31.464;lng=74.386\", uriString);\n        }\n\n\n        [Fact]\n        public void ShouldAllowUriTemplateWithQueryParamsButNoValues()\n        {\n            var template = new UriTemplate(\"http://example.org/foo{?bar,baz}\");\n            //template.SetParameter(\"bar\", \"yo\");\n            //template.SetParameter(\"blar\", \"yuck\");\n            var uriString = template.Resolve();\n            Assert.Equal(\"http://example.org/foo\", uriString);\n        }\n\n        [Fact]\n        public void ShouldAllowPartialUriTemplateWithQueryParamsButNoValues()\n        {\n            var template = new UriTemplate(\"http://example.org/foo{?bar,baz}\", resolvePartially: true);\n            //template.SetParameter(\"bar\", \"yo\");\n            //template.SetParameter(\"blar\", \"yuck\");\n            var uriString = template.Resolve();\n            Assert.Equal(\"http://example.org/foo{?bar,baz}\", uriString);\n        }\n\n        [Fact]\n        public void ShouldAllowUriTemplateToRemoveParameter()\n        {\n            var template = new UriTemplate(\"http://example.org/foo{?bar,baz}\");\n            template.SetParameter(\"bar\", \"yo\");\n            template.SetParameter(\"baz\", \"yuck\");\n            template.ClearParameter(\"bar\");\n\n            var uriString = template.Resolve();\n            Assert.Equal(\"http://example.org/foo?baz=yuck\", uriString);\n        }\n\n\n        [Fact]\n        public void ShouldAllowUriTemplateWithQueryParamsWithOneValue()\n        {\n            var template = new UriTemplate(\"http://example.org/foo{?bar,baz}\");\n            template.SetParameter(\"baz\", \"yo\");\n\n            var uriString = template.Resolve();\n            Assert.Equal(\"http://example.org/foo?baz=yo\", uriString);\n        }\n\n\n        [Fact]\n        public void LabelExpansionWithDotPrefixAndEmptyKeys()\n        {\n            var template = new UriTemplate(\"X{.empty_keys}\");\n            template.SetParameter(\"empty_keys\", new Dictionary<string, string>());\n            var uriString = template.Resolve();\n            Assert.Equal(\"X\", uriString);\n        }\n\n        [Fact]\n        public void QueryParametersFromDictionary()\n        {\n            var template = new UriTemplate(\"http://example.org/customers{?query*}\");\n            template.SetParameter(\"query\", new Dictionary<string, string>()\n            {\n                {\"active\",\"true\"},\n                {\"Country\",\"Brazil\"}\n            });\n            var uriString = template.Resolve();\n            Assert.Equal(\"http://example.org/customers?active=true&Country=Brazil\", uriString);\n        }\n\n        [Fact]\n        public void ShouldAllowListAndSingleValueInQueryParam()\n        {\n            var template = new UriTemplate(\"http://example.org{/id*}{?fields,token}\");\n            template.SetParameter(\"id\", new List<string>() { \"person\", \"albums\" });\n            template.SetParameter(\"fields\", new List<string>() { \"id\", \"name\", \"picture\" });\n            template.SetParameter(\"token\", \"12345\");\n            var uriString = template.Resolve();\n            Assert.Equal(\"http://example.org/person/albums?fields=id,name,picture&token=12345\", uriString);\n        }\n\n\n        [Fact]\n        public void ShouldHandleUriEncoding()\n        {\n            var template = new UriTemplate(\"http://example.org/sparql{?query}\");\n            template.SetParameter(\"query\", \"PREFIX dc: <http://purl.org/dc/elements/1.1/> SELECT ?book ?who WHERE { ?book dc:creator ?who }\");\n            var uriString = template.Resolve();\n            Assert.Equal(\"http://example.org/sparql?query=PREFIX%20dc%3A%20%3Chttp%3A%2F%2Fpurl.org%2Fdc%2Felements%2F1.1%2F%3E%20SELECT%20%3Fbook%20%3Fwho%20WHERE%20%7B%20%3Fbook%20dc%3Acreator%20%3Fwho%20%7D\", uriString);\n        }\n\n        [Fact]\n        public void ShouldHandleEncodingAParametersThatIsAUriWithAUriAsAParameter()\n        {\n            var template = new UriTemplate(\"http://example.org/go{?uri}\");\n            template.SetParameter(\"uri\", \"http://example.org/?uri=http%3A%2F%2Fexample.org%2F\");\n            var uriString = template.Resolve();\n            Assert.Equal(\"http://example.org/go?uri=http%3A%2F%2Fexample.org%2F%3Furi%3Dhttp%253A%252F%252Fexample.org%252F\", uriString);\n        }\n\n\n        [Fact]\n        public void ShouldThrowWhenExpressionIsNotClosed()\n        {\n            var result = string.Empty;\n            try\n            {\n                var template = new UriTemplate(\"http://example.org/foo/{bar/baz/\");\n                var uriString = template.Resolve();\n            }\n            catch (ArgumentException ex)\n            {\n\n                result = ex.Message;\n            }\n\n            Assert.Equal(\"Malformed template, missing } : http://example.org/foo/{bar/baz/\", result);\n\n        }\n\n        [Fact]\n        public void ShouldThrowWhenTemplateExpressionIsEmpty()\n        {\n            var result = string.Empty;\n            try\n            {\n                var template = new UriTemplate(\"http://example.org/foo/{}/baz/\");\n                var uriString = template.Resolve();\n            }\n            catch (ArgumentException ex)\n            {\n\n                result = ex.Message;\n            }\n\n            Assert.Equal(\"Malformed template : http://example.org/foo/{}/baz/\", result);\n\n        }\n\n        [Fact]\n        public void Query_param_with_exploded_array()\n        {\n            UriTemplate template = new UriTemplate(\"/foo/{foo}/baz{?haz*}\");\n            template.SetParameter(\"foo\", \"1234\");\n            template.SetParameter(\"haz\", new string[] { \"foo\", \"bar\" });\n\n            string uri = template.Resolve();\n\n            Assert.Equal(\"/foo/1234/baz?haz=foo&haz=bar\", uri);\n        }\n\n        [Fact]\n        public void Query_param_with_list_array()\n        {\n            UriTemplate template = new UriTemplate(\"/foo/{foo}/baz{?haz}\");\n            template.SetParameter(\"foo\", \"1234\");\n            template.SetParameter(\"haz\", new string[] { \"foo\", \"bar\" });\n\n            string uri = template.Resolve();\n\n            Assert.Equal(\"/foo/1234/baz?haz=foo,bar\", uri);\n        }\n\n        [Fact]\n        public void Query_param_with_empty_array()\n        {\n            UriTemplate template = new UriTemplate(\"/foo/{foo}/baz{?haz*}\");\n            template.SetParameter(\"foo\", \"1234\");\n            template.SetParameter(\"haz\", new string[] { });\n\n            string uri = template.Resolve();\n\n            Assert.Equal(\"/foo/1234/baz\", uri);\n        }\n\n        [Fact]\n        public void ResolveOptionalAndRequiredQueryParameters()\n        {\n            UriTemplate template = new UriTemplate(\"https://api.github.com/search/code?q={query}{&page,per_page,sort,order}\");\n            template.SetParameter(\"query\", \"1234\");\n            template.SetParameter(\"per_page\", \"19\");\n            var result = template.Resolve();\n\n            Assert.Equal(\"https://api.github.com/search/code?q=1234&per_page=19\", result);\n        }\n\n\n        [Fact]\n        public void ReservedCharacterExpansion()\n        {\n            UriTemplate template = new UriTemplate(\"https://foo.com/{?format}\");\n            template.SetParameter(\"format\", \"application/vnd.foo+xml\");\n\n            var result = template.Resolve();\n\n            Assert.Equal(\"https://foo.com/?format=application%2Fvnd.foo%2Bxml\", result);\n\n        }\n\n        [Fact(Skip = \"Unit tests should not require internet access!!\")]\n        public void PreserveReservedCharacterExpansion()\n        {\n            UriTemplate template = new UriTemplate(\"https://foo.com/?format={+format}\");\n            template.SetParameter(\"format\", \"application/vnd.foo+xml\");\n\n            var result = template.Resolve();\n\n            Assert.Equal(\"https://foo.com/?format=application/vnd.foo+xml\", result);\n\n        }\n\n        [Fact]\n        public void ShouldSupportUnicodeCharacters()\n        {\n            UriTemplate template = new UriTemplate(\"/lookup{?Stra%C3%9Fe}\");\n            template.SetParameter(\"Stra%C3%9Fe\", \"Grüner Weg\");\n\n            var result = template.Resolve();\n\n\n            Assert.Equal(\"/lookup?Stra%C3%9Fe=Gr%C3%BCner%20Weg\", result);\n\n        }\n        // \"assoc_special_chars\"  : { \"šöäŸœñê€£¥‡ÑÒÓÔÕ\" : \"Ö×ØÙÚàáâãäåæçÿ\" }\n        [Fact]\n        public void ShouldSupportUnicodeCharacters2()\n        {\n            UriTemplate template = new UriTemplate(\"{?assoc_special_chars*}\");\n            var dict = new Dictionary<string, string>\n            {\n                {\"šöäŸœñê€£¥‡ÑÒÓÔÕ\", \"Ö×ØÙÚàáâãäåæçÿ\"}\n            };\n            template.SetParameter(\"assoc_special_chars\", dict);\n\n            var result = template.Resolve();\n\n\n            Assert.Equal(\"?%C5%A1%C3%B6%C3%A4%C5%B8%C5%93%C3%B1%C3%AA%E2%82%AC%C2%A3%C2%A5%E2%80%A1%C3%91%C3%92%C3%93%C3%94%C3%95=%C3%96%C3%97%C3%98%C3%99%C3%9A%C3%A0%C3%A1%C3%A2%C3%A3%C3%A4%C3%A5%C3%A6%C3%A7%C3%BF\", result);\n\n        }\n\n        [Fact]\n        public void Remove_a_query_parameters2()\n        {\n\n            var target = new Uri(\"http://example.org/customer?format=xml&id=23\");\n\n            var template = target.MakeTemplate();\n            template.ClearParameter(\"format\");\n\n\n            Assert.Equal(\"http://example.org/customer?id=23\", template.Resolve());\n        }\n\n\n        [Fact]\n        public void EncodingTest1()\n        {\n\n            var url = new UriTemplate(\"/1/search/auto/{folder}{?query}\")\n                .AddParameter(\"folder\", \"My Documents\")\n                .AddParameter(\"query\", \"draft 2013\")\n                .Resolve();\n\n            Assert.Equal(\"/1/search/auto/My%20Documents?query=draft%202013\", url);\n        }\n\n        [Fact]\n        public void EncodingTest2()\n        {\n\n            // Parameter values get encoded but hyphen doesn't need to be encoded because it\n            // is an \"unreserved\" character according to RFC 3986\n            var url = new UriTemplate(\"{/greeting}\")\n                .AddParameter(\"greeting\", \"hello-world\")\n                .Resolve();\n\n            Assert.Equal(\"/hello-world\", url);\n\n            // A slash does need to be encoded\n            var url2 = new UriTemplate(\"{/greeting}\")\n                .AddParameter(\"greeting\", \"hello/world\")\n                .Resolve();\n\n            Assert.Equal(\"/hello%2Fworld\", url2);\n\n            // If you truly want to make multiple path segments then do this\n            var url3 = new UriTemplate(\"{/greeting*}\")\n                .AddParameter(\"greeting\", new List<string> { \"hello\", \"world\" })\n                .Resolve();\n\n            Assert.Equal(\"/hello/world\", url3);\n\n        }\n\n        //  /docs/salary.csv?columns=1,2\n        //  /docs/salary.csv?column=1&column=2\n\n        //   /emails?from[name]=Don&from[date]=1998-03-24&to[name]=Norm\n\n        // : /log?a=b&c=4\n        [Fact]\n        public void EncodingTest3()\n        {\n\n            // There are different ways that lists can be included in query params\n            // Just as a comma delimited list\n            var url = new UriTemplate(\"/docs/salary.csv{?columns}\")\n                .AddParameter(\"columns\", new List<int> { 1, 2 })\n                .Resolve();\n\n            Assert.Equal(\"/docs/salary.csv?columns=1,2\", url);\n\n            // or as a multiple parameter instances\n            var url2 = new UriTemplate(\"/docs/salary.csv{?columns*}\")\n                .AddParameter(\"columns\", new List<int> { 1, 2 })\n                .Resolve();\n\n            Assert.Equal(\"/docs/salary.csv?columns=1&columns=2\", url2);\n        }\n\n        [Fact]\n        public void EncodingTest4()\n        {\n            var url = new UriTemplate(\"/emails{?params*}\")\n                .AddParameter(\"params\", new Dictionary<string, string>\n                {\n                    {\"from[name]\",\"Don\"},\n                    {\"from[date]\",\"1998-03-24\"},\n                    {\"to[name]\",\"Norm\"}\n                })\n                .Resolve();\n\n            Assert.Equal(\"/emails?from[name]=Don&from[date]=1998-03-24&to[name]=Norm\", url);\n        }\n\n        [Fact]\n        public void EncodingTest5()\n        {\n            ///log?a=b&c=4\n            var url = new UriTemplate(\"/log?a={a}&c={c}\")\n                .AddParameter(\"a\", \"b\")\n                .AddParameter(\"c\", \"4\")\n                .Resolve();\n\n            Assert.Equal(\"/log?a=b&c=4\", url);\n        }\n\n\n        [Fact]\n        public void InvalidSpace()\n        {\n            Exception ex = null;\n            try\n            {\n                var url = new UriTemplate(\"/feeds/events{? fromId\").Resolve();\n            }\n            catch (Exception e)\n            {\n                ex = e;\n            }\n            Assert.NotNull(ex);\n        }\n\n\n    }\n}"
  },
  {
    "path": "src/UriTemplates/OperatorInfo.cs",
    "content": "namespace Tavis.UriTemplates\n{\n    public class OperatorInfo\n    {\n        public bool Default { get; set; }\n        public string First { get; set; }\n        public char Separator { get; set; }\n        public bool Named { get; set; }\n        public string IfEmpty { get; set; }\n        public bool AllowReserved { get; set; }\n\n    }\n}"
  },
  {
    "path": "src/UriTemplates/QueryStringParameterOrder.cs",
    "content": "namespace Tavis.UriTemplates\n{\n    public enum QueryStringParameterOrder\n    {\n        Strict,\n        Any\n    }\n}"
  },
  {
    "path": "src/UriTemplates/Result.cs",
    "content": "using System.Collections;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace Tavis.UriTemplates\n{\n    public class Result\n    {\n        public bool ErrorDetected { get; set; }\n        public List<string> ParameterNames { get; set; }\n        private const string _UriReservedSymbols = \":/?#[]@!$&'()*+,;=\";\n        private const string _UriUnreservedSymbols = \"-._~\";\n\n        private StringBuilder _Result = new StringBuilder();\n\n        public Result()\n        {\n            ParameterNames = new List<string>();\n        }\n        public StringBuilder Append(char value)\n        {\n            return _Result.Append(value);\n        }\n        public StringBuilder Append(string value)\n        {\n\n            return _Result.Append(value);\n        }\n\n        public override string ToString()\n        {\n            return _Result.ToString();\n        }\n        public void AppendName(string variable, OperatorInfo op, bool valueIsEmpty)\n        {\n            _Result.Append(variable);\n            if (valueIsEmpty) { _Result.Append(op.IfEmpty); } else { _Result.Append(\"=\"); }\n        }\n\n\n        public void AppendList(OperatorInfo op, bool explode, string variable, IList list)\n        {\n            foreach (object item in list)\n            {\n                if (op.Named && explode)\n                {\n                    _Result.Append(variable);\n                    _Result.Append(\"=\");\n                }\n                AppendValue(item.ToString(), 0, op.AllowReserved);\n\n                _Result.Append(explode ? op.Separator : ',');\n            }\n            if (list.Count > 0)\n            {\n                _Result.Remove(_Result.Length - 1, 1);\n            }\n        }\n\n        public void AppendDictionary(OperatorInfo op, bool explode, IDictionary<string, string> dictionary)\n        {\n            foreach (string key in dictionary.Keys)\n            {\n                _Result.Append(Encode(key, op.AllowReserved));\n                if (explode) _Result.Append('='); else _Result.Append(',');\n                AppendValue(dictionary[key], 0, op.AllowReserved);\n\n                if (explode)\n                {\n                    _Result.Append(op.Separator);\n                }\n                else\n                {\n                    _Result.Append(',');\n                }\n            }\n            if (dictionary.Count() > 0)\n            {\n                _Result.Remove(_Result.Length - 1, 1);\n            }\n        }\n\n        public void AppendValue(string value, int prefixLength, bool allowReserved)\n        {\n\n            if (prefixLength != 0)\n            {\n                if (prefixLength < value.Length)\n                {\n                    value = value.Substring(0, prefixLength);\n                }\n            }\n\n            _Result.Append(Encode(value, allowReserved));\n\n        }\n\n\n        private static string Encode(string p, bool allowReserved)\n        {\n\n            var result = new StringBuilder();\n            foreach (char c in p)\n            {\n                if ((c >= 'A' && c <= 'z')   //Alpha\n                    || (c >= '0' && c <= '9')  // Digit\n                    || _UriUnreservedSymbols.IndexOf(c) != -1  // Unreserved symbols  - These should never be percent encoded\n                    || (allowReserved && _UriReservedSymbols.IndexOf(c) != -1))  // Reserved symbols - should be included if requested (+)\n                {\n                    result.Append(c);\n                }\n                else\n                {\n                    var bytes = Encoding.UTF8.GetBytes(new[] { c });\n                    foreach (var abyte in bytes)\n                    {\n                        result.Append(HexEscape(abyte));\n                    }\n\n                }\n            }\n\n            return result.ToString();\n\n\n        }\n\n        public static string HexEscape(byte i)\n        {\n            var esc = new char[3];\n            esc[0] = '%';\n            esc[1] = HexDigits[((i & 240) >> 4)];\n            esc[2] = HexDigits[(i & 15)];\n            return new string(esc);\n        }\n        public static string HexEscape(char c)\n        {\n            var esc = new char[3];\n            esc[0] = '%';\n            esc[1] = HexDigits[(((int)c & 240) >> 4)];\n            esc[2] = HexDigits[((int)c & 15)];\n            return new string(esc);\n        }\n        private static readonly char[] HexDigits = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };\n\n\n\n    }\n}"
  },
  {
    "path": "src/UriTemplates/UriTemplate.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Text.RegularExpressions;\n\nnamespace Tavis.UriTemplates\n{\n    using System;\n    using System.Collections;\n    using System.Collections.Generic;\n#if TYPE_CONVERTER\n    using System.ComponentModel;\n#endif\n    using System.Linq;\n    using System.Text;\n\n#if TYPE_CONVERTER\n    [TypeConverter(typeof(UriTemplateConverter))]\n#endif\n    public class UriTemplate\n    {\n\n\n        private static Dictionary<char, OperatorInfo> _Operators = new Dictionary<char, OperatorInfo>() {\n                                        {'\\0', new OperatorInfo {Default = true, First = \"\", Separator = ',', Named = false, IfEmpty = \"\",AllowReserved = false}},\n                                        {'+', new OperatorInfo {Default = false, First = \"\", Separator = ',', Named = false, IfEmpty = \"\",AllowReserved = true}},\n                                        {'.', new OperatorInfo {Default = false, First = \".\", Separator = '.', Named = false, IfEmpty = \"\",AllowReserved = false}},\n                                        {'/', new OperatorInfo {Default = false, First = \"/\", Separator = '/', Named = false, IfEmpty = \"\",AllowReserved = false}},\n                                        {';', new OperatorInfo {Default = false, First = \";\", Separator = ';', Named = true, IfEmpty = \"\",AllowReserved = false}},\n                                        {'?', new OperatorInfo {Default = false, First = \"?\", Separator = '&', Named = true, IfEmpty = \"=\",AllowReserved = false}},\n                                        {'&', new OperatorInfo {Default = false, First = \"&\", Separator = '&', Named = true, IfEmpty = \"=\",AllowReserved = false}},\n                                        {'#', new OperatorInfo {Default = false, First = \"#\", Separator = ',', Named = false, IfEmpty = \"\",AllowReserved = true}}\n                                        };\n\n        private readonly string _template;\n        private readonly Dictionary<string, object> _Parameters;\n        private enum States { CopyingLiterals, ParsingExpression }\n\n\n        private readonly bool _resolvePartially;\n\n        public UriTemplate(string template, bool resolvePartially = false, bool caseInsensitiveParameterNames = false)\n        {\n            _resolvePartially = resolvePartially;\n            _template = template;\n            _Parameters = caseInsensitiveParameterNames\n                ? new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase)\n                : new Dictionary<string, object>();\n        }\n\n        public override string ToString()\n        {\n            return _template;\n        }\n\n        public void SetParameter(string name, object value)\n        {\n            _Parameters[name] = value;\n        }\n\n        public void ClearParameter(string name)\n        {\n            _Parameters.Remove(name);\n        }\n\n        public void SetParameter(string name, string value)\n        {\n            _Parameters[name] = value;\n        }\n\n        public void SetParameter(string name, IEnumerable<string> value)\n        {\n            _Parameters[name] = value;\n        }\n\n        public void SetParameter(string name, IDictionary<string, string> value)\n        {\n            _Parameters[name] = value;\n        }\n\n        public IEnumerable<string> GetParameterNames()\n        {\n            Result result = ResolveResult();\n            return result.ParameterNames;\n        }\n\n        public string Resolve()\n        {\n            var result = ResolveResult();\n            return result.ToString();\n        }\n\n        private Result ResolveResult()\n        {\n            var currentState = States.CopyingLiterals;\n            var result = new Result();\n            StringBuilder currentExpression = null;\n            foreach (var character in _template.ToCharArray())\n            {\n                switch (currentState)\n                {\n                    case States.CopyingLiterals:\n                        if (character == '{')\n                        {\n                            currentState = States.ParsingExpression;\n                            currentExpression = new StringBuilder();\n                        }\n                        else if (character == '}')\n                        {\n                            throw new ArgumentException(\"Malformed template, unexpected } : \" + result.ToString());\n                        }\n                        else\n                        {\n                            result.Append(character);\n                        }\n                        break;\n                    case States.ParsingExpression:\n                        if (character == '}')\n                        {\n                            ProcessExpression(currentExpression, result);\n\n                            currentState = States.CopyingLiterals;\n                        }\n                        else\n                        {\n                            currentExpression.Append(character);\n                        }\n\n                        break;\n                }\n            }\n            if (currentState == States.ParsingExpression)\n            {\n                result.Append(\"{\");\n                result.Append(currentExpression.ToString());\n\n                throw new ArgumentException(\"Malformed template, missing } : \" + result.ToString());\n            }\n\n            if (result.ErrorDetected)\n            {\n                throw new ArgumentException(\"Malformed template : \" + result.ToString());\n            }\n            return result;\n        }\n\n        private void ProcessExpression(StringBuilder currentExpression, Result result)\n        {\n\n            if (currentExpression.Length == 0)\n            {\n                result.ErrorDetected = true;\n                result.Append(\"{}\");\n                return;\n            }\n\n            OperatorInfo op = GetOperator(currentExpression[0]);\n\n            var firstChar = op.Default ? 0 : 1;\n            bool multivariableExpression = false;\n\n            var varSpec = new VarSpec(op);\n            for (int i = firstChar; i < currentExpression.Length; i++)\n            {\n                char currentChar = currentExpression[i];\n                switch (currentChar)\n                {\n                    case '*':\n                        varSpec.Explode = true;\n                        break;\n\n                    case ':':  // Parse Prefix Modifier\n                        var prefixText = new StringBuilder();\n                        currentChar = currentExpression[++i];\n                        while (currentChar >= '0' && currentChar <= '9' && i < currentExpression.Length)\n                        {\n                            prefixText.Append(currentChar);\n                            i++;\n                            if (i < currentExpression.Length) currentChar = currentExpression[i];\n                        }\n                        varSpec.PrefixLength = int.Parse(prefixText.ToString());\n                        i--;\n                        break;\n\n                    case ',':\n                        multivariableExpression = true;\n                        var success = ProcessVariable(varSpec, result, multivariableExpression);\n                        bool isFirst = varSpec.First;\n                        // Reset for new variable\n                        varSpec = new VarSpec(op);\n                        if (success || !isFirst || _resolvePartially) varSpec.First = false;\n                        if (!success && _resolvePartially) { result.Append(\",\"); }\n                        break;\n\n\n                    default:\n                        if (IsVarNameChar(currentChar))\n                        {\n                            varSpec.VarName.Append(currentChar);\n                        }\n                        else\n                        {\n                            result.ErrorDetected = true;\n                        }\n                        break;\n                }\n            }\n\n            ProcessVariable(varSpec, result, multivariableExpression);\n            if (multivariableExpression && _resolvePartially) result.Append(\"}\");\n        }\n\n        private bool ProcessVariable(VarSpec varSpec, Result result, bool multiVariableExpression = false)\n        {\n            var varname = varSpec.VarName.ToString();\n            result.ParameterNames.Add(varname);\n\n            if (!_Parameters.ContainsKey(varname)\n                || _Parameters[varname] == null\n                || (_Parameters[varname] is IList && ((IList)_Parameters[varname]).Count == 0)\n                || (_Parameters[varname] is IDictionary && ((IDictionary)_Parameters[varname]).Count == 0))\n            {\n                if (_resolvePartially == true)\n                {\n                    if (multiVariableExpression)\n                    {\n                        if (varSpec.First)\n                        {\n                            result.Append(\"{\");\n                        }\n\n                        result.Append(varSpec.ToString());\n                    }\n                    else\n                    {\n                        result.Append(\"{\");\n                        result.Append(varSpec.ToString());\n                        result.Append(\"}\");\n                    }\n                    return false;\n                }\n                return false;\n            }\n\n            if (varSpec.First)\n            {\n                result.Append(varSpec.OperatorInfo.First);\n            }\n            else\n            {\n                result.Append(varSpec.OperatorInfo.Separator);\n            }\n\n            object value = _Parameters[varname];\n\n            // Handle Strings\n            if (value is string)\n            {\n                var stringValue = (string)value;\n                if (varSpec.OperatorInfo.Named)\n                {\n                    result.AppendName(varname, varSpec.OperatorInfo, string.IsNullOrEmpty(stringValue));\n                }\n                result.AppendValue(stringValue, varSpec.PrefixLength, varSpec.OperatorInfo.AllowReserved);\n            }\n            else\n            {\n                // Handle Lists\n                var list = value as IList;\n                if (list == null && value is IEnumerable<string>)\n                {\n                    list = ((IEnumerable<string>)value).ToList<string>();\n                };\n                if (list != null)\n                {\n                    if (varSpec.OperatorInfo.Named && !varSpec.Explode)  // exploding will prefix with list name\n                    {\n                        result.AppendName(varname, varSpec.OperatorInfo, list.Count == 0);\n                    }\n\n                    result.AppendList(varSpec.OperatorInfo, varSpec.Explode, varname, list);\n                }\n                else\n                {\n\n                    // Handle associative arrays\n                    var dictionary = value as IDictionary<string, string>;\n                    if (dictionary != null)\n                    {\n                        if (varSpec.OperatorInfo.Named && !varSpec.Explode)  // exploding will prefix with list name\n                        {\n                            result.AppendName(varname, varSpec.OperatorInfo, dictionary.Count() == 0);\n                        }\n                        result.AppendDictionary(varSpec.OperatorInfo, varSpec.Explode, dictionary);\n                    }\n                    else\n                    {\n                        // If above all fails, convert the object to string using the default object.ToString() implementation\n                        var stringValue = value.ToString();\n                        if (varSpec.OperatorInfo.Named)\n                        {\n                            result.AppendName(varname, varSpec.OperatorInfo, string.IsNullOrEmpty(stringValue));\n                        }\n                        result.AppendValue(stringValue, varSpec.PrefixLength, varSpec.OperatorInfo.AllowReserved);\n                    }\n\n                }\n\n            }\n            return true;\n        }\n\n        private static bool IsVarNameChar(char c)\n        {\n            return ((c >= 'A' && c <= 'z') //Alpha\n                    || (c >= '0' && c <= '9') // Digit\n                    || c == '_'\n                    || c == '%'\n                    || c == '.');\n        }\n\n        private static OperatorInfo GetOperator(char operatorIndicator)\n        {\n            OperatorInfo op;\n            switch (operatorIndicator)\n            {\n\n                case '+':\n                case ';':\n                case '/':\n                case '#':\n                case '&':\n                case '?':\n                case '.':\n                    op = _Operators[operatorIndicator];\n                    break;\n\n                default:\n                    op = _Operators['\\0'];\n                    break;\n            }\n            return op;\n        }\n\n        private const string varname = \"[a-zA-Z0-9_]*\";\n        private const string op = \"(?<op>[+#./;?&]?)\";\n        private const string var = \"(?<var>(?:(?<lvar>\" + varname + \")[*]?,?)*)\";\n        private const string varspec = \"(?<varspec>{\" + op + var + \"})\";\n\n        // (?<varspec>{(?<op>[+#./;?&]?)(?<var>[a-zA-Z0-9_]*[*]?|(?:(?<lvar>[a-zA-Z0-9_]*[*]?),?)*)})\n\n        private Regex _ParameterRegex = null;\n\n        private Uri _ComponentBaseUri = new Uri(\"https://localhost.com\", UriKind.Absolute);\n\n        public IDictionary<string, object> GetParameters(Uri uri, QueryStringParameterOrder order = QueryStringParameterOrder.Strict)\n        {\n            switch (order)\n            {\n                case QueryStringParameterOrder.Strict:\n                    {\n                        if (_ParameterRegex == null)\n                        {\n                            var matchingRegex = CreateMatchingRegex(_template);\n                            lock (this)\n                            {\n                                _ParameterRegex = new Regex(matchingRegex);\n                            }\n                        }\n\n                        var match = _ParameterRegex.Match(uri.OriginalString);\n                        var parameters = new Dictionary<string, object>();\n\n                        for (int x = 1; x < match.Groups.Count; x++)\n                        {\n                            if (match.Groups[x].Success)\n                            {\n                                var paramName = _ParameterRegex.GroupNameFromNumber(x);\n                                if (!string.IsNullOrEmpty(paramName))\n                                {\n                                    parameters.Add(paramName, Uri.UnescapeDataString(match.Groups[x].Value));\n                                }\n\n                            }\n                        }\n                        return match.Success ? parameters : null;\n                    }\n                case QueryStringParameterOrder.Any:\n                    {\n                        if (!uri.IsAbsoluteUri)\n                            uri = new Uri(_ComponentBaseUri, uri);\n\n                        var uriString = uri.GetComponents(UriComponents.SchemeAndServer | UriComponents.Path | UriComponents.Fragment, UriFormat.UriEscaped);\n                        var uriWithoutQuery = new Uri(uriString, UriKind.Absolute);\n\n                        var pathParameters = GetParameters(uriWithoutQuery) ?? new Dictionary<string, object>(_Parameters.Comparer);\n\n                        Result result = ResolveResult();\n\n                        var parameterNames = result.ParameterNames;\n                        foreach (var parameter in uri.GetQueryStringParameters())\n                        {\n                            if (!parameterNames.Contains(parameter.Key))\n                                continue;\n                            pathParameters.Add(parameter.Key, parameter.Value);\n                        }\n\n                        return pathParameters.Count == 0 ? null : pathParameters;\n                    }\n                default:\n                    throw new ArgumentOutOfRangeException(nameof(order), order, null);\n            }\n        }\n\n        public static string CreateMatchingRegex(string uriTemplate)\n        {\n            var findParam = new Regex(varspec);\n\n            var template = new Regex(@\"([^{]|^)\\?\").Replace(uriTemplate, @\"$+\\?\"); ;//.Replace(\"?\",@\"\\?\");\n            var regex = findParam.Replace(template, delegate (Match m)\n            {\n                var paramNames = m.Groups[\"lvar\"].Captures.Cast<Capture>().Where(c => !string.IsNullOrEmpty(c.Value)).Select(c => c.Value).ToList();\n                var op = m.Groups[\"op\"].Value;\n                switch (op)\n                {\n                    case \"?\":\n                        return GetQueryExpression(paramNames, prefix: \"?\");\n                    case \"&\":\n                        return GetQueryExpression(paramNames, prefix: \"&\");\n                    case \"#\":\n                        return GetExpression(paramNames, prefix: \"#\");\n                    case \"/\":\n                        return GetExpression(paramNames, prefix: \"/\");\n\n                    case \"+\":\n                        return GetExpression(paramNames);\n                    default:\n                        return GetExpression(paramNames);\n                }\n\n            });\n\n            return regex + \"$\";\n        }\n\n        public static string CreateMatchingRegex2(string uriTemplate)\n        {\n            var findParam = new Regex(varspec);\n            //split by host/path/query/fragment\n\n            var template = new Regex(@\"([^{]|^)\\?\").Replace(uriTemplate, @\"$+\\?\"); ;//.Replace(\"?\",@\"\\?\");\n            var regex = findParam.Replace(template, delegate (Match m)\n            {\n                var paramNames = m.Groups[\"lvar\"].Captures.Cast<Capture>().Where(c => !string.IsNullOrEmpty(c.Value)).Select(c => c.Value).ToList();\n                var op = m.Groups[\"op\"].Value;\n                switch (op)\n                {\n                    case \"?\":\n                        return GetQueryExpression(paramNames, prefix: \"?\");\n                    case \"&\":\n                        return GetQueryExpression(paramNames, prefix: \"&\");\n                    case \"#\":\n                        return GetExpression(paramNames, prefix: \"#\");\n                    case \"/\":\n                        return GetExpression(paramNames, prefix: \"/\");\n\n                    case \"+\":\n                        return GetExpression(paramNames);\n                    default:\n                        return GetExpression(paramNames);\n                }\n\n            });\n\n            return regex + \"$\";\n        }\n\n        private static string GetQueryExpression(List<String> paramNames, string prefix)\n        {\n            StringBuilder sb = new StringBuilder();\n            foreach (var paramname in paramNames)\n            {\n\n                sb.Append(@\"\\\" + prefix + \"?\");\n                if (prefix == \"?\") prefix = \"&\";\n\n                sb.Append(\"(?:\");\n                sb.Append(paramname);\n                sb.Append(\"=\");\n\n                sb.Append(\"(?<\");\n                sb.Append(paramname);\n                sb.Append(\">\");\n                sb.Append(\"[^/?&]+\");\n                sb.Append(\")\");\n                sb.Append(\")?\");\n            }\n\n            return sb.ToString();\n        }\n\n\n        private static string GetExpression(List<String> paramNames, string prefix = null)\n        {\n            StringBuilder sb = new StringBuilder();\n\n            string paramDelim;\n\n            switch (prefix)\n            {\n                case \"#\":\n                    paramDelim = \"[^,]+\";\n                    break;\n                case \"/\":\n                    paramDelim = \"[^/?]+\";\n                    break;\n                case \"?\":\n                case \"&\":\n                    paramDelim = \"[^&#]+\";\n                    break;\n                case \";\":\n                    paramDelim = \"[^;/?#]+\";\n                    break;\n                case \".\":\n                    paramDelim = \"[^./?#]+\";\n                    break;\n\n                default:\n                    paramDelim = \"[^/?&]+\";\n                    break;\n\n            }\n\n            foreach (var paramname in paramNames)\n            {\n                if (string.IsNullOrEmpty(paramname)) continue;\n\n                if (prefix != null)\n                {\n                    sb.Append(@\"\\\" + prefix + \"?\");\n                    if (prefix == \"#\") { prefix = \",\"; }\n                }\n                sb.Append(\"(?<\");\n                sb.Append(paramname);\n                sb.Append(\">\");\n                sb.Append(paramDelim); // Param Value\n                sb.Append(\")?\");\n            }\n\n            return sb.ToString();\n        }\n\n\n    }\n\n\n}\n"
  },
  {
    "path": "src/UriTemplates/UriTemplateConverter.cs",
    "content": "﻿using System;\nusing System.ComponentModel;\nusing System.Globalization;\n\nnamespace Tavis.UriTemplates\n{\n#if TYPE_CONVERTER\n    /// <summary>\n    /// Converts to <see cref=\"UriTemplate\"/> instances from other representations.\n    /// </summary>\n    public sealed class UriTemplateConverter\n        : TypeConverter\n    {\n        /// <inheritdoc/>\n        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)\n        {\n            return sourceType == typeof(string);\n        }\n\n        /// <inheritdoc/>\n        public override object ConvertFrom(\n            ITypeDescriptorContext context,\n            CultureInfo culture,\n            object value)\n        {\n            if (value == null) { return null; }\n\n            var template = value as string;\n            if (template != null)\n            {\n                if (template.Length == 0)\n                {\n                    // For TypeConverter purposes, an empty string is \"no value.\"\n                    return null;\n                }\n\n                return new UriTemplate(template);\n            }\n\n            throw (NotSupportedException)GetConvertFromException(value);\n        }\n    }\n#endif\n}\n"
  },
  {
    "path": "src/UriTemplates/UriTemplateExtensions.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Reflection;\nusing System.Text.RegularExpressions;\n\nnamespace Tavis.UriTemplates\n{\n    public static class UriTemplateExtensions\n    {\n        public static UriTemplate AddParameter(this UriTemplate template, string name, object value)\n        {\n            template.SetParameter(name, value);\n\n            return template;\n        }\n\n        public static UriTemplate AddParameters(this UriTemplate template, object parametersObject)\n        {\n\n            if (parametersObject != null)\n            {\n                IEnumerable<PropertyInfo> properties;\n#if NETSTANDARD1_0\n                var type = parametersObject.GetType().GetTypeInfo();\n                properties = type.DeclaredProperties.Where(p=> p.CanRead);\n#else\n                properties = parametersObject.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);\n#endif\n\n                foreach (var propinfo in properties)\n                {\n                    template.SetParameter(propinfo.Name, propinfo.GetValue(parametersObject, null));\n                }\n            }\n\n            return template;\n        }\n        public static UriTemplate AddParameters(this UriTemplate uriTemplate, IDictionary<string, object> linkParameters)\n        {\n            if (linkParameters != null)\n            {\n                foreach (var parameter in linkParameters)\n                {\n                    uriTemplate.SetParameter(parameter.Key, parameter.Value);\n                }\n            }\n            return uriTemplate;\n        }\n    }\n\n    public static class UriExtensions\n    {\n        public static UriTemplate MakeTemplate(this Uri uri)\n        {\n            var parameters = uri.GetQueryStringParameters();\n            return MakeTemplate(uri, parameters);\n\n        }\n\n        public static UriTemplate MakeTemplate(this Uri uri, IDictionary<string, object> parameters)\n        {\n            var target = uri.GetComponents(UriComponents.AbsoluteUri\n                                                     & ~UriComponents.Query\n                                                     & ~UriComponents.Fragment, UriFormat.Unescaped);\n            var template = new UriTemplate(target + \"{?\" + string.Join(\",\", parameters.Keys.ToArray()) + \"}\");\n            template.AddParameters(parameters);\n\n            return template;\n        }\n\n        public static Dictionary<string, object> GetQueryStringParameters(this Uri target)\n        {\n            Uri uri = target;\n            var parameters = new Dictionary<string, object>();\n\n            var reg = new Regex(@\"([-A-Za-z0-9._~]*)=([^&]*)&?\");\t\t// Unreserved characters: http://tools.ietf.org/html/rfc3986#section-2.3\n            foreach (Match m in reg.Matches(uri.Query))\n            {\n                string key = m.Groups[1].Value.ToLowerInvariant();\n                string value = m.Groups[2].Value;\n                parameters.Add(key, value);\n            }\n            return parameters;\n        }\n\n\n    }\n}\n"
  },
  {
    "path": "src/UriTemplates/UriTemplateTable.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace Tavis.UriTemplates\n{\n    public class UriTemplateTable\n    {\n        private Dictionary<string, UriTemplate> _Templates = new Dictionary<string, UriTemplate>();\n\n        public void Add(string key, UriTemplate template)\n        {\n            _Templates.Add(key, template);\n        }\n\n        public TemplateMatch Match(Uri url, QueryStringParameterOrder order = QueryStringParameterOrder.Strict)\n        {\n            foreach (var template in _Templates)\n            {\n                var parameters = template.Value.GetParameters(url, order);\n                if (parameters != null)\n                {\n                    return new TemplateMatch() { Key = template.Key, Parameters = parameters, Template = template.Value };\n                }\n            }\n            return null;\n        }\n\n        public UriTemplate this[string key]\n        {\n            get\n            {\n                UriTemplate value;\n                if (_Templates.TryGetValue(key, out value))\n                {\n                    return value;\n                }\n                else\n                {\n                    return null;\n                }\n            }\n        }\n\n    }\n\n    public class TemplateMatch\n    {\n        public string Key { get; set; }\n        public UriTemplate Template { get; set; }\n        public IDictionary<string, object> Parameters { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/UriTemplates/UriTemplates.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <TargetFrameworks>netstandard2.0;netstandard2.1;</TargetFrameworks>\n    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>\n    <RootNamespace>Tavis</RootNamespace>\n    <AssemblyName>Tavis.UriTemplates</AssemblyName>\n    <Description>URI Template resolution library - Implementation of RFC 6570</Description>\n    <Authors>Darrel Miller</Authors>\n    <Version>2.0.0</Version>\n    <PackageReleaseNotes><![CDATA[\n            For full release notes see https://github.com/tavis-software/Tavis.UriTemplates/blob/main/CHANGELOG.md\n            ]]></PackageReleaseNotes>\n    <PackageProjectUrl>https://github.com/tavis-software/Tavis.UriTemplates</PackageProjectUrl>\n    <PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>\n    <PackageLicenseFile>LICENSE</PackageLicenseFile>\n    <PackageReadmeFile>README.md</PackageReadmeFile>\n    <RepositoryType>git</RepositoryType>\n    <SignAssembly>true</SignAssembly>\n    <RepositoryUrl>https://github.com/tavis-software/Tavis.UriTemplates</RepositoryUrl>\n    <IncludeSymbols>true</IncludeSymbols>\n    <SymbolPackageFormat>snupkg</SymbolPackageFormat>\n    <PublishRepositoryUrl>true</PublishRepositoryUrl>\n    <EmbedUntrackedSources>true</EmbedUntrackedSources>\n    <AssemblyOriginatorKeyFile>../../UriTemplateKey.snk</AssemblyOriginatorKeyFile>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"Microsoft.CodeAnalysis.NetAnalyzers\" Version=\"9.0.0\">\n      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>\n      <PrivateAssets>all</PrivateAssets>\n    </PackageReference>\n    <PackageReference Include=\"Microsoft.SourceLink.GitHub\" Version=\"8.0.0\" PrivateAssets=\"all\" />\n  </ItemGroup>\n\n  <PropertyGroup Condition=\"'$(TargetFramework)'!='netstandard1.0'\">\n    <DefineConstants>DEBUG;TRACE;TYPE_CONVERTER</DefineConstants>\n  </PropertyGroup>\n\n  <PropertyGroup Condition=\"'$(GITHUB_ACTIONS)' == 'true'\">\n    <ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>\n  </PropertyGroup>\n  <ItemGroup>\n    <None Include=\"../../LICENSE\" Pack=\"true\" PackagePath=\"\" />\n    <None Include=\"../../README.md\" Pack=\"true\" PackagePath=\"\" />\n  </ItemGroup>\n</Project>\n"
  },
  {
    "path": "src/UriTemplates/VarSpec.cs",
    "content": "using System.Text;\n\nnamespace Tavis.UriTemplates\n{\n    public class VarSpec\n    {\n        private readonly OperatorInfo _operatorInfo;\n        public StringBuilder VarName = new StringBuilder();\n        public bool Explode = false;\n        public int PrefixLength = 0;\n        public bool First = true;\n\n        public VarSpec(OperatorInfo operatorInfo)\n        {\n            _operatorInfo = operatorInfo;\n        }\n\n        public OperatorInfo OperatorInfo\n        {\n            get { return _operatorInfo; }\n        }\n\n        public override string ToString()\n        {\n            return (First ? _operatorInfo.First : \"\") +\n                   VarName.ToString()\n                   + (Explode ? \"*\" : \"\")\n                   + (PrefixLength > 0 ? \":\" + PrefixLength : \"\");\n\n        }\n    }\n}"
  }
]